Skip to content

Add dark mode support (system-follow) - #147

Merged
bholmesdev merged 8 commits into
bholmesdev:mainfrom
saucy-tech:feat/gh-110-dark-mode
Jul 9, 2026
Merged

Add dark mode support (system-follow)#147
bholmesdev merged 8 commits into
bholmesdev:mainfrom
saucy-tech:feat/gh-110-dark-mode

Conversation

@saucy-tech

Copy link
Copy Markdown
Contributor

Description

Adds dark mode that follows the system appearance, as discussed in #110 (spec + implementation, per your comment there).

How it works:

  • nativeTheme.themeSource flips from "light" to "system", so the native chrome and menus follow the OS.
  • The dark variant is class-based (@custom-variant dark (&:is(.dark *))), so a small renderer module (apps/desktop/src/theme.ts) watches matchMedia("(prefers-color-scheme: dark)") and toggles .dark on <html>, reacting to live OS changes. An inline <head> guard sets the class before first paint so a cold launch into dark doesn't flash light.

Surfaces covered:

  1. Desktop app — the existing .dark token block in index.css activates; placeholder values replaced with a warmer palette (neutrals at hue ~95 matching the light palette's warm family, background near Notion's #191919, warm amber selection, brand green accent).
  2. Code blocksEditorView.css had hardcoded Xcode-light hex for .hljs-*; added a .dark-scoped Atom One Dark palette.
  3. Embedded HTML appshtml-app-theme.css moves to color-scheme: light dark with a @media (prefers-color-scheme: dark) token block mirroring the desktop palette. The sandboxed iframe inherits the host's resolved scheme, so no host→iframe messaging is needed.
  4. Terminal panel — no changes needed: it already watches for .dark on <html> with a MutationObserver and ships its own dark ANSI palette, so it lights up as soon as the toggle exists.

Product + tech specs are in specs/gh-110/ (PRODUCT.md, TECH.md).

Scope note: system-follow only — no in-app theme override in this slice. The .dark class toggle keeps that easy to add later.

Closes #110

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Documentation update

Testing

  • Existing tests pass
  • Added new tests for changes
  • Tested manually (describe below)

New happy-dom unit test for the theme toggle (apps/desktop/src/theme.test.ts) covering initial dark, initial light, and live OS switching. Full workspace suites pass; pnpm build:desktop (electron-vite + tsc) and Biome are clean. (Pre-existing, unrelated on main: TerminalPanel.test.tsx fails with useResizeSeparator is not a function — fails identically on a clean main checkout.)

Manual Testing Details:

Verified on macOS: cold launch into dark (no flash), live switching while the app is open (editor, code blocks, embedded HTML app, and terminal panel all follow), and light mode unchanged. Windows smoke test + screenshots coming shortly (draft until then). The mechanism is all standard Electron/matchMedia.

Screenshots

editor-dark editor-light htmlapp-dark htmlapp-light terminal-dark terminal-light
dark-mode-macos-toggle-web.mp4

Checklist

  • I discussed this change in a GitHub issue before submitting this PR
  • I have run the linter, formatter, and tests to ensure my code is ready for review

@saucy-tech

saucy-tech commented Jul 6, 2026

Copy link
Copy Markdown
Contributor Author

Windows build and screenshots coming later tonight
cold-launch-dark

editor-dark editor-light htmlapp-dark htmlapp-light terminal-dark terminal-light

@saucy-tech
saucy-tech marked this pull request as draft July 6, 2026 23:29
The native window-control overlay (min/max/close strip on Windows/Linux)
was pinned to a light color, so it stayed white in dark mode even though
`nativeTheme.themeSource = "system"` drove the rest of the chrome. Derive
the overlay colors from the current appearance and repaint them on
`nativeTheme` "updated" so the button strip tracks live theme switches,
and seed the initial `titleBarOverlay` from the same helper so a cold
launch into dark starts dark instead of flashing a white strip.

Colors are the resolved sRGB of the `--background` / `--muted-foreground`
tokens in index.css and its `.dark` block.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 6, 2026

Copy link
Copy Markdown

@saucy-tech is attempting to deploy a commit to the bholmesdev's projects Team on Vercel.

A member of the Team first needs to authorize it.

@saucy-tech
saucy-tech marked this pull request as ready for review July 7, 2026 01:59
@bholmesdev

Copy link
Copy Markdown
Owner

Amazing work @saucy-tech! Made some subtle tweaks to bring out the highlight in the left sidebar and reduce the warmth a degree. Otherwise looked good to me.

Also, note-to-self for the future: product and tech specs are too much paperwork today. Need to pair down their level of detail and only use them when needed.

CleanShot 2026-07-08 at 23 17 52@2x

@bholmesdev
bholmesdev merged commit 8bb0d32 into bholmesdev:main Jul 9, 2026
3 of 4 checks passed
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.

Add dark mode support

2 participants