WORK-211
Setting up your dashboard 0 entities found · 8/31 branches scanned
ID:WORK-211Status:done

Add theme toggle to Lumina

Lumina currently does not ship a theme toggle. SPEC-052's tint cascade requires a toggle that honours data-tint-lock (hides on locked pages, shows + functions on unlocked pages). This work item adds the toggle — implementation, persistence, contract documentation.

Priority:highComplexity:mediumMilestone:v0.14.0Source:SPEC-052

Criteria completion

Criteria completion: 10 of 11 (91%) checked; tracking started on May 18, no incremental history yet0%25%50%75%100%May 18Jun 15

Tracking started May 18 — check back for trends.

Branches 2
History 4
  1. 560019a
    statusreadydone
    by bjornolofandersson
  2. 9149614
    Content editedby Claude
    v0.14.0 Chunk 10: SPEC-052 site adoption (WORK-211, 215, 216) — final
  3. 3b92415
    Created (ready)by bjornolofandersson
  4. 8df92a3
    Content editedby Claude
    plan: add SPEC-052 tint cascade work items (WORK-211 to 216)

Acceptance Criteria

  • Theme toggle component shipped as ThemeToggle.svelte in @refrakt-md/svelte (not Lumina directly — adapter-level so any theme using SvelteKit can use it; Lumina styles it via the contract's --rf-* tokens)
  • Three states (auto / light / dark) cycle on click
  • State persists via localStorage (key rf-theme, in lockstep with the pre-paint script from WORK-214)
  • Pre-paint script applies saved preference before first paint (shipped in Chunk 9 / WORK-214; hooks.server.ts now injects it)
  • Hides itself when <html data-tint-lock="true"
  • MutationObserver watches data-tint-lock for client-side navigation between locked and unlocked pages
  • Locked pages preserve saved preference in localStorage — the toggle reads but doesn't apply on locked pages; reactivates on navigation to an unlocked page
  • Keyboard accessible (button element, aria-label, title, :focus-visible outline)
  • Built-in icon variants for auto/light/dark using inline SVG mask-images; class and children props allow custom presentation while keeping behaviour
  • Integrated on the refrakt site as a fixed top-right element via +layout.svelte (per WORK-215)
  • Dedicated /docs/themes/lumina/theme-toggle documentation page (deferred — the cascade docs page at /docs/themes/tint-cascade covers the toggle's behaviour in the "How it works" section; a dedicated reference page can come later if the contract grows)

Approach

The toggle is a small UI component, not a deep architectural change. Most of the work is plumbing — the inline pre-paint script, the localStorage contract, the lock-detection.

The pre-paint script is the tricky part:

<script>
  (function() {
    var locked = document.documentElement.dataset.tintLock === 'true';
    if (locked) return; // honour SSR-resolved lock
    var saved = localStorage.getItem('rf-theme');
    var resolved = saved || (matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
    document.documentElement.setAttribute('data-theme', resolved);
  })();
</script>

This runs before paint, so there's no flash. It deliberately no-ops on locked pages — SSR has already set the correct data-theme for those.

The toggle component itself is straightforward: button, three states, click handler that writes to localStorage and updates data-theme. Hide-when-locked is a CSS rule on the toggle component: [data-tint-lock="true"] .lumina-theme-toggle { display: none }.

Pre-paint script and toggle component land together — they share state (the localStorage key, the data-theme attribute) and only work as a pair.

Dependencies

  • WORK-189theme.colorScheme field at site level is the toggle's site-wide ancestor. The toggle reads data-tint-lock which SPEC-052 will emit (per WORK-214); for now, design the toggle's hide-when-locked behaviour so it works the moment data-tint-lock starts appearing.

References

  • SPEC-052 — "Toggle UI" section explains the contract
  • WORK-214 — renderer integration that will emit data-tint-lock on locked pages
  • Linear's, Vercel's, Stripe's theme toggles — references for placement / interaction patterns