#184: accent theme engine — presets and free color (ADR 0018 stage B) #185

Merged
stwaidele merged 2 commits from feat/184-theme-engine into main 2026-07-29 10:11:31 +02:00
Collaborator

Implements #184. STACKED on #183 (contains its commit b799ad1) — merge #183 first, then this PR's diff reduces to stage B only.

  • packages/shared/src/theme.ts: dependency-free WCAG math + deriveAccentTokens (hue/saturation preserved, lightness binary-searched until >= 4.5:1 against bg/bg-subtle/surface of the mode); THEME_PRESETS (pond green = default), BASE_PALETTE.
  • apply-theme.ts writes
Implements #184. STACKED on #183 (contains its commit b799ad1) — merge #183 first, then this PR's diff reduces to stage B only. - packages/shared/src/theme.ts: dependency-free WCAG math + deriveAccentTokens (hue/saturation preserved, lightness binary-searched until >= 4.5:1 against bg/bg-subtle/surface of the mode); THEME_PRESETS (pond green = default), BASE_PALETTE. - apply-theme.ts writes <style id="user-theme"> with :root:root + :root:root[data-theme='dark'] blocks (doubled :root wins over tokens.css regardless of document order — theme-init.js injects the ui.theme.css cache before the bundle styles); default preset = no override; main.tsx re-derives at startup (heals stale caches). - Settings: accent radiogroup with visible names + per-mode preview swatches + custom color input, i18n de+en. Section count unchanged (jump-nav fence untouched). - Tests: shared sweep (972 derivations, all >= 4.5:1), BASE_PALETTE<->tokens.css drift fence, apply-theme unit pack, e2e theme-accent.spec incl. axe smoke with garish yellow in both modes. Theme specs scoped via input[name] (second fieldset made bare .settings-fieldset ambiguous). - ADR 0018 amendment. Verified locally: typecheck, eslint+prettier, i18n:check, shared 177 + web 131 unit tests, fresh e2e env (DB dorfteich_e2e_182): theme 3/3, theme-accent 2/2, settings-nav 2/2, a11y light+dark 8/8; screenshots (accent UI light/dark, garish-yellow derivation visible). Side note (pre-existing, NOT from this PR): /settings shows a 23px horizontal overflow at 320px viewport also on current main — worth a separate look. Do not merge on red CI. Issue #184 will be closed manually after a green pipeline.
fable-5 added 3 commits 2026-07-29 08:59:48 +02:00
#182: top-bar theme toggle — cycle light/dark/system without a menu
Some checks failed
CI / Lint, typecheck, test (pull_request) Successful in 4m40s
CI / Build container images (pull_request) Successful in 4m1s
CI / Auth e2e pack (pull_request) Successful in 8m30s
CI / Import/export fidelity gate (pull_request) Successful in 54s
CD / Deploy to Test (push) Blocked by required conditions
CD / Smoke tests against Test (push) Blocked by required conditions
CD / Promote to Int (push) Blocked by required conditions
CI / Auth e2e pack (push) Blocked by required conditions
CI / Import/export fidelity gate (push) Blocked by required conditions
CI / Build container images (push) Blocked by required conditions
CD / Build and push images (push) Has been cancelled
CI / Lint, typecheck, test (push) Has been cancelled
b799ad180b
An IconButton between the notifications bell and the user menu cycles
the theme mode in radio order (sun/moon/monitor mirror the CURRENT
choice). New useThemeMode() hook is the single write path (persist +
apply + same-document event), so the settings radios and the toggle
stay in sync; AppearanceSection now uses it too. Also rendered for
signed-out visitors — the mode is a device-local preference. i18n de+en;
unit tests for cycle/setter, theme.spec covers cycling, radio sync,
persistence, and the signed-out top bar.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QRtCnB3uLdQtFmvp9HXcRX
Dependency-free packages/shared/src/theme.ts: relativeLuminance /
contrastRatio (WCAG 2.1), deriveAccentTokens(hex, mode) keeps hue and
saturation and binary-searches lightness until the accent clears 4.5:1
against the mode's bg, bg-subtle AND surface (a passing hex is kept
verbatim; accent-contrast follows by symmetry). THEME_PRESETS (pond
green = default), BASE_PALETTE as the canonical backgrounds. A sweep
test (36 hues x 3 saturations x 3 lightnesses x both modes) fences the
by-construction guarantee for arbitrary input.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QRtCnB3uLdQtFmvp9HXcRX
#184: user accent theming — presets and free color as one mechanism
Some checks failed
CI / Lint, typecheck, test (pull_request) Successful in 4m41s
CI / Build container images (pull_request) Successful in 4m4s
CI / Auth e2e pack (pull_request) Successful in 11m40s
CI / Import/export fidelity gate (pull_request) Successful in 52s
CD / Deploy to Test (push) Blocked by required conditions
CD / Smoke tests against Test (push) Blocked by required conditions
CD / Promote to Int (push) Blocked by required conditions
CI / Auth e2e pack (push) Blocked by required conditions
CI / Import/export fidelity gate (push) Blocked by required conditions
CI / Build container images (push) Blocked by required conditions
CD / Build and push images (push) Has been cancelled
CI / Lint, typecheck, test (push) Has been cancelled
83a2fe470e
apply-theme.ts derives BOTH modes' accent tokens from the stored choice
(ui.theme.accent: preset id or {custom:'#hex'}) and writes them as
<style id="user-theme"> with :root:root + :root:root[data-theme='dark']
blocks — the doubled :root beats tokens.css regardless of document
order, since theme-init.js injects the ui.theme.css cache during <head>
parsing, before the bundle styles. The default preset means NO override
(hand-tuned tokens.css values stay). main.tsx re-derives from the
choice at startup, healing stale caches after app updates.

Settings: accent radiogroup inside the Appearance section (visible
names, color never the only cue) with per-mode preview swatches on
each mode's canonical background, plus a custom color input; i18n
de+en. The second fieldset made bare .settings-fieldset locators
ambiguous — theme specs now scope via input[name] (fence stays).

Tests: apply-theme unit pack, BASE_PALETTE<->tokens.css drift fence in
theme-contrast.test.ts, e2e theme-accent.spec (instant apply, pre-paint
persistence, default removes override, axe smoke with garish yellow in
both modes). ADR 0018 amendment documents the stage-B details.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QRtCnB3uLdQtFmvp9HXcRX
stwaidele merged commit 83a2fe470e into main 2026-07-29 10:11:31 +02:00
Sign in to join this conversation.
No description provided.