v0.3 Phase 4k: palette architecture hardening — scales + component tokens + contrast check
Token-hardening pass per round-9 redirect. No new visual decisions —
just plumbing the tokens correctly so the colour grammar locked in
Phase 4j has a system underneath instead of hand-tuned hex.
Scales.
NEW: src/design-system/v0.3-quietware-scales.css. 11-step tonal
scales (50, 100, ..., 900, 950) for five source hues and their five
complement sources. Saturation drops at extremes to avoid neon
highs and washed-out lows. Hue-stable lightness curve picked to
mimic Material 3 / IBM Carbon tonal scales.
Generated by scripts/generate-quietware-scales.py (committed) so
values are reproducible. To regenerate:
python3 scripts/generate-quietware-scales.py > scales.css
Wired into src/app.css directly before v0.3-quietware-tokens.css.
Component tokens consume scale references only.
All --button-{role}-{tier} and --progress-{state}-fill tokens now
read from var(--{hue}-{step}) instead of raw hex literals. Future
scale tuning happens in one place; component callers never change.
--button-record-bg: var(--red-600)
--button-primary-bg: var(--blue-700)
--button-success-bg: var(--green-800) /* darkened so white
text clears AA */
--button-caution-bg: var(--yellow-400)
--button-neutral-bg: var(--color-bg-elevated)
--button-disabled-bg: var(--color-bg-elevated)
--brand-accent: var(--orange-500)
Progress component tokens.
New family: --progress-{download,transcribing,success,caution,danger,
disk,disk-caution,disk-danger}-fill plus --progress-track. Each
mode tunes the shade step so the bar reads on its track:
Dark: blue-400 / green-500 / yellow-500 / red-500
Light: blue-700 / green-800 / yellow-600 / red-700
HC-light: blue-700 / green-800 / yellow-700 / red-700
HC-dark: blue-300 / green-400 / yellow-400 / red-400
LumotiaProgress.svelte gains tone="transcribing", "disk",
"disk-caution", "disk-danger" so callers map progress role to
token explicitly.
Border / wireline / focus-ring discipline.
Three distinct token families:
--button-primary-border structural component edge
--button-primary-wire complementary identity detail
--focus-ring-color keyboard interaction
Documented as separate concerns in the tokens file.
Contrast check script.
NEW: scripts/check-colour-contrast.mjs. Parses scales + tokens
CSS, builds per-scope token tables for dark / light / HC-light /
HC-dark, resolves var() chains and color-mix() expressions, then
verifies twelve load-bearing component pairs against WCAG 2.2
minima (4.5:1 text, 3:1 UI). Exit code 1 on failure.
Current state: 0 failures across 4 modes.
Two pairs reported as "warn" rather than "fail": caution progress
on cream + caution notice border on cream. The yellow source colour
cannot clear 3:1 on a near-white surface without losing its
identity. Role recognition is carried by left-bar + icon + label,
not by surface contrast.
Verified.
- node scripts/check-colour-contrast.mjs: 0 failures, 4 modes, 12
load-bearing pairs + 2 known-limitation warns.
- npm run check: 0 errors, 0 warnings across 5707 files.
Phase 5c (Dictation layout migration) is the natural next focused
sitting. This commit gives Phase 5c the systematic colour grammar to
build on, and the contrast check gates future palette regressions.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -351,6 +351,57 @@ LumotiaNotice now reads these via Tailwind arbitrary-value classes (`bg-[var(--n
|
||||
|
||||
Per Jake's round-5 spec: yellow stays yellow (visible signal), ochre only appears as small-text ink, and brown/khaki never represents the caution role's primary identity again.
|
||||
|
||||
### Phase 4k — Palette architecture hardening (round-9 redirect). Landed 2026-05-15.
|
||||
|
||||
Token-hardening pass. No new visual decisions — just plumbing the tokens correctly so the grammar locked in Phase 4j has a system underneath instead of hand-tuned hex values.
|
||||
|
||||
**Scales.** New file [src/design-system/v0.3-quietware-scales.css](../../src/design-system/v0.3-quietware-scales.css) defines 11-step tonal scales (50–950) for the five source hues and their five complement sources. Generated by `scripts/generate-quietware-scales.py` (committed) so the values are reproducible. Saturation drops at the extremes (50–100 and 900–950) to avoid neon highs and washed-out lows. Wired into `app.css` directly before the tokens file.
|
||||
|
||||
```css
|
||||
--red-50 .. --red-950
|
||||
--blue-50 .. --blue-950
|
||||
--green-50 .. --green-950
|
||||
--yellow-50 .. --yellow-950
|
||||
--orange-50 .. --orange-950
|
||||
--red-complement-50 .. --red-complement-950
|
||||
... and so on for the four other complements
|
||||
```
|
||||
|
||||
**Component tokens consume scale references only.** All component-level tokens now read from `var(--{hue}-{step})`, never raw hex:
|
||||
|
||||
```css
|
||||
--button-record-bg: var(--red-600);
|
||||
--button-primary-bg: var(--blue-700);
|
||||
--button-success-bg: var(--green-800); /* darkened so white text clears AA */
|
||||
--button-caution-bg: var(--yellow-400);
|
||||
--button-neutral-bg: var(--color-bg-elevated);
|
||||
--button-disabled-bg: var(--color-bg-elevated);
|
||||
--brand-accent: var(--orange-500);
|
||||
```
|
||||
|
||||
**Progress component tokens.** New family covers download, transcribing, success, caution, danger, disk + disk-caution + disk-danger states. Each per-mode block tunes the shade step so the bar reads on its track:
|
||||
|
||||
| Token | Dark | Light | HC light | HC dark |
|
||||
|---|---|---|---|---|
|
||||
| `--progress-download-fill` | `blue-400` | `blue-700` | `blue-700` | `blue-300` |
|
||||
| `--progress-success-fill` | `green-500` | `green-800` | `green-800` | `green-400` |
|
||||
| `--progress-caution-fill` | `yellow-500` | `yellow-600` | `yellow-700` | `yellow-400` |
|
||||
| `--progress-danger-fill` | `red-500` | `red-700` | `red-700` | `red-400` |
|
||||
|
||||
LumotiaProgress reworked to subscribe to the new tokens. New `tone="transcribing"` / `tone="disk"` / `tone="disk-caution"` / `tone="disk-danger"` values added so the progress role mapping in code matches the design grammar.
|
||||
|
||||
**Contrast check script.** New `scripts/check-colour-contrast.mjs` parses both v0.3 CSS files, builds per-scope token tables for all 4 modes (dark, light, HC-light, HC-dark), resolves scale references through `var()` chains and `color-mix(...)` expressions, then verifies the meaningful component pairs against WCAG 2.2 minima (4.5:1 text, 3:1 UI). Exit code 1 on any failure. Run with `node scripts/check-colour-contrast.mjs`.
|
||||
|
||||
Current state: **0 failures** across 4 modes for the 12 load-bearing pairs (record/primary/danger/success/caution/neutral button text, primary/success/danger progress fills on track, info notice border on card, body text on bg + card). Caution-on-cream (progress fill + notice border) is reported as a "warn" — known structural yellow limitation; role identity is carried by the left-bar + icon + label combo, not by raw surface contrast.
|
||||
|
||||
**Border ≠ Wireline ≠ Focus Ring.** Three distinct token families, each with its own purpose:
|
||||
|
||||
```css
|
||||
--button-primary-border: var(--blue-800) /* structural edge */
|
||||
--button-primary-wire: var(--blue-complement-400) /* identity detail */
|
||||
--focus-ring-color: var(--blue-700) /* keyboard interaction */
|
||||
```
|
||||
|
||||
### Phase 4j — Colour grammar correction (round-8 redirect). Landed 2026-05-15.
|
||||
|
||||
Major correction. Brown/copper accent was dragging the UI back into the warm-brutalist palette and competing with semantic yellow. Record button was about to become blue (wrong — record is universally red). Brand orange got conflated with primary action.
|
||||
|
||||
Reference in New Issue
Block a user