Files
session-history/ohm/0019/SESSION-0019.1-TRANSCRIPT-2026-05-28T11-29--2026-05-28T11-32.md
T
Ben Stull 9e6756f678 ohm: migrate orphaned subagent transcripts (0019.x, 0026.x) + renumber dup 0046.0 -> 0073
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>
2026-06-05 03:28:59 -07:00

7.9 KiB
Raw Blame History

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 on 5be2c48 (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

  1. Read tokens.css (the foundation) + both target files in full (App.css needed two reads — 2434 lines). Confirmed branch/base.
  2. Extracted the full distinct-hex inventory via grep+uniq across both files to build the mapping deterministically rather than guessing.
  3. 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: #ddd appears 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.
  4. 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).
  5. 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.
  6. index.css base :rootvar(--font-sans), var(--color-text), var(--color-bg).
  7. 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.
  8. Promoted .app-header to semantic --color-header-bg / --color-text-inverse.
  9. Appended (not interleaved) the interaction-polish layer: a transition vocab on hover-reactive selectors + one :focus-visible ring using --color-focus-ring. Additive only — no resting-state change, reduced-motion handled by the token module's media query.
  10. Build: first attempt failed on VITE_APP_NAME is required (env gate, not CSS). Re-ran with VITE_APP_NAME=OHM → built in 249ms, only the expected ~500kB chunk-size warning. CSS is sound.
  11. 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-visible ring 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 future flotilla-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.