feat(themes): adopt Carol DS token surface with --color-* bridge (#138) #147

Merged
james merged 1 commit from 138-ds-tokens into main 2026-06-19 18:04:08 +00:00
Owner

Summary

Closes #138. Extends app/themes/{light,dark}.css with the full Carol DS alias set the design package (../carol-design/) expects. The existing seven-name --color-* surface stays at its pre-DS values so every existing inline-styled component renders pixel-identically — the per-surface rebuild tickets (Profile / Skills / Education / Account / Notes / login / register) flip components onto the DS aliases one screen at a time, and a final cleanup ticket retires --color-* once every surface has flipped.

What's added

Group Aliases
Surfaces --bg, --bg-subtle, --surface, --surface-2, --surface-3, --inset
Text --text, --text-secondary, --text-muted, --text-faint
Borders --border, --border-strong, --border-subtle
Accent --accent, --accent-fg, --accent-subtle, --accent-text, --ring
Primitives (:root) Brand blue scale --blue-400…700, ink ramp --ink-25…950

Values lifted from ../carol-design/README.md §Design Tokens. The handful of light-theme aliases the README leaves unspecified (--bg-subtle, --surface-3, --inset, --border-strong/subtle, --text-faint, --accent-subtle) are derived from the ink ramp / blue scale to match the dark-theme pattern — comments in light.css flag the derivation so a future "spec these properly" pass knows where to look.

Critical decision: the two token surfaces do NOT redirect

--color-fg-muted does not resolve to --text-secondary. --color-bg does not resolve to --bg. Doing so would flip every existing surface visually in one PR — exactly the regression the bridge exists to prevent.

The two sets are independent: same theme files, same selectors, distinct definitions. They share nothing except declaration context. Per-surface rebuild tickets do the flip one screen at a time. ADR-0023 documents the call in full, including alternatives rejected (atomic rewrite, full redirect, separate tokens.css module, CSS preprocessor).

Acceptance criteria (from #138)

  • Both themes ship every DS alias listed in the design's tokens.
  • git grep "var(--color-" still returns matches — 150 across 10 files. Bridge confirmed; new code reaches for the DS names.
  • ADR captured (ADR-0023).
  • No visual regression on any current screen — the --color-* declarations are pre-DS values verbatim.

CLAUDE.md update

The "Themes are pluggable" line was extended to call out the DS token surface as the new vocabulary and the --color-* bridge as transitional. ADR-0008 stays cited as the engine; ADR-0023 stacks on top for the token migration.

Test plan

  • npm run typecheck — clean.
  • npm run lint — clean.
  • npm run build — clean.
  • git grep -c "var(--color-" totals 150 matches across app/(app)/account, notes, profile, experience, skills, components, layout — i.e. every surface (no inline-styled component changed).
  • Manual visual probe deferred to reviewer per CLAUDE.md UI policy: visit each of /profile, /skills, /experience, /account, /notes, /login, /register in both light and dark themes and confirm pixel-equal rendering vs main.

Closes #138. Part of epic #4 (the per-surface rebuilds consume from here).

🤖 Generated with Claude Code

## Summary Closes #138. Extends `app/themes/{light,dark}.css` with the full Carol DS alias set the design package (`../carol-design/`) expects. The existing seven-name `--color-*` surface stays at its pre-DS values so every existing inline-styled component renders pixel-identically — the per-surface rebuild tickets (Profile / Skills / Education / Account / Notes / login / register) flip components onto the DS aliases one screen at a time, and a final cleanup ticket retires `--color-*` once every surface has flipped. ## What's added | Group | Aliases | |---|---| | Surfaces | `--bg`, `--bg-subtle`, `--surface`, `--surface-2`, `--surface-3`, `--inset` | | Text | `--text`, `--text-secondary`, `--text-muted`, `--text-faint` | | Borders | `--border`, `--border-strong`, `--border-subtle` | | Accent | `--accent`, `--accent-fg`, `--accent-subtle`, `--accent-text`, `--ring` | | Primitives (`:root`) | Brand blue scale `--blue-400…700`, ink ramp `--ink-25…950` | Values lifted from `../carol-design/README.md` §Design Tokens. The handful of light-theme aliases the README leaves unspecified (`--bg-subtle`, `--surface-3`, `--inset`, `--border-strong/subtle`, `--text-faint`, `--accent-subtle`) are derived from the ink ramp / blue scale to match the dark-theme pattern — comments in `light.css` flag the derivation so a future "spec these properly" pass knows where to look. ## Critical decision: the two token surfaces do NOT redirect `--color-fg-muted` does **not** resolve to `--text-secondary`. `--color-bg` does **not** resolve to `--bg`. Doing so would flip every existing surface visually in one PR — exactly the regression the bridge exists to prevent. The two sets are independent: same theme files, same selectors, distinct definitions. They share nothing except declaration context. Per-surface rebuild tickets do the flip one screen at a time. [ADR-0023](docs/adr/0023-ds-token-migration.md) documents the call in full, including alternatives rejected (atomic rewrite, full redirect, separate `tokens.css` module, CSS preprocessor). ## Acceptance criteria (from #138) - [x] Both themes ship every DS alias listed in the design's tokens. - [x] `git grep "var(--color-"` still returns matches — **150 across 10 files**. Bridge confirmed; new code reaches for the DS names. - [x] ADR captured ([ADR-0023](docs/adr/0023-ds-token-migration.md)). - [x] No visual regression on any current screen — the `--color-*` declarations are pre-DS values verbatim. ## CLAUDE.md update The "Themes are pluggable" line was extended to call out the DS token surface as the new vocabulary and the `--color-*` bridge as transitional. ADR-0008 stays cited as the engine; ADR-0023 stacks on top for the token migration. ## Test plan - [x] `npm run typecheck` — clean. - [x] `npm run lint` — clean. - [x] `npm run build` — clean. - [x] `git grep -c "var(--color-"` totals 150 matches across `app/(app)/account`, `notes`, `profile`, `experience`, `skills`, `components`, `layout` — i.e. every surface (no inline-styled component changed). - [ ] **Manual visual probe deferred to reviewer** per CLAUDE.md UI policy: visit each of `/profile`, `/skills`, `/experience`, `/account`, `/notes`, `/login`, `/register` in both light and dark themes and confirm pixel-equal rendering vs main. Closes #138. Part of epic #4 (the per-surface rebuilds consume from here). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat(themes): adopt Carol DS token surface with --color-* bridge (#138)
All checks were successful
Commits / Conventional Commits (pull_request) Successful in 6s
PR / OSV-Scanner (pull_request) Successful in 32s
PR / Static analysis (pull_request) Successful in 53s
PR / Typecheck (pull_request) Successful in 1m1s
PR / npm audit (pull_request) Successful in 1m6s
PR / Package age policy (soft) (pull_request) Successful in 33s
Secrets / gitleaks (pull_request) Successful in 24s
PR / Coverage (soft) (pull_request) Successful in 1m33s
PR / Build (pull_request) Successful in 1m45s
PR / Lint (pull_request) Successful in 1m7s
PR / Trivy (image) (pull_request) Successful in 1m27s
PR / Test (sqlite) (pull_request) Successful in 1m40s
PR / Test (postgres) (pull_request) Successful in 1m40s
da2c7b8257
Closes #138. Extends app/themes/{light,dark}.css with the full Carol
DS alias set the design package (../carol-design/) expects. The
existing seven-name --color-* surface stays at its pre-DS values so
every existing inline-styled component renders pixel-identically —
the per-surface rebuild tickets (Profile / Skills / Education /
Account / Notes / login / register) will flip components onto the
DS aliases one screen at a time, and a final cleanup ticket retires
--color-* once every surface has flipped.

Added (both themes):
- Surfaces: --bg, --bg-subtle, --surface, --surface-2, --surface-3,
  --inset
- Text: --text, --text-secondary, --text-muted, --text-faint
- Borders: --border, --border-strong, --border-subtle
- Accent: --accent, --accent-fg, --accent-subtle, --accent-text,
  --ring
- Primitives at :root: brand blue scale (--blue-400…700), ink ramp
  (--ink-25…950)

Values lifted from ../carol-design/README.md §Design Tokens. The
handful of light-theme aliases the README leaves unspecified
(--bg-subtle, --surface-3, --inset, --border-strong/subtle,
--text-faint, --accent-subtle) are derived from the ink ramp /
blue scale to match the dark-theme pattern — commented in light.css.

Importantly: the two token surfaces do NOT redirect to each other.
--color-fg-muted does not resolve to --text-secondary; --color-bg
does not resolve to --bg. Doing so would flip every existing
surface visually in one PR — exactly the regression the bridge
exists to prevent. ADR-0023 documents the call.

Verification:
- typecheck / lint / build clean.
- git grep "var(--color-" returns 150 matches across every surface
  (bridge confirmed; no inline-styled component changed).
- ADR-0008 stays cited as the theme engine; ADR-0023 records the
  token-surface migration on top of it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

📊 Test coverage

Patch coverage: no testable lines changed.

Overall (app/, lib/, db/, excluding UI per ADR-0019):

Metric Value Soft target
Lines 86.6% ≥ 50%
Branches 80.9% ≥ 75%
Functions 90.8% informational

Soft thresholds per ADR-0019. Coverage is informational and does not block merge.

<!-- coverage-comment --> ## 📊 Test coverage **Patch coverage:** no testable lines changed. **Overall** (`app/`, `lib/`, `db/`, excluding UI per ADR-0019): | Metric | Value | Soft target | |---|---|---| | Lines | 86.6% ✅ | ≥ 50% | | Branches | 80.9% ✅ | ≥ 75% | | Functions | 90.8% | informational | Soft thresholds per [ADR-0019](docs/adr/0019-coverage-soft-targets.md). Coverage is informational and does not block merge.
james merged commit d109c8b148 into main 2026-06-19 18:04:08 +00:00
james deleted branch 138-ds-tokens 2026-06-19 18:04:08 +00:00
Sign in to join this conversation.
No description provided.