#186: pond accent theming (ADR 0018 stage C) #187

Merged
stwaidele merged 1 commits from feat/186-pond-accent into main 2026-07-29 10:11:45 +02:00
Collaborator

Implements #186. STACKED on #185 (which stacks on #183) — merge order: #183 -> #185 -> this; each merge shrinks the next diff accordingly.

  • pondSettingsSchema.theme = { accent: '#rrggbb' | null } (null = inherit), top-level key in the flat updatePondInputSchema AND in the PondsService settings merge (silent-no-op pitfall covered by a DB test).
  • PondThemeScope next to PondFontScope in PageEditorPage: inline --color-accent/--color-accent-contrast derived per effective mode; inline wins over tokens.css and the user-theme style — cascade pond > user > default without extra machinery.
  • PondThemeSection in the pond settings appearance section: inherit | presets | custom color, per-mode preview swatches, explicit save. i18n de+en. No server-side contrast check (derivation guarantees conformance at render time).
  • Public no-JS shell stays un-themed in v1 (ADR 0018 amendment).

Verified locally: typecheck, eslint+prettier, i18n:check; FULL api suite vs. fresh DB dorfteich_t186 (424 passed incl. new theme-merge test); shared 177 + web 131 unit tests; e2e vs. fresh env dorfteich_e2e_182: pond-theme 1/1 (scope boundary + axe on pond settings), theme-accent 2/2, theme 3/3, settings-nav 2/2, a11y light+dark 8/8; screenshots show the violet pond accent on content links while the app chrome keeps the viewer's green in both modes.

Do not merge on red CI. Issue #186 will be closed manually after a green pipeline.

Implements #186. STACKED on #185 (which stacks on #183) — merge order: #183 -> #185 -> this; each merge shrinks the next diff accordingly. - pondSettingsSchema.theme = { accent: '#rrggbb' | null } (null = inherit), top-level key in the flat updatePondInputSchema AND in the PondsService settings merge (silent-no-op pitfall covered by a DB test). - PondThemeScope next to PondFontScope in PageEditorPage: inline --color-accent/--color-accent-contrast derived per effective mode; inline wins over tokens.css and the user-theme style — cascade pond > user > default without extra machinery. - PondThemeSection in the pond settings appearance section: inherit | presets | custom color, per-mode preview swatches, explicit save. i18n de+en. No server-side contrast check (derivation guarantees conformance at render time). - Public no-JS shell stays un-themed in v1 (ADR 0018 amendment). Verified locally: typecheck, eslint+prettier, i18n:check; FULL api suite vs. fresh DB dorfteich_t186 (424 passed incl. new theme-merge test); shared 177 + web 131 unit tests; e2e vs. fresh env dorfteich_e2e_182: pond-theme 1/1 (scope boundary + axe on pond settings), theme-accent 2/2, theme 3/3, settings-nav 2/2, a11y light+dark 8/8; screenshots show the violet pond accent on content links while the app chrome keeps the viewer's green in both modes. Do not merge on red CI. Issue #186 will be closed manually after a green pipeline.
fable-5 added 4 commits 2026-07-29 09:09:55 +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
#186: pond accent theming — scoped derivation, cascade pond > user > default
All checks were successful
CI / Lint, typecheck, test (pull_request) Successful in 4m44s
CI / Build container images (pull_request) Successful in 4m2s
CI / Auth e2e pack (pull_request) Successful in 10m50s
CI / Import/export fidelity gate (pull_request) Successful in 55s
CD / Build and push images (push) Successful in 19s
CD / Deploy to Test (push) Successful in 17s
CD / Smoke tests against Test (push) Successful in 4m2s
CI / Lint, typecheck, test (push) Successful in 4m47s
CI / Build container images (push) Has been skipped
CD / Promote to Int (push) Successful in 14s
CI / Auth e2e pack (push) Successful in 10m7s
CI / Import/export fidelity gate (push) Successful in 56s
Release / Build release images and notes (push) Successful in 1m11s
Release / Release-candidate operations QA (push) Successful in 1m0s
Prod deploy / Deploy the released images to Prod (push) Successful in 17s
b5d2a436e0
pondSettingsSchema gains theme = { accent: '#rrggbb' | null } (null =
inherit the viewer's theme), exposed as a top-level key of the flat
updatePondInputSchema and included in the PondsService settings merge
(the known silent-no-op pitfall). The server validates only the hex;
conformance arises at render time: PondThemeScope (mounted around the
page content next to PondFontScope) derives the accent pair for the
EFFECTIVE mode via useEffectiveTheme and sets it as inline custom
properties — inline beats both tokens.css and the user-theme <style>,
which IS the cascade precedence pond > user > default.

Pond settings get a PondThemeSection (inherit | presets | custom color
with per-mode preview swatches, explicit save like the font manager);
AccentSwatches extracted for reuse; i18n de+en. The no-JS public shell
stays deliberately un-themed (ADR 0018 amendment).

Tests: pond DB test (theme merge keeps fonts, invalid hex 400), e2e
pond-theme.spec (scope boundary content vs. chrome, per-mode
re-derivation, axe on the pond settings page; resets the fixture pond).

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