feat(dashboard): add light/dark theme with sidebar toggle #81

Merged
sharang merged 1 commits from feat/light-mode-theme-toggle into main 2026-05-13 11:44:23 +00:00
Owner

Summary

  • Adds a light theme as a second set of CSS custom-property tokens; existing dark Obsidian Control look is unchanged when dark is selected.
  • Adds a sun/moon toggle in the sidebar footer (above the existing collapse arrow). Persists choice in localStorage.
  • Default for new users: matches the OS via prefers-color-scheme. No JS needed on first paint — the media query covers it, so no flash.
  • Once the user clicks the toggle, their explicit choice (written as <html data-theme="light|dark">) wins over the OS pref.

How it works

  • :root keeps the dark tokens. :root[data-theme="light"] overrides them with light tokens. A @media (prefers-color-scheme: light) { :root:not([data-theme="dark"]) { … } } block applies the same light tokens when the user has made no explicit choice.
  • components/theme_toggle.rs is a tiny Dioxus component. On mount it reads localStorage; if an explicit value is stored it applies it to the DOM to avoid an OS-vs-stored mismatch flash. On click it flips the value and writes both the DOM attribute and localStorage. All web_sys calls are gated behind #[cfg(feature = "web")] so the server build stays clean.
  • Three CSS rules that hardcoded near-black hex values (page dot grid, .code-block, graph stabilization overlay) get explicit light-mode overrides so they don't render as dark patches on white.
  • web-sys feature list extended with Storage, MediaQueryList, Element so the toggle can do its job.

Test plan

  • Visit dashboard with a light-OS browser, no stored pref: page renders light immediately, no flash, sidebar shows moon icon ("Dark mode" label when expanded).
  • Click moon → page goes dark, icon becomes sun, localStorage["compliance-scanner.theme"] === "dark", <html data-theme="dark">.
  • Refresh: still dark (explicit pref wins).
  • Click sun → back to light, attribute + storage flip.
  • Visit with dark-OS browser, no stored pref: renders dark (current behavior, unchanged).
  • Inspect collapsed sidebar: toggle still visible and centered, label hidden.
  • Visit a graph page in light mode: stabilization overlay is light, not a dark splash.
  • Visit a finding detail with .code-block in light mode: code block has light bg, dark text.

🤖 Generated with Claude Code

## Summary - Adds a **light theme** as a second set of CSS custom-property tokens; existing dark Obsidian Control look is unchanged when dark is selected. - Adds a **sun/moon toggle** in the sidebar footer (above the existing collapse arrow). Persists choice in `localStorage`. - **Default for new users:** matches the OS via `prefers-color-scheme`. No JS needed on first paint — the media query covers it, so no flash. - Once the user clicks the toggle, their explicit choice (written as `<html data-theme="light|dark">`) wins over the OS pref. ## How it works - `:root` keeps the dark tokens. `:root[data-theme="light"]` overrides them with light tokens. A `@media (prefers-color-scheme: light) { :root:not([data-theme="dark"]) { … } }` block applies the same light tokens when the user has made no explicit choice. - `components/theme_toggle.rs` is a tiny Dioxus component. On mount it reads `localStorage`; if an explicit value is stored it applies it to the DOM to avoid an OS-vs-stored mismatch flash. On click it flips the value and writes both the DOM attribute and `localStorage`. All `web_sys` calls are gated behind `#[cfg(feature = "web")]` so the server build stays clean. - Three CSS rules that hardcoded near-black hex values (page dot grid, `.code-block`, graph stabilization overlay) get explicit light-mode overrides so they don't render as dark patches on white. - `web-sys` feature list extended with `Storage`, `MediaQueryList`, `Element` so the toggle can do its job. ## Test plan - [ ] Visit dashboard with a light-OS browser, no stored pref: page renders light immediately, no flash, sidebar shows moon icon ("Dark mode" label when expanded). - [ ] Click moon → page goes dark, icon becomes sun, `localStorage["compliance-scanner.theme"] === "dark"`, `<html data-theme="dark">`. - [ ] Refresh: still dark (explicit pref wins). - [ ] Click sun → back to light, attribute + storage flip. - [ ] Visit with dark-OS browser, no stored pref: renders dark (current behavior, unchanged). - [ ] Inspect collapsed sidebar: toggle still visible and centered, label hidden. - [ ] Visit a graph page in light mode: stabilization overlay is light, not a dark splash. - [ ] Visit a finding detail with `.code-block` in light mode: code block has light bg, dark text. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
sharang added 1 commit 2026-05-13 11:27:13 +00:00
feat(dashboard): add light/dark theme with sidebar toggle
CI / Check (pull_request) Successful in 10m12s
CI / Detect Changes (pull_request) Has been skipped
CI / Deploy Agent (pull_request) Has been skipped
CI / Deploy Dashboard (pull_request) Has been skipped
CI / Deploy Docs (pull_request) Has been skipped
CI / Deploy MCP (pull_request) Has been skipped
4424db5acb
Introduces a light theme alongside the existing dark Obsidian Control
look, plus a sun/moon toggle in the sidebar footer.

The dashboard's CSS already drove every surface through custom
properties on :root, so the light theme is added as a second token set
under `:root[data-theme="light"]` and, in parallel, inside a
`@media (prefers-color-scheme: light)` block guarded by
`:not([data-theme="dark"])`. Net effect:
- A user with no stored preference gets their OS theme via the media
  query (no flash, no JS required).
- A user who clicked the toggle gets `data-theme="light|dark"` set on
  `<html>`, which wins over the media query.

The toggle component (`theme_toggle.rs`) reads `localStorage` first
then `prefers-color-scheme` on mount, and writes both the DOM
attribute and `localStorage` on click. All `web_sys` calls are gated
behind `#[cfg(feature = "web")]` so the server build stays clean.

Three CSS rules that hardcoded near-black hex values (the page dot
grid, `.code-block`, and the graph stabilization overlay) get explicit
light-mode overrides so they don't render as dark patches on white.

web-sys feature list extended with Storage, MediaQueryList, and
Element so the toggle can read the media query and set the attribute.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
sharang merged commit a8cef58e02 into main 2026-05-13 11:44:23 +00:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sharang/compliance-scanner-agent#81