Dark mode: Light/Dark/System setting with token-based dark palette #180

Closed
opened 2026-07-28 20:35:57 +02:00 by fable-5 · 1 comment
Collaborator

The SPA is light-only today; only the no-JS public shell has a minimal dark block (#167). Add a real dark mode as the first stage of the theming roadmap (accent theming and pond accents follow as separate issues).

Scope

  • Dark palette as a second token set in tokens.css via :root[data-theme='dark']; JS always resolves to a concrete data-theme (light/dark), so the selector contract stays simple for later theming stages. color-scheme per theme so scrollbars and native controls follow.
  • Three-way user setting Light / Dark / System, stored device-locally (ui.theme.mode in localStorage, like the single-key-shortcut toggle from #170). New Appearance section on the settings page (fieldset with native radios, de+en).
  • Flash-of-white prevention: external public/theme-init.js loaded in <head> before the bundle (CSP script-src 'self' forbids inline scripts), plus color-scheme meta and paired theme-color metas in index.html.
  • Migrate the ~10 remaining hardcoded colors in base.css to new tokens first (badges, danger hover, notification badge, overdue date) - light rendering stays pixel-identical.
  • Contrast per ADR 0017 (text >= 4.5:1, UI >= 3:1) for the whole dark palette, mechanically asserted by a unit test that parses tokens.css.
  • ADR 0018 documenting the theming model (broad: modes now, accent/pond theming later).

Acceptance criteria

  • Settings offer Light/Dark/System; the choice applies instantly, persists across reloads, System follows OS changes live.
  • No white flash on load when dark is active (also offline/PWA).
  • a11y e2e pack runs its scans in light AND dark with zero axe violations.
  • Token contrast unit test covers both palettes.
  • pnpm typecheck, lint, i18n:check green.
The SPA is light-only today; only the no-JS public shell has a minimal dark block (#167). Add a real dark mode as the first stage of the theming roadmap (accent theming and pond accents follow as separate issues). ## Scope - Dark palette as a second token set in `tokens.css` via `:root[data-theme='dark']`; JS always resolves to a concrete `data-theme` (`light`/`dark`), so the selector contract stays simple for later theming stages. `color-scheme` per theme so scrollbars and native controls follow. - Three-way user setting Light / Dark / System, stored device-locally (`ui.theme.mode` in localStorage, like the single-key-shortcut toggle from #170). New Appearance section on the settings page (fieldset with native radios, de+en). - Flash-of-white prevention: external `public/theme-init.js` loaded in `<head>` before the bundle (CSP `script-src 'self'` forbids inline scripts), plus `color-scheme` meta and paired `theme-color` metas in `index.html`. - Migrate the ~10 remaining hardcoded colors in `base.css` to new tokens first (badges, danger hover, notification badge, overdue date) - light rendering stays pixel-identical. - Contrast per ADR 0017 (text >= 4.5:1, UI >= 3:1) for the whole dark palette, mechanically asserted by a unit test that parses `tokens.css`. - ADR 0018 documenting the theming model (broad: modes now, accent/pond theming later). ## Acceptance criteria - Settings offer Light/Dark/System; the choice applies instantly, persists across reloads, System follows OS changes live. - No white flash on load when dark is active (also offline/PWA). - a11y e2e pack runs its scans in light AND dark with zero axe violations. - Token contrast unit test covers both palettes. - `pnpm typecheck`, lint, `i18n:check` green.
Author
Collaborator

Shipped with PR #181 (merged fast-forward, main=77df813). CI run 454 green (453 caught the settings-nav section-count fence, fixed). CD run 455 green; verified live on Test and Int: readyz 5/5 ok, theme-init.js served (200), dark token block present in the shipped CSS bundle (index-B3JdpRej.css). ADR 0018 documents the theming model; accent/pond stages follow as separate issues.

Shipped with PR #181 (merged fast-forward, main=77df813). CI run 454 green (453 caught the settings-nav section-count fence, fixed). CD run 455 green; verified live on Test and Int: readyz 5/5 ok, theme-init.js served (200), dark token block present in the shipped CSS bundle (index-B3JdpRej.css). ADR 0018 documents the theming model; accent/pond stages follow as separate issues.
Sign in to join this conversation.
No Milestone
No project
No Assignees
1 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: stwaidele/dorfteich#180
No description provided.