Skip to content

feat(frontend): dark-theme token overrides via data-theme with explicit-choice persistence - #1452

Open
okonkwofreeman001 wants to merge 2 commits into
solutions-plug:mainfrom
okonkwofreeman001:main
Open

feat(frontend): dark-theme token overrides via data-theme with explicit-choice persistence#1452
okonkwofreeman001 wants to merge 2 commits into
solutions-plug:mainfrom
okonkwofreeman001:main

Conversation

@okonkwofreeman001

Copy link
Copy Markdown

Closes #1324
Closes #1325
Closes #1326
Closes #1327

Description

Layers the dark theme on top of the base design tokens and reworks theme persistence so the app can tell "the user explicitly chose a theme" apart from "following the OS default".

Dark-theme overrides. The dark semantic palette now lives in a [data-theme="dark"] block in tokens.css that declares an override for every semantic token from the base :root set (brand accents, surface/background/border/foreground families, --primary, --accent, --destructive, --success, --ring, all shadows, and color-scheme). Explicit light moved to [data-theme="light"].

Persistence model. The theme mechanism switched from dark-mode/light-mode classes to a data-theme attribute that is only pinned when a preference is stored in localStorage:

  • Explicit choice → data-theme="dark"|"light" is set before first paint (blocking init script) and on every toggle.
  • No stored choice → the attribute is left absent and prefers-color-scheme is followed live via the @media (prefers-color-scheme: light) { :root:not([data-theme]) } rule plus a matchMedia listener — OS changes apply without a page reload.
  • Edge case: an explicit stored choice can never be overridden by an OS-level theme change, because the attribute outranks the media query (which is scoped to :not([data-theme])) and no OS-change listener is registered while a preference is stored.

Latent bug fixed. Previously, system-following dark applied the dark-mode class and was misread by the hook as an explicit choice (hasStoredPreference: true), which disabled the live OS-following listener. Attribute-absence now unambiguously means "following the system," so OS changes keep being tracked.

Type of Change

  • Bug fix
  • New feature
  • Refactor / code cleanup
  • Documentation update
  • CI / tooling change
  • Breaking change

Testing Done

  • useDarkMode unit suite rewritten for the attribute model — 18/18 pass, including new coverage for:
    • system-following dark does not pin the theme (distinguishes explicit choice from OS default),
    • OS theme changes are followed live with no page reload when no preference is stored,
    • an explicit stored choice registers no OS-change listener (edge case).
  • Full jest run: 5 failing suites (30 tests) are pre-existing and unrelated — verified identical with the changes stashed (LandingPage.accessibility, LandingPage.keyboard, Statistics, useAsync, useReferral).
  • tsc --noEmit: only pre-existing errors in e2e specs and frontend-mock.test.ts; none in the changed files.

Bundle Size

Not measured — the change adds no dependencies and only ~50 lines of CSS plus small TS edits (runtime code is similar in size to what it replaces).

Checklist

  • Tests pass locally
  • Documentation updated (if applicable)
  • No breaking changes, or breaking changes are documented above
  • If you added or changed an API endpoint, regenerated the OpenAPI spec and committed the result
  • If you changed system architecture, updated docs/architecture.md
  • Bundle size checked (if frontend changes)

Related Issues

Follows on from the base-token work (task #11 in the design-token series). No dedicated issue number was provided for this task, so no Closes reference is included.

okonkwofreeman001 and others added 2 commits August 29, 2026 13:24
…it-choice persistence

Move the dark semantic palette into a [data-theme="dark"] override block layered
on the base tokens, and switch the theme mechanism from dark-mode/light-mode
classes to a data-theme attribute that is only pinned for explicit user choices.
With no stored preference the app follows prefers-color-scheme live via CSS and
a matchMedia listener, and an explicit stored choice can never be overridden by
an OS-level theme change.

Also fixes a latent bug where system-following dark was misread as an explicit
choice, which disabled live OS-following.

Generated with Codebuff 🤖
Co-Authored-By: Codebuff <noreply@codebuff.com>
…ersistence

feat(frontend): dark-theme token overrides via data-theme with explicit-choice persistence
@drips-wave

drips-wave Bot commented Aug 29, 2026

Copy link
Copy Markdown

@okonkwofreeman001 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant