Skip to content

feat: M2 — light/dark theme toggle + persistence#96

Merged
bilbospocketses merged 8 commits into
masterfrom
feat/m2-theme-toggle
Jun 3, 2026
Merged

feat: M2 — light/dark theme toggle + persistence#96
bilbospocketses merged 8 commits into
masterfrom
feat/m2-theme-toggle

Conversation

@bilbospocketses
Copy link
Copy Markdown
Owner

M2 — Theme Toggle + Persistence

User-facing light/dark theming on top of M1's tokens. Implements docs/.../specs/2026-06-02-svgedit-theme-toggle-design.md.

Commits

  • theme.ts extended — getCurrentTheme/toggleTheme/resolveInitialTheme + applyTheme dispatches a svgedit-themechange CustomEvent.
  • theme pref + startup — apply persisted theme after configObj.load() (stored choice wins over OS; no FOUC).
  • se-theme-toggle — top-bar sun/moon icon (token chrome + currentColor SVG); click toggles + persists via ConfigObj.pref('theme') → ext-storage/localStorage.
  • Embed reconciliation__setTheme/?theme= rerouted from the legacy class-based src/embed/theme.ts (retired) to M1's html[data-theme] tokens; embed unit + e2e tests updated to assert data-theme.
  • ?theme= standalone — honored at startup (URL > stored pref > system).
  • Rulers re-theme on change — Canvas-2D ruler ticks resolve --se-text (probe element) + redraw on svgedit-themechange. Closes the M1 follow-up (rulers were stuck black in dark mode).

Verified locally: npm run build green; npx vitest run 733 (738 −5 retired legacy embed-theme unit tests); npm run lint green (enforcing hex-guard — toggle uses tokens + currentColor); e2e green (chromium): theme toggle+persist, ?theme=dark, ruler ink follows theme, embed data-theme. Full cross-browser e2e on CI.

Behavior: persistence is gated on the user's storage opt-in (consistent with how svgedit persists all prefs). Base = master (incl. M1). Second item of the UI-Modernization program.

Adds <se-theme-toggle id="theme_toggle"> to the MainMenu top bar as a
sibling of the main_button menu. The component wires to the svgedit-
themechange event for live icon sync (sun↔moon), toggleTheme() for
apply+announce, and bubbles toggle-theme so MainMenu can persist the
choice via configObj.pref('theme'). E2e test verified on chromium+firefox
(explicit LS+cookie write needed to exercise loadContentAndPrefs gate in
headless Playwright — see DONE_WITH_CONCERNS note).
…okens

- server.ts: swap import from legacy embed/theme.js to editor/styles/theme.js
  applyTheme()/resolveInitialTheme(); params.theme and __setTheme now set
  html[data-theme] instead of adding theme-* CSS classes to body
- Delete src/embed/theme.ts (legacy class-based module, now unreferenced)
- Delete tests/unit/embed-theme.test.ts (tested only the retired module)
- Update embed-server tests: assert html[data-theme] attribute instead of
  body.classList theme-* checks; add data-theme cleanup in beforeEach
- tsconfig.embed.json: widen rootDir from src/embed to src so server.ts
  may import from src/editor/styles/theme.ts without TS6059 error
Follow-up to the embed __setTheme/?theme= reroute (217af95): the embed
theme reroute now sets html[data-theme] inside the iframe instead of adding
body.classList theme-* classes, so these two e2e assertions were left failing.
Update them to read frame :root [data-theme]; await the setTheme round-trip
via expect.poll. Echo-loop-prevention test unchanged (asserts event emission).
@bilbospocketses bilbospocketses merged commit 6762ad1 into master Jun 3, 2026
9 checks passed
@bilbospocketses bilbospocketses deleted the feat/m2-theme-toggle branch June 3, 2026 03:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant