feat: four switchable skins, and the token that makes them possible - #252
Conversation
Midnight (the current palette, unchanged) plus three alternatives, switchable in Settings → General and remembered per machine. They fill a grid: dark/cool is Midnight, light/warm Atelier, dark/warm Foundry, light/cool Lagoon. This is mostly a CSS change because of a property this codebase already has: 916 token classes against 8 hardcoded hex values and no Tailwind greys. A skin is therefore one block of custom properties — Lagoon cost 51 lines. The selector is `[data-skin=…]` rather than `:root[data-skin=…]`, so any element can open a skin context for its subtree, which is how the picker renders a live miniature of each skin instead of a painted mockup. One token is doing real work beyond theming: --color-accent-ink, the ink carried by a filled accent or danger surface. Components hardcode `text-white` on those fills at ~20 sites, which holds only while every accent is dark enough to carry white. That is the constraint behind a measurement worth having on its own: white on the current accent is 3.65:1 and on danger 3.10:1, both short of the 4.5:1 that AA asks of the 12-14px labels they carry. Midnight sets the token to #ffffff so its rendering is byte-identical, while Foundry can invert the relationship — brass fill, near-black lettering, 7.4:1 — which is what lets it read as lit metal rather than as a dark paint chip. Same story for the disabled state: `disabled:opacity-40` works on a dark ground and inverts on a light one, where Atelier's accent at 40% over white lands on #d9bfa8 and white lettering measures 1.75:1. Disabled buttons get their own colours instead of transparency. Contrast is measured, not asserted: check-skin-contrast.mjs parses the stylesheet, reads @theme and :root as the inherited base — so a token a skin does NOT set is still measured as it ships — and fails on an undefined token rather than skipping it. 27 pairs per skin. Midnight is advisory, since its job is to be the original, gaps included.
|
Warning Review limit reached
Next review available in: 59 minutes Limit details: You’ve used all 3 included reviews currently available. Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits within each organization. For paid Pro and Pro+ reviews, CodeRabbit uses a developer's included PR review attempts over the past 7 days to set the current hourly allowance. At typical activity levels, the full plan allowance applies. Higher sustained activity can lower the allowance until earlier attempts leave the 7-day window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (7)
Comment |
milind-soni
left a comment
There was a problem hiding this comment.
Reviewed against current main, including the newly merged Ubuntu/Wayland work. The CSS-token architecture, pre-paint application, local persistence, live previews, and accessible selection semantics are sound. Verified all four skins visually, reload persistence, production build, contrast checks, typecheck, full test suite, and packaged-server smoke test. No blocking issues.
main의 milind-soni#277(팀 임포트 additive-only), milind-soni#252(스킨), milind-soni#276(Ubuntu 릴리스) 병합 충돌 9개 파일을 해결했다. - localComputerMcp capability는 채택, 정적 effortLevels 재주입은 제거 - fake-acp-cli dump를 main의 dumpState 구조로 통일하고 droid exec --help와 RPC calls 기록을 유지했다 - grok argv는 main 순서(서브커맨드 뒤 -m)로, duplicate PATCH 테스트는 PR의 검증 계약(미확인 인스턴스 409)에 맞췄다 Tested: pnpm typecheck, pnpm vitest run (125 files, 1205 passed, 12 skipped) Confidence: high Scope-risk: moderate Reversability: moderate
Midnight (the current palette, unchanged) plus three alternatives, switchable in Settings → General and remembered per machine. They fill a grid:
Why this is mostly a CSS change
Because of a property this codebase already has: 916 token classes against 8 hardcoded hex values and no Tailwind greys. A skin is therefore one block of custom properties — Lagoon cost 51 lines. That token discipline is what made this worth trying at all.
The selector is
[data-skin=…]rather than:root[data-skin=…], so any element can open a skin context for its subtree. That is how the picker renders a live miniature of each skin instead of a painted mockup.One token does real work beyond theming
--color-accent-ink— the ink carried by a filled accent or danger surface. Components hardcodetext-whiteon those fills at ~20 sites, which holds only while every accent is dark enough to carry white.That constraint is measurable, and the numbers are worth having on their own: white on the current accent is 3.65:1 and on danger 3.10:1, both short of the 4.5:1 that AA asks of the 12–14px labels they carry. (Measured in more detail in #249, which stands alone.)
Midnight sets the token to
#ffffff, so its rendering is byte-identical. But Foundry can invert the relationship — brass fill, near-black lettering, 7.4:1 — which is what lets it read as lit metal rather than a dark paint chip. With hardcoded white, every accent would have to stay dark and the skin would lose its point.Same story for the disabled state:
disabled:opacity-40works on a dark ground and inverts on a light one, where Atelier's accent at 40% over white lands on#d9bfa8and white lettering measures 1.75:1. Disabled buttons get their own colours instead of transparency.Contrast is measured, not asserted
scripts/check-skin-contrast.mjsparses the stylesheet, reads@themeand:rootas the inherited base — so a token a skin does not set is still measured as it actually ships — and fails on an undefined token rather than skipping it. 27 pairs per skin, all three new ones clear. Midnight is advisory, since its job is to be the original, gaps included.That inheritance detail is not theoretical: while rebasing this onto v0.1.24 the new
--color-focuslanded, no skin overrode it, and all three would have silently worn Midnight's blue — 2.27:1 on Lagoon. The check now catches that class of regression.Fully understand if a four-way skin picker is not where you want this app to go — the
--color-accent-inkand disabled-state parts stand on their own if you would rather take only those.🤖 Generated with Claude Code