Dark is default · Light is AA mirror
Precision instrument, not a landing page.
Near-black canvas #0a0c0e+ electric cyan #00d0ff+ generous negative space. Numbers are the hero — tabular mono so digits never jitter. Hover glows are restrained, focus rings are always visible.
1 — Background layers
Near-black base → panel → elevated → hover → inset. Light theme swaps to off-white/white but keeps the same stack.
background
#0a0c0e
light: #f8f9fa
surface
#14181c
light: #ffffff
surface-elevated
#1c2329
light: #f1f5f9
surface-hover
#242e38
light: #e2e8f0
surface-inset
#070a0c
light: #eef2f6
border
#1e252c / light #e2e8f0
border-strong
#2a3440 / light #cbd5e1
border-subtle
#161c22 / light #f1f5f9
2 — Text
foreground
#eef2f6
Aa 123
15.8:1 on bg
muted
#8a9aa8
Aa 123
7.1:1
muted-strong
#a3b2bf
Aa 123
9.2:1
faint
#6b7d8c
Aa 123
4.6:1 AA
disabled
#3d4b57
Aa 123
placeholder #4a5d6b
3 — Accent: electric cyan (motion)
One hue drives: speed number, primary button, active toggle, focus ring, links. Three steps + tints. Glow is restrained — instrument, not flashy.
accent
#00d0ff
base — button fill, speed digits (dark), links
accent-hover
#33daff
hover — 160ms ease-out-expo + glow
accent-active
#00a6cc
active/pressed — scale 0.98
accent-muted
#00d0ff14 (8%) — wash
accent-subtle
#00d0ff0a (4%) — ghost hover
accent-ring
#00d0ff66 (40%) — focus glow
accent-foreground
#00141a (dark) / #ffffff (light)
text on accent
Light mirror: accent #0096bf (4.6:1 on white AA), hover #00a6cc, active #007a9c, ring #0096bf4d.
Warning — amber
Weak GPS, low accuracy, stale fix — only warnings.
Light: #b45309 (5.2:1), hover #d97706, active #92400e
Success — GPS locked
≤15m accuracy, live dot, good signal.
Light: #059669 (4.8:1), hover #10b981
Error — permission denied
Blocked, critical error.
Light: #dc2626 (4.6:1)
4 — Typography
Geist / Inter (sans, grotesk) for UI + Geist Mono / JetBrains Mono for every numeric readout — tabular-nums so digits never jitter.
Hero — speed display (largest element)
Type scale — sans
3xl 30px/36px 700 -0.035em — Page heading
2xl 24px/32px 600 — Section
xl 20px/28px 600 — Card title
lg 18px/26px 500 — Card title sm
md 16px/24px — Lead
base 14px/20px — Default body
sm 13px/18px — Toolbar, toggle labels
xs 12px/16px — Helper, accuracy ±4m
caption 11px/14px 500 0.06em — Eyebrow, kbd hint
mono-sm 12px/16px 500 — metadata
Tabular-nums — jitter test
All numbers below use tabular-nums + tnum and fixed ch width — they should not shift as digits change.
Spacing scale (4px base)
Container max-w-5xl (1024px), gutters 16px mobile / 24px desktop. Section bands 96–128px vertical.
Radius — soft-rounded + pill
Buttons/toggles are always rounded-pill. Speed card is rounded-lg–xl. Hairline is 1px solid border.
5 — Motion
Short, instrument-like. Respects prefers-reduced-motion.
instant 100ms
ease-out-expo
Number tick, dot pulse
fast 160ms
ease-out-expo
Button, toggle, theme flip
normal 240ms
cubic-bezier(0.32,0.72,0,1)
Accordion, banner, toast
slow 400ms
same ease
Page transition
glitch 80ms
linear
Speed jump crossfade
6 — Interactive-color behavior
Every clickable has 5 states driven by accent tokens — not just opacity. Hover gets a subtle accent glow/ring; focus-visible gets 2px ring + outer glow.
Primary button
default bg-accent text-accent-foreground · hover bg-accent-hover + glow-accent · focus ring-2 ring-ring · active bg-accent-active scale-[0.98]
Ghost / secondary
Segmented control · unit switcher
Track bg-surface-inset border rounded-pill p-1 · active bg-accent text-accent-foreground + glow · inactive text-muted hover:text-foreground
Icon button · link · input
7 — Component states vocabulary
Every control supports these 7 states (via data-state + aria). Banners use role="status" aria-live="polite".
--:-- km/h
Speed --, stats --, button Start tracking — prompt “Press Start to begin”.
Button disabled + spinner — “Requesting location permission…”
Location blocked — enable in browser settings
Banner bg-error-muted border-error-border + help link.
“Searching for satellites…” — speed -- shimmer, accuracy —, button Cancel (ghost).
64.2 km/h
Dot solid + success glow, accuracy ±4m, button Pause + Stop.
48.1 km/h
Amber pulse — “Weak GPS — accuracy ±32m — move outdoors” — speed still live, smoothing ↑.
54.0 km/h frozen + dimmed
Dot gray static, banner “Paused” surface-elevated, button Resume + Reset.
Accuracy thresholds: ≤15m = success · 16–50m = warning · >50m = error-tinged (still tracking-weak). Stale fix >5s → warning “GPS signal lost — last fix Xs ago”.
Preview — speed card (instrument)
Avg
48.2
km/h
Max
88.4
km/h
Distance
3.42
km