This spike researched 10 health and productivity apps, defined the information architecture, visual design system, data visualization strategy, interaction patterns, and technical stack for the Data Hub personal health dashboard. The dashboard will serve a single user (Taps) training for Race to the Sun (30 May 2026) with data from Strava, Google Fit, Garmin Index S2, food logging, and Ozempic tracking.
| ID | Title | Owner | Status |
|---|---|---|---|
| D-1 | Competitive analysis (10 app teardowns) | UX Researcher | Complete |
| D-2 | Common patterns and best practices | UX Researcher | Complete |
| D-3 | Dashboard layout recommendation | UI Designer | Complete |
| D-4 | Information hierarchy and progressive disclosure | UX Researcher | Complete |
| D-5 | Color, typography, dark mode specs | UI Designer | Complete |
| D-6 | Data visualization recommendations | UI Designer | Complete |
| D-7 | Interaction patterns catalog | UX Researcher | Complete |
| D-8 | Race countdown and goal tracking UI | UX Researcher | Complete |
| D-9 | Mobile-first responsive considerations | Frontend Developer | Complete |
| D-10 | Chart library and tech stack | Frontend Developer | Complete |
| D-11 | MVP metrics and acceptance criteria | Product Owner | Complete |
| D-12 | Technical feasibility review | Frontend Developer | Complete |
| D-13 | Final report (this document) | Business Analyst | Complete |
scripts/build-dashboard-data.js. No client-side data processing.This spike researched 10 health and productivity apps to extract proven UX patterns for the Data Hub dashboard. The findings shaped a comprehensive design specification covering layout, typography, color, data visualization, interaction patterns, and technology choices.
The recommended dashboard follows a three-layer progressive disclosure model. Layer 1 (Glance) shows the race countdown, daily scores, and a training load overview. Layer 2 (Domain) provides full charts with time range controls for each data source. Layer 3 (Detail) surfaces all data for a single activity or entry.
The technical stack is deliberately lightweight: uPlot for time series (~48 KB gz) and Chart.js for categorical charts (~70 KB gz), with vanilla CSS custom properties and static data builds. Total JavaScript payload is under 120 KB gzipped, targeting sub-second loads on 3G.
Six must-have MVP sections were defined with 39 testable acceptance criteria. Three open issues require resolution before implementation: the Garmin Index S2 connector (weight data), the CTL/ATL/TSB computation model (FEAT-004), and data freshness indicators.
Ten apps were analyzed across health, fitness, nutrition, and productivity categories. Each teardown identifies key patterns to adopt and anti-patterns to avoid.
Customizable Summary tab with favorite cards is the gold standard for letting users choose what matters. Progressive disclosure with three clean layers: Summary > Browse > Detail. Data card pattern combines value, sparkline, and drill-down in a single component. Trend notifications alert users to meaningful changes.
Adopt: data card pattern, three-layer disclosure, customizable summary
Avoid: information overload from aggregating too many sources without hierarchy
Training Status system translates complex physiological data into a single actionable word (Productive, Maintaining, Peaking, and others). Load Focus uses a three-segment bar to show training distribution across aerobic, anaerobic, and low intensity. Performance Dashboard offers 120+ premade charts. Deep cycling-specific features including power curves and FTP tracking.
Adopt: training status summarization, load focus visualization, cycling depth
Avoid: cluttered navigation, removing customization in redesigns
Fitness and Freshness chart (CTL/ATL/TSB) is directly relevant to Data Hub. Best-in-class activity detail pages with map, stats grid, and HR/power charts. PR badges provide motivation without being overbearing.
Adopt: CTL/ATL/TSB chart, activity detail layout, PR badge system
Avoid: social feed overwhelming personal data, gating key features behind paywall
Three-score framework (Recovery, Strain, Sleep) is the best example of distilling biometrics into an actionable system. Traffic light color coding is reserved exclusively for recovery scores, maintaining strong signal clarity. Ring and dial visualizations communicate at a glance. Progressive disclosure is executed cleanly.
Adopt: score-based summarization, disciplined color coding, ring visualization
Avoid: rigid framework with no customization options
Three-tab temporal hierarchy (Today, Vitals, My Health) is the cleanest information architecture in the space. "One Big Thing" approach surfaces a single priority insight each day. Cumulative Stress tracking uses a 31-day rolling window for context.
Adopt: temporal tab hierarchy, single priority insight, rolling window context
Avoid: major navigation changes that disorient existing users, generic AI advice
Customizable Today tab is based on user-defined goals. Consistent chart design with clear headers across all data types. Compassionate design language frames data positively. Time range consistency (Day, Month, Year) across all views.
Adopt: goal-based customization, consistent chart headers, time range consistency
Avoid: brand confusion during transitions, messy data migration paths
Micronutrient target bars (horizontal progress with three-state color coding) are the best nutritional visualization in any app. Dashboard widgets include Report Summary and Streaks. Strong scientific accuracy positioning appeals to data-focused users.
Adopt: micronutrient target bars, three-state color coding, scientific framing
Avoid: overwhelming data density, weak mobile experience
Streamlined food logging with barcode scanner and fast search reduces friction. Macro ring visualization is immediately readable. "Remaining" framing (calories left) is more actionable than "consumed" framing.
Adopt: streamlined logging, macro ring, "remaining" framing
Avoid: ad clutter degrading experience, false calorie precision
100ms rule for interaction response creates a feeling of instantaneousness. Command palette (Cmd+K) with passive shortcut learning teaches power features without blocking novice use. Split Inbox pattern for domain separation. Keyboard-first navigation with gamification of productivity metrics.
Adopt: 100ms interaction target, command palette, keyboard navigation
Avoid: steep learning curve for mouse-oriented users
Composable dashboard with charts, metric blocks, and tables. Global dashboard-level filters apply consistently. Project graph with forecasting capabilities. Keyboard shortcuts with command palette. Monochrome base with selective color accents for emphasis.
Adopt: composable widgets, global filters, monochrome with selective color
Avoid: cold or unapproachable aesthetic, enterprise-only complexity
Ten reusable UX patterns were identified across all 10 apps, prioritized by importance to the Data Hub MVP.
Single-page dashboard using a 12-column CSS Grid (1200px max width), dark mode default. The layout is organized into two zones: above the fold (hero zone, ~700px viewport) and below the fold (domain sections).
"Data Hub" wordmark on the left. Date navigation strip in the center. Race countdown chip on the right showing days remaining until Race to the Sun.
Five stat cards in a 1fr grid: Weight, Body Fat, Steps, Resting HR, and Form (TSB). Each card shows the current value with a delta indicator and optional sparkline.
Full-width CTL/ATL/TSB area chart with a race day marker line. This is the primary chart the user sees on load.
Reverse-chronological Strava activities as cards. Each card shows activity type, distance, duration, and key metrics.
Body composition: weight trend chart with body comp breakdown bar. Sleep: last night's summary card, sleep stages horizontal bar, and 7-day trend.
Heart rate: today's HR timeline with HR zone distribution bars. Nutrition: today's food log entries with Ozempic tracker card.
Seven-column mini heatmap calendar showing activity, compliance, and key metrics for the week at a glance.
Grid specification: repeat(12, 1fr) on desktop, single column on mobile. 16px gap between cards, 24px gap between sections.
The dashboard uses a three-layer progressive disclosure architecture. Each layer increases data density and time-on-task.
The hero insight, 3 to 4 score cards, and race countdown. Low data density. Answers "How am I doing right now?" at a glance.
Full charts with time range controls and chart headers for each data source. Medium data density. Answers "What are the trends?" with interactive exploration.
All data for a single entry, activity, or day. High data density. Answers "What exactly happened?" Accessed via card drill-down (slide-over on desktop, full page on mobile).
Race countdown > Weight > Training > Nutrition > Medication. This order determines default sort, visual prominence, and loading priority.
Extends the existing Data Hub CSS variables with additional surface and accent tones.
--green to --redSystem font stack with Major Third ratio (1.25). Seven sizes from 11px (--text-xs) to 32px (--text-3xl). Five weights from 400 (regular) to 800 (extra bold). All metric values use font-variant-numeric: tabular-nums for alignment.
| Token | Size | Usage |
|---|---|---|
--text-3xl | 32px | Page titles, hero numbers |
--text-2xl | 26px | Section headers |
--text-xl | 20px | Card titles, stat values |
--text-lg | 16px | Body text |
--text-md | 14px | Secondary text, labels |
--text-sm | 12px | Captions, metadata |
--text-xs | 11px | Axis labels, fine print |
Dark mode is the only mode. No light mode toggle. Four-level surface elevation creates depth without box shadows:
#0f0f0f (page background)#1a1a1a (card surfaces)#242424 (elevated elements, inputs)#2e2e2e (hover states, active elements)Depth is communicated through 1px borders, not shadows. All text/background combinations meet WCAG AA+ contrast ratios.
Complete mapping of every metric to its optimal chart type, organized by data source.
| Source / Metric | Chart Type | Library |
|---|---|---|
| Strava: Distance/Elevation | Bars with goal line | Chart.js |
| Strava: Watts/HR | Line + dots | uPlot |
| Strava: Suffer Score | Lollipop | Chart.js |
| Strava: CTL (Fitness) | Area | uPlot |
| Strava: ATL (Fatigue) | Line | uPlot |
| Strava: TSB (Form) | Diverging area | uPlot |
| Google Fit: Steps | Bars + goal line, radial gauge (today) | Chart.js |
| Google Fit: Daily HR | Zone-colored area | uPlot |
| Google Fit: Resting HR | Line (trend) | uPlot |
| Google Fit: HR Zones | Horizontal stacked bar | Chart.js |
| Google Fit: Sleep Stages | Horizontal stacked bar | Chart.js |
| Google Fit: Hypnogram | Stepped area | uPlot |
| Google Fit: Sleep Duration | Stacked bars (trend) | Chart.js |
| Garmin: Weight | Line + goal line + Ozempic markers | uPlot |
| Garmin: Body Fat | Secondary line | uPlot |
| Food: Entries | Card list | HTML |
| Food: Calories | Bars | Chart.js |
| Food: Macros | Donut | Chart.js |
| Ozempic: Doses | Timeline dot plot | uPlot |
| Ozempic: + Weight | Dual-axis overlay | uPlot |
| Computed: CTL/ATL/TSB | Multi-layer area + line | uPlot |
| Computed: Form Label | Badge | HTML |
| Computed: Projection | Annotated projection line | uPlot |
Six-preset segmented control: 1W, 1M, 3M, 6M, 1Y, All. Instant chart update on selection. Consistent position (top-right of every chart). Active state uses --accent background.
Card-to-page: route-based, each domain view has its own URL. List-to-detail: slide-over panel on desktop (400px wide), full page on mobile. Back navigation via breadcrumb: Today > Domain > Detail.
Three-line format: date on line one, primary value on line two, contextual comparison on line three. Hover-triggered on desktop, tap-triggered on mobile. 150ms appearance delay to prevent flicker.
Vertical line snapping to the nearest data point on time series charts. Shows tooltip anchored to the crosshair. Synced across charts in the same domain view.
Persistent sidebar on desktop with domain icons and labels. Hamburger menu on mobile collapsing to a bottom sheet. Current domain highlighted with --accent indicator.
Large day count (using --text-3xl) with race name and a dynamic status line showing the current training phase. A horizontal training phase bar visualizes progression through Base > Build > Peak > Taper > Race phases. The current phase is highlighted with --accent. Compact version appears in the top bar on all other views.
Progress cards for each key target:
Each card shows: current value, target value, progress bar, and a status label (on track, behind, at risk, or achieved). Status labels use color discipline: green for on track/achieved, yellow for behind, red for at risk.
env(safe-area-inset-*) for notched phonesprefers-reduced-motion media query disables transitions| Asset | Size (gzipped) |
|---|---|
| HTML + CSS | ~15 KB |
| uPlot | ~50 KB |
| Chart.js | ~70 KB |
| Dashboard data (JSON) | ~20 KB |
| Total | ~155 KB |
Target: under 1 second load on 3G connections.
| Library | Role | Size (gz) | Rationale |
|---|---|---|---|
| uPlot | Primary: all time series | ~48 KB | Fastest Canvas renderer. Handles CTL/ATL/TSB, HR, weight, sleep hypnogram. Gradient fills for diverging and zone-colored areas (~15 lines each). |
| Chart.js | Secondary: categorical | ~70 KB | Mature ecosystem. Handles bars, doughnuts, lollipops, stacked bars. Required for chart types uPlot does not support natively. |
Vanilla CSS with custom properties. No framework (Tailwind, Bootstrap, etc.). Extends the existing Data Hub design system variables. Grid layout via native CSS Grid.
Static build step: scripts/build-dashboard-data.js pre-computes a single dashboard-data.json file at deploy time. No client-side API calls or data processing. Charts initialize from pre-shaped arrays.
Script tags with CDN or local copies. No bundler (Webpack, Vite, etc.) needed. Canvas does not read CSS variables directly; resolve variable values at chart initialization time via getComputedStyle().