Browser Color-Scheme Isolation — Delivery Notes
Delivery summary for making Tranquil’s browser tabs render like a conventional browser even though the editor forces
nativeTheme.themeSourceto match its own (usually dark) chrome. This note says what shipped and where.
The problem
Pulsar’s theme-manager (src/theme-manager.js refreshWindowTheme) forces nativeTheme.themeSource to "dark"/"light" so native chrome (menus, scrollbars) matches the editor theme. But themeSource is app-global — it also drives what every web frame sees, including the browser’s <webview> guests. Electron 30 has no per-webContents color-scheme flag, so the only lever is the
Chromium DevTools Protocol: attach the debugger to each guest and override rendering there.
There are two distinct symptoms, and they need two different CDP overrides — fixing one does not fix the other:
| Symptom | Cause | Lever |
|---|---|---|
| Site renders darker than normal (it ships a dark stylesheet) | prefers-color-scheme: dark reported to the guest | Emulation.setEmulatedMedia |
| Editor’s dark backdrop shows through the site | Site sets no html/body background, so Chromium paints its default canvas — which follows the forced-dark themeSource | Emulation.setDefaultBackgroundColorOverride |
The first override only satisfies media queries. A page that never asks about the color scheme —
e.g. a bare Markdown-export page whose body sets only margin and font — has no media query to
satisfy, so its canvas just inherits the dark base color and the editor chrome bleeds through.
The confirming example was qount25.dev/Shrimple/: its stylesheet
sets only margin / line-height / font on body, no background-color anywhere.
What shipped
Both overrides are pinned to a light guest (a browser’s conventional default and the “original” appearance sites were reported to lose):
Emulation.setEmulatedMedia→prefers-color-scheme: lightEmulation.setDefaultBackgroundColorOverride→ opaque white ({ r:255, g:255, b:255, a:1 })
They are applied together in applyScheme, so they share one lifecycle:
- Re-primed on every navigation —
attach()(not justapplyScheme()) runs ondom-readyanddid-finish-load, so a failed initial attach (guest CDP target not ready at creation) is retried and a cross-process navigation that resets the overrides gets them back. - Released/reclaimed around native DevTools — opening DevTools on a
<webview>needs the single CDP client free; Electron auto-detaches our debugger, and we reclaim + re-apply ondevtools-closed. - Only
webContentsof type'webview'(the live guest tabs) are managed. HAR capture attaches its own debugger to a hidden capture window (type: 'window'), never a live tab, so the two never contend for the one CDP client awebContentsallows.
Where it lives
tranquil-client—src/main-process/tb-color-scheme.js—manageBrowserColorScheme(wc); theGUEST_SCHEMEandGUEST_DEFAULT_BGconstants; attach / re-prime / DevTools handling.tranquil-client—src/main-process/cz-init.js— wires guestwebContentsintomanageBrowserColorScheme.
Constraints / follow-ups
- Pinned to light, not OS-following. A real browser would follow the OS appearance. The clean API
for reading the OS setting while
themeSourceis overridden (nativeTheme.shouldUseDarkColorsForSystemIntegratedUI) doesn’t exist in Electron 30. Revisit and switch to OS-following after the Pulsar/Electron upgrade — at which pointGUEST_DEFAULT_BGshould track the chosen scheme (white for light, a dark canvas for dark) instead of being constant white.
Verified
prefers-color-schemeoverride: a site with a dark media-query stylesheet renders in its light appearance in a browser tab. ✅- default-background override:
qount25.dev/Shrimple/(nobodybackground) renders on a white canvas instead of showing the dark editor backdrop. ✅ (confirmed live)