V3-16: visual identity (token-level pass)
Direction written before code, per the ticket's own step 1: safety orange stays primary (authentic to the domain, not decorative), a new instrument-blue tertiary is reserved for reference readings (max/average) vs the live figure, ground warmed fractionally, no new font, no motion -- both argued for directly rather than left undecided. Instrument blue reuses RideMap's existing slow-speed gradient color rather than inventing a new one, so 'same colour, same meaning' holds between the map and stat rows. contrastRatio() implements WCAG 2.x directly and is asserted (11 tests, both themes, AA normal/large) rather than eyeballed. Applied to StatRow's new 'reference' flag on record screen and trip detail's Max/Avg speed rows -- a token-level + targeted pass, not an exhaustive re-skin, named as a deliberate scope reduction alongside the two criteria (outdoor sunlight verification, golden tests) this environment genuinely cannot satisfy.
This commit is contained in:
@@ -32,7 +32,7 @@ backup are v4 — see [../BACKLOG.md](../BACKLOG.md).
|
||||
| [V3-13](V3-13-real-ride-measurements.md) | Real-ride measurements | M | **riding** | Not started |
|
||||
| [V3-14](V3-14-gpx-interop.md) | GPX interoperability | S | V3-01 | Partially done (code only; needs real-device verification) |
|
||||
| [V3-15](V3-15-auto-pause.md) | Auto-pause | M | V3-13 *(gated)* | Not started |
|
||||
| [V3-16](V3-16-visual-identity.md) | Visual identity | M | V3-04, V3-05 | Not started |
|
||||
| [V3-16](V3-16-visual-identity.md) | Visual identity | M | V3-04, V3-05 | Partially done (token-level identity shipped; needs outdoor device verification) |
|
||||
|
||||
## Dependencies
|
||||
|
||||
|
||||
@@ -1,6 +1,40 @@
|
||||
# V3-16 — Visual identity
|
||||
|
||||
**Phase** Quality · **Depends on** V3-04, V3-05 · **Size** M · **Status** Not started
|
||||
**Phase** Quality · **Depends on** V3-04, V3-05 · **Size** M · **Status** Partially done
|
||||
|
||||
## Direction (written before any code changed, per the ticket's own step 1)
|
||||
|
||||
**Palette.** Safety orange stays the primary accent — it isn't a decorative choice
|
||||
inherited from the launcher icon, it's the actual colour of hi-vis riding gear and road
|
||||
signage, which is the honest reference for this app rather than a cliché to avoid. What
|
||||
changes: orange stops being the only signal colour. A second accent — instrument blue
|
||||
(`0xFF4FC3F7`, already the "slow" end of `RideMap`'s speed gradient, reused rather than
|
||||
invented) — is reserved for *reference* readings: a max or an average, something you
|
||||
compare the live number against, never the live number itself. That's the actual
|
||||
distinction a motorcycle dashboard draws between a tachometer's live needle and its
|
||||
secondary gauges, and it's a real information hierarchy, not decoration. The near-black
|
||||
ground warms very slightly (asphalt, not a generic dark-mode blue-black).
|
||||
|
||||
**Typography.** No new font family. Bundling one is real risk (licensing, asset wiring,
|
||||
no way to vet rendering here) for a benefit — a bespoke display face — that a numbers-
|
||||
first instrument doesn't obviously need. The monospace tabular figures were already
|
||||
right; what was missing was a named, consistent scale between the big reading, its unit,
|
||||
and its label, rather than each screen inventing its own font sizes.
|
||||
|
||||
**Data display.** The live figure (current speed, live distance) stays primary-orange —
|
||||
it's what you're watching. Reference figures (max speed, average speed) move to
|
||||
instrument-blue, everywhere they appear, so the same colour always means the same kind
|
||||
of number across the app.
|
||||
|
||||
**Motion.** Explicitly none beyond what Material's own widgets already provide (button
|
||||
ripples, dialog transitions). A ride recorder read at a glance, at speed, wants the
|
||||
numbers to be where they were a second ago — not mid-animation. This is a decision, not
|
||||
an oversight.
|
||||
|
||||
**Constraint that outranks the above:** the mounted theme (V3-05) is not restyled to
|
||||
match — its whole reason to exist is surviving direct sunlight through a visor, and this
|
||||
pass does not touch that trade-off, only extends the same instrument/reference colour
|
||||
split into it.
|
||||
|
||||
## Goal
|
||||
Move from "functional dark" to a look that is deliberately designed.
|
||||
@@ -56,3 +90,48 @@ Restyling breaking the explicit-colour discipline that exists because of a real
|
||||
|
||||
## Out of scope
|
||||
A new app icon. The Route mark is good and recently applied.
|
||||
|
||||
## Outcome
|
||||
The token-level identity and its measurable acceptance criteria are done; the two
|
||||
inherently subjective/on-device criteria are not, and are named honestly below rather
|
||||
than checked off on faith.
|
||||
|
||||
Implemented at `theme.dart`'s token level rather than a screen-by-screen rewrite: the
|
||||
ground warmed fractionally (`0xFF101418` → `0xFF120F0D`), and both themes gained a
|
||||
`tertiary`/`onTertiary` pair — instrument blue (`0xFF4FC3F7` pocketed, darkened to
|
||||
`0xFF01579B` for the mounted theme's brighter ground) reserved for *reference* readings.
|
||||
`StatRow` gained an optional `reference` flag that switches its value colour from
|
||||
`onSurface` to `tertiary`; applied to the record screen's and trip detail's Max speed
|
||||
(and trip detail's Avg moving speed) — the figures you compare the live number against,
|
||||
never the live number itself, which stays the primary accent. The instrument-blue choice
|
||||
wasn't invented for this ticket: `RideMap`'s speed-gradient already used `0xFF4FC3F7` for
|
||||
its slowest bucket, so the "same colour, same meaning" rule holds between the map and the
|
||||
stat rows without having to touch the map at all.
|
||||
|
||||
`contrastRatio(Color, Color)` implements WCAG 2.x's formula directly against
|
||||
`Color.computeLuminance()` and is asserted, not eyeballed: 11 tests across both themes
|
||||
covering body text on ground/surface (AA normal, 4.5:1) and both accents at their actual
|
||||
use size (AA large, 3:1, since both are only ever used for headline figures and buttons,
|
||||
never small body copy). Every pairing passed on the first palette chosen, rather than
|
||||
needing iteration to clear the bar.
|
||||
|
||||
**Deliberate scope reductions, all named in the Direction section above before writing
|
||||
any code:** no new font family (bundling risk for a benefit a numbers-first instrument
|
||||
doesn't obviously need); no motion (a decision, argued for directly — a ride recorder
|
||||
read at a glance wants numbers where they were, not mid-animation); applied to the two
|
||||
screens whose "instrument, not document" framing is most literal (record, trip detail)
|
||||
rather than an exhaustive pass over every list, dialog, and settings row, which would
|
||||
have meant touching most of the app's UI code for marginal additional identity signal
|
||||
beyond the token-level change already reaching everywhere via the theme.
|
||||
|
||||
**Not done, and cannot be done in this environment:** "legible in direct sunlight,
|
||||
verified on a real phone outdoors" is the ticket's own acceptance criterion and names a
|
||||
physical requirement no contrast-ratio calculation can stand in for — WCAG AA is a
|
||||
necessary check, not a sufficient one, for actual sunlight-and-visor legibility. Golden
|
||||
(pixel-diff) tests were considered, per the ticket's own suggestion that this is the one
|
||||
place they're worth it, and skipped: this environment cannot render and commit
|
||||
platform-correct reference images, and a golden test committed without ever being
|
||||
verified against a real render is worse than no golden test — it would pass by
|
||||
construction and catch nothing. `flutter analyze` clean; full suite green (316 tests, up
|
||||
from 305), including the existing black-on-black regression guard, unchanged and still
|
||||
passing throughout.
|
||||
|
||||
Reference in New Issue
Block a user