Backfills transcripts that existed only locally in ohm-infra: - 0019.1/.2/.3 - UX-polish wave subagent transcripts (driver 0019.0 never finalized) - 0026.1-.9 - security-audit-0026 subagent transcripts (driver abandoned/closed-out by 0068; audit drove published 0030 remediation) - 0073.0 - PPE/progressive-delivery + engineering-handbook session, originally drafted as a duplicate 0046.0; reassigned next free number (0072 taken by a concurrent session) sessions.json: add 0019/0073 titles, update 0026 title. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
7.9 KiB
SESSION-0019.1 — App.css + index.css token sweep
Parent: SESSION-0019.0-TRANSCRIPT-… (UX-polish wave, rfc-app v0.21.0, roadmap item #31)
Goal
Sweep frontend/src/App.css (2434 lines) and frontend/src/index.css
onto the design-token system established in frontend/src/styles/tokens.css:
hardcoded hex → nearest semantic/primitive token, font-size literals →
--text-* scale, border-radius literals → --radius-* scale, safe
spacing where it maps cleanly, a coherent transition vocabulary + a
consistent :focus-visible ring, and base rules pointed at semantic
tokens. Hard constraint: never rename/remove a selector (parallel
siblings reference these class names); values + additive rules only.
Pre-state
- Worktree:
/Users/benstull/git/rfc-app-v0.21.0-appcss - Branch
feature/v0.21.0-appcss-sweep, based on5be2c48(token-module foundation). - index.css used literal
#1a1a1a/#fafaf8/ inline font stack. - App.css had ~98 distinct hex (66×
#fff, 51×#1a1a1a, plus a long tail of near-duplicate grays: #888/#999/#777/#666/#555 etc.), 16 distinct font-size px values, 12 distinct radius px values.
Turn-by-turn arc
- Read tokens.css (the foundation) + both target files in full (App.css needed two reads — 2434 lines). Confirmed branch/base.
- Extracted the full distinct-hex inventory via grep+uniq across both files to build the mapping deterministically rather than guessing.
- Round-1 script (
/tmp/sweep.py): regex word-boundary, case-insensitive, longest-key-first replacement of all hex that map cleanly to a ramp step or status/accent token. 615 replacements.- One dict-key collision worth noting:
#dddappears as both a gray-300 candidate and an on-dark-muted candidate. Resolved to--c-gray-300(the consolidation target); the header user-name#ddd-on-dark shifts imperceptibly to #d1d5db.
- One dict-key collision worth noting:
- Re-grepped remainder; 35 distinct hex left — all genuinely off-token (true blues, violet tints/shades, functional state dots, deep diff-contrast shades, warm oranges with no token).
- Round-2 script (
/tmp/sweep2.py): conservative — only the 7 hex that are unambiguously the same ROLE as an existing token and visually near it (light-red error bgs/borders → danger-bg/-border, warm soft bgs → warning-bg-soft). 15 replacements. Left everything else. - index.css base
:root→var(--font-sans),var(--color-text),var(--color-bg). - Round-3 script (
/tmp/sweep3.py): font-size px →--text-*(280), border-radius px →--radius-*(128),rgba(255,255,255,.15/.25)→--color-on-dark-soft/-hover(5), the three 48px header-band values →--header-height(3). Note: script rewrite invalidated an Edit I had queued for.app-header; re-read + re-applied. - Promoted
.app-headerto semantic--color-header-bg/--color-text-inverse. - Appended (not interleaved) the interaction-polish layer: a
transition vocab on hover-reactive selectors + one
:focus-visiblering using--color-focus-ring. Additive only — no resting-state change, reduced-motion handled by the token module's media query. - Build: first attempt failed on
VITE_APP_NAME is required(env gate, not CSS). Re-ran withVITE_APP_NAME=OHM→ built in 249ms, only the expected ~500kB chunk-size warning. CSS is sound. - Committed; wrote this transcript.
Hex → token MAPPING TABLE
Grays (→ neutral ramp):
| literal(s) | token | note |
|---|---|---|
| #ffffff, #fff | --c-white | |
| #fafafa, #fafaf8, #fafaf9, #fafbfc, #f9f9f9, #f9fafb | --c-gray-50 | |
| #f3f4f6, #f5f5f5, #f6f6f4, #f7f7f5, #f7f6f0 | --c-gray-100 | |
| #f0f0ee, #fcfcfb | --c-gray-150 | warm canvas |
| #e5e5e5, #e5e7eb, #e5e5e0, #e7e5e4, #eee | --c-gray-200 | |
| #d1d5db, #d4d4d4, #ddd, #ccc | --c-gray-300 | #ccc→#d1d5db slightly lighter; #ddd→#d1d5db imperceptible |
| #9ca3af, #aaa, #b0b0b0, #94a3b8 | --c-gray-400 | #94a3b8 is slightly cooler→neutral, minor |
| #6b7280, #888, #999, #777, #666, #555 | --c-gray-500 | #555→#6b7280 and #999→#6b7280 are the most noticeable gray shifts (the desired consolidation) |
| #4b5563, #444, #475569 | --c-gray-600 | #444→#4b5563 noticeable; #475569→#4b5563 slightly warmer |
| #374151, #333 | --c-gray-700 | #333→#374151 slightly cooler, noticeable |
| #1f2937, #222 | --c-gray-800 | #222→#1f2937 noticeable |
| #111 | --c-gray-900 | |
| #1a1a1a | --c-ink (header→--color-header-bg, base text→--color-text) |
Accent / status (→ family tokens):
| literal | token |
|---|---|
| #5b5bd6 | --c-accent |
| #4338ca | --c-accent-strong |
| #7c3aed | --c-violet |
| #166534 | --c-success-fg |
| #dcfce7 | --c-success-bg |
| #991b1b | --c-danger-fg |
| #b91c1c | --c-danger-fg-strong |
| #fef2f2, #fff5f5 | --c-danger-bg |
| #fecaca, #fca5a5, #fcc | --c-danger-border |
| #fee2e2, #fee | --c-danger-bg (light red error bg; slightly pinker→#fef2f2) |
| #92400e | --c-warning-fg |
| #b45309 | --c-warning-accent |
| #fef3c7 | --c-warning-bg |
| #fffbeb, #fff7ed, #fff8e0 | --c-warning-bg-soft (#fff7ed warm→#fffbeb, minor) |
Font-size (→ --text-* scale; round-to-nearest flagged): 10→2xs · 11→xs · 12→sm · 13→base · 14→md · 15→lg(16) · 16→lg · 17→xl(18) · 18→xl · 19→xl(18) · 22→2xl · 24→2xl(22) · 26→3xl(28) · 28→3xl. 9px left as literal (below the 10px scale floor — the .beta-chip micro-tag).
Border-radius (→ --radius-* scale):
2→xs · 3→sm(4) · 4→sm · 5→md(6) · 6→md · 8→lg ·
10→xl(12) · 12→xl · 14→xl(12) · 20→pill (model-pill,
already pill-shaped at its height) · 99→pill · 999→pill. 50%
(circle dots) left untouched.
On-dark + layout: rgba(255,255,255,0.15)→--color-on-dark-soft; rgba(255,255,255,0.25)→--color-on-dark-hover; 48px header band (×3)→--header-height. rgba(255,255,255,0.08) (header link hover) LEFT — no token.
Left as literals (no clean token, intentional): blues #3b82f6 #2563eb #1e40af #dbeafe #bfdbfe #eff6ff #eef2ff #c7d2fe; violet tints/shades #c4b5fd #ede9fe #ddd6fe #faf5ff #4c1d95 #3737a0 #3730a3; status dots / functional #ef4444 #dc2626 #fbbf24 #f59e0b #d97706 #10b981; deep diff-contrast #14532d #7f1d1d #78350f #bbf7d0 #f0fdf4; warm/misc #9a3412 #c2410c #4a3f00 #cb6a6a #cfc8a8 #e6dca0 #fde68a #fdba74 #fed7aa #f1f5f9. Spacing (--space-*) left untouched this pass — prioritized color/type/radius per the brief; no spacing literal was both clearly mappable and zero-layout-risk enough to be worth it.
Cut state
- Branch:
feature/v0.21.0-appcss-sweep - Final commit:
cbf02d5507f91cc5bf16a68b3f8a8b6b52990849 - Files changed:
frontend/src/App.css,frontend/src/index.css(only). - Replacements: ~630 color, 280 font-size, 128 radius, 5 on-dark, 3
header-height. Build green (
VITE_APP_NAME=OHM npm run build, 249ms, expected chunk-size warning only).
What the driver needs to know
- No selector renamed/removed — safe for parallel sibling subagents.
- Gray consolidation deliberately shifts a handful of mid-grays by a visually-noticeable amount (#555/#999→gray-500, #444→gray-600, #333→gray-700, #222→gray-800). This IS the intended polish; operator should eyeball muted-text and secondary-button text before deploy.
- 15px reading-body → 16px (lg): markdown/entry/landing body text gets 1px larger. Intentional rounding; check it reads well.
- The appended polish layer adds a global
:focus-visiblering and transitions to broad element selectors (button/a/input/…). Low risk (focus-visible never fires on pointer), but it is the one place that touches elements beyond the named classes.
§19.2 candidates
- The on-dark translucent-white pattern (
--color-on-dark-soft/-hover) and the focus-ring/transition vocabulary are deployment-agnostic and would belong in a futureflotilla-core-adjacent shared UI layer if rfc-app's frontend ever extracts one — but that's an rfc-app concern, not flotilla. No flotilla-side extraction implied by this work.