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>
158 lines
7.3 KiB
Markdown
158 lines
7.3 KiB
Markdown
# 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-motion`
|
|
handling 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`, with `font-variant-numeric: tabular-nums`.
|
|
- Headings and structural labels (`h1`/`h2`, `.badge`, table `th`, the new
|
|
topbar labels): uppercase, `letter-spacing: 0.06em`, extending the tracked
|
|
uppercase style `table th` already has today — applied consistently instead
|
|
of only in tables.
|
|
|
|
### Layout
|
|
|
|
- Topbar becomes the cockpit header: `--surface` background, hairline bottom
|
|
border, brand mark left, nav + live countdown grouped right (existing
|
|
`.topbar-right` wrapper from the next-sync work is reused).
|
|
- Cards: hairline `--border` outline, `--surface` background, 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 `--accent` background with dark text (for contrast
|
|
against the light lime); secondary keeps outline/ghost style against
|
|
`--surface`.
|
|
- Focus-visible: 2px `--accent` outline 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:
|
|
|
|
1. On load, read `data-utc` as the target instant.
|
|
2. If the target is in the future, start a `setInterval` (1s) that computes
|
|
the remaining duration and renders it as `HH:MM:SS` (or `MM:SS` under an
|
|
hour) in monospace, e.g. `NEXT SYNC ▸ 00:12:04`.
|
|
3. If the target is in the past (page left open past the sync time, or
|
|
`next_tick` not yet known on first boot), render `due now` instead of a
|
|
negative countdown.
|
|
4. The `title` attribute keeps showing the absolute local time (via
|
|
`toLocaleString()`) and the original UTC instant, so hovering still gives
|
|
an absolute reference — this preserves today's behavior as a fallback/aid.
|
|
5. `data-utc` stays 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 — only `app.js` behavior and the
|
|
surrounding CSS/markup in `base.html` change.
|
|
|
|
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.
|