Documents the color/type/layout token system and the next-sync countdown evolution agreed on with the user, ahead of implementation planning. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
7.3 KiB
Visual Redesign — "Ride Computer" Dark Theme — Design
Date: 2026-08-16 Status: Draft for user review
1. Goal
Replace the current light, generic admin-template look with a distinctive dark "bike computer cockpit" theme grounded in the app's own subject matter (indoor cycling sync, Garmin telemetry). The redesign is a shared foundation: it must land before the planned dashboard summary tiles and live-update UX polish, so those can be built directly in the new visual language instead of needing a second pass.
This spec covers only the visual system and the already-shipped next-sync indicator's evolution into a live countdown. It does not add new pages, new data, or new business logic.
2. Scope
In scope
- New CSS design-token system (color, spacing reuse, borders) replacing the
current light theme in
app/web/static/style.css. - Cockpit-style topbar (dark, brand mark, nav, live sync countdown).
- Restyled shared components: cards, buttons, badges, tables, forms, empty states — applied globally via shared CSS classes so every existing template (login, account-login, dashboard, account detail/edit, users detail/new/edit, system, sync result fragment) picks it up without structural rewrites.
- Typographic treatment: uppercase tracked labels for headings/section labels (extends the existing table-header pattern), tabular monospace numerals for all stats/timestamps/counters.
- Evolving the existing static next-sync timestamp (
app/web/static/app.js,base.html) into a live, client-ticking countdown. - Accessible focus states (visible lime outline) and
prefers-reduced-motionhandling for the one animated element (the countdown) and card entrance.
Out of scope (separate follow-up specs)
- Dashboard summary/stat tiles (next phase, builds on this theme).
- Live sync results without full page reload (UX-polish phase).
- Any light-mode / theme-toggle support (explicitly rejected by user — dark only).
- New charts/statistics views.
- Any change to routes, models, or sync/business logic.
3. Design tokens
Color
| Token | Hex | Usage |
|---|---|---|
--bg |
#12151A |
Page background |
--surface |
#1A1F27 |
Cards, topbar, table zebra-free rows |
--surface-raised |
#232935 |
Hover states, inputs |
--border |
#2A3038 |
Hairline dividers/card borders |
--text |
#E7EAF0 |
Primary text |
--text-muted |
#8B93A3 |
Secondary text, labels, hints |
--accent |
#C8FF4D |
Electric lime — signature countdown, primary buttons, focus rings, "healthy" status |
Semantic status colors (each with a ~14% opacity tint of the same hue over
--surface for badge backgrounds, matching the existing --*-bg variable
pattern already in style.css):
| State | Hex | Meaning |
|---|---|---|
| Success / healthy | --accent #C8FF4D |
Reuses the signature accent — a healthy sync is the good state the accent celebrates |
| Warning / degraded | #FFB454 |
Amber |
| Danger / action required / failed | #FF5F6D |
Coral |
| Info / syncing / running | #5FD4FF |
Cyan |
| Neutral / disabled | #5A6472 |
Slate |
These map 1:1 onto the existing --success, --warning, --danger,
--info, --neutral variable names already used by .badge-* classes in
style.css — only their values change, not the class structure, so templates
need no edits for badges.
Typography
- Body/UI face: unchanged system stack (
-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif) — no new font loads, works offline in a self-hosted container. - Monospace face for all numeric data (stats, timestamps, the countdown,
table numeric columns):
ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace, withfont-variant-numeric: tabular-nums. - Headings and structural labels (
h1/h2,.badge, tableth, the new topbar labels): uppercase,letter-spacing: 0.06em, extending the tracked uppercase styletable thalready has today — applied consistently instead of only in tables.
Layout
- Topbar becomes the cockpit header:
--surfacebackground, hairline bottom border, brand mark left, nav + live countdown grouped right (existing.topbar-rightwrapper from the next-sync work is reused). - Cards: hairline
--borderoutline,--surfacebackground, no drop shadow (shadows read poorly on dark; hairlines carry the "device bezel" feel instead). Radius stays at the existing--radius: 10px, unchanged. - Buttons: primary uses
--accentbackground with dark text (for contrast against the light lime); secondary keeps outline/ghost style against--surface. - Focus-visible: 2px
--accentoutline on all interactive elements (links, buttons, inputs) — dark backgrounds need this to stay accessible since the current subtle browser default focus ring is hard to see on--surface.
Motion
- The live countdown ticks once per second (text content update only, no layout shift).
- Cards fade/slide in ~150ms on initial page load,
translateY(4px) → 0. - Both respect
prefers-reduced-motion: reduce(countdown text still updates, since it's informational, not decorative; the card entrance animation is skipped entirely).
4. Signature element: live sync countdown
app/web/static/app.js currently does a one-time UTC→local conversion of the
time[data-utc] element on DOMContentLoaded. It's extended to:
- On load, read
data-utcas the target instant. - If the target is in the future, start a
setInterval(1s) that computes the remaining duration and renders it asHH:MM:SS(orMM:SSunder an hour) in monospace, e.g.NEXT SYNC ▸ 00:12:04. - If the target is in the past (page left open past the sync time, or
next_ticknot yet known on first boot), renderdue nowinstead of a negative countdown. - The
titleattribute keeps showing the absolute local time (viatoLocaleString()) and the original UTC instant, so hovering still gives an absolute reference — this preserves today's behavior as a fallback/aid. data-utcstays the templating contract between server and client (same attribute the current tests assert on), so no server-side route or test changes are needed for this evolution — onlyapp.jsbehavior and the surrounding CSS/markup inbase.htmlchange.
No server-side change: next_sync_tick() in app/web/routes.py and the
base.html template variable wiring stay as they are; only the visual
presentation and app.js ticking logic change.
5. Testing
This is a CSS/JS-presentation change with one markup adjustment (countdown
wrapper element/label in base.html). Existing server-rendered tests assert
on data-utc="..." substrings and text content, not on CSS classes or exact
visual output, so no test breakage is expected. No new automated test is
meaningful for pure CSS token values; the existing
tests/web/test_next_sync_display.py continues to guard the server-side
contract (the attribute and value), and manual verification (screenshot) is
used to confirm the visual outcome, consistent with how the next-sync feature
was verified.
6. Rollout
Single pass across style.css, base.html, app.js. No template
restructuring needed beyond base.html's topbar, since all other templates
already consume the shared .card, .badge, button/.btn, table, and
form.stacked-form classes this spec restyles centrally.