Skip to content

feat: dark mode (light/dark/system) with persisted preference#414

Open
shaunpatterson wants to merge 3 commits into
dgraph-io:mainfrom
shaunpatterson:sp/dark-mode
Open

feat: dark mode (light/dark/system) with persisted preference#414
shaunpatterson wants to merge 3 commits into
dgraph-io:mainfrom
shaunpatterson:sp/dark-mode

Conversation

@shaunpatterson

Copy link
Copy Markdown
Contributor

What

Adds dark mode with three settings — light / dark / system — cycled by a sun/moon toggle pinned to the bottom of the sidebar. The preference lives in the (already persisted) ui store; system follows prefers-color-scheme live via a matchMedia listener.

How

  • App.js applies data-theme to <html> from the resolved setting.
  • assets/css/theme-dark.scss — all overrides scoped under [data-theme='dark'], built on a small set of CSS custom properties (--bg, --bg-raised, --text, --border, …). Bootstrap is deliberately not fully rethemed; only the surfaces Ratel uses are (panels, frames, modals, dropdowns, tables, forms, nav-tabs, alerts, the graph canvas background, entity selector, history strip).
  • CodeMirror switches reactively to the bundled material-darker theme when dark.
  • Pure helper lib/theme.js (resolveTheme, nextTheme) + reducer action setTheme.

Two real bugs found and fixed during verification:

  1. Editor.scss gutter/cursor rules outranked the CM theme by source order — re-asserted under the dark scope.
  2. .cm-invalidchar is painted black by Editor.scss (a deliberate hack: the graphql-ish editor mode flags valid DQL like dotted predicates as invalid, and black-on-white reads as normal text). On dark these tokens were invisible — the dark scope now mirrors the intent with the normal dark text color. Verified per-token via computed styles in headless Chrome.

Testing

  • 9 unit tests (theme resolution incl. unknown-setting fallback; reducer default + transitions).
  • Headless-Chrome acceptance: toggled dark via the real sidebar button, asserted data-theme, body background luminance < 80, persisted setting, and that the theme survives a full page reload. Light mode screenshot-verified unchanged.
  • npm run build passes.

Known limits: less-traveled pages (ACL/Backups/Cluster) inherit the generic overrides but weren't individually audited; the JSON tab gets a partial dark hljs override; graph node colors are JS-generated and unchanged.

🤖 Generated with Claude Code

shaunpatterson and others added 2 commits June 12, 2026 12:11
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Editor.scss paints .cm-invalidchar black on purpose: the graphql-ish
editor mode flags valid DQL (dotted predicates like dgraph.type,
numeric arguments) as invalid, and black-on-white makes those tokens
read as normal text. On the dark background they were invisible.
Mirror the intent under [data-theme='dark']: normal text color.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@shaunpatterson shaunpatterson requested a review from a team as a code owner June 12, 2026 17:37
The EditorTabs strip used hardcoded light colors, leaving a bright bar
above the editor in dark mode. Add [data-theme=dark] overrides so the
strip, inactive tabs, hover and rename input use the dark palette, and
the active tab blends into the editor header (--bg-raised) below it.

(EditorTabs ships on the multi-tab branch; these rules are dormant until
both features are present, e.g. on sp/all-features.)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant