[Feature Request] Keep dynamic fixes active after dark theme detector stand-down ("theme off, fixes on")
Prerequisites
- I searched for any existing report about this feature request to avoid opening a duplicate.
Is this feature request related to a problem?
Yes
Feature Request Description
Problem
Dark Reader's dark theme detector is strictly binary: when it concludes a site ships its own dark theme, it performs a full stand-down (removeDynamicTheme() + CLEAN_UP), discarding everything — including URL-matched dynamic fixes. That verdict is architecturally correct (a native dark theme should not be double-transformed), but it leaves a real class of sites unpatchable: natively dark sites whose dark theme itself has a first-paint / hydration bug. This pattern is common in SPAs with CSS-in-JS theme contexts that initialize after hydration. On such sites:
- No dynamic fix can help, because fixes are torn down together with the theme at the moment of stand-down (
src/inject/index.ts,ADD_DYNAMIC_THEMEhandler). The fix database is structurally empty exactly where it is needed. - The
NO DARK THEMEhint forces Dark Reader to keep transforming — rejected in #15959 as an anti-pattern, and rightly so. - The
TARGET/MATCHhint merged in #15959 cannot express it either (see below).
So today there is no mechanism that can say: "trust this site's dark theme, but keep the documented fix entries to patch its broken first paint."
Concrete example: pixiv.net
- An inline head script sets
<html data-theme="dark">before first paint — correct. - But the React theme context initially renders styled-components with light values and only syncs to dark after hydration. Captured with an
insertRulerecorder: light rule at ~1195 ms, dark variant of the same component at ~1352 ms. - Out of the box (
detectDarkThemedefaults totrue), both the generic path (runCheck()checksdocument.documentElement.dataset.theme === 'dark'insrc/inject/detector.ts) and the merged hint detect the site as dark and Dark Reader stands down entirely — leaving the white window exposed on every warm-start load. - The merged
TARGET html+MATCH [data-theme="dark"]hint is behaviorally a no-op here: it matches the same attribute on the same element the generic path already checks. Its only effect is timing —detectUsingHint()can fire beforecanCheckForStyle()would letrunCheck()run, so the stand-down happens earlier and covers less of the race window. (Repro is timing-dependent: on MV3 cold start, service-worker wake-up can push the inject → detect cycle past the hydration window, which is likely why the flash did not reproduce during review.)
Proposed solution
A new opt-in directive in detector-hints.config — KEEP FIXES, usable alongside TARGET/MATCH:
- On dark detection for a hinted site, the content script drops the generated theme but keeps the URL's dynamic fix entries applied ("theme off, fixes on"), instead of the current
removeDynamicTheme()+DARK_THEME_DETECTED→CLEAN_UPsequence. - A fixes-only rebuild should skip the UA-stylesheet overrides an active theme injects (e.g.
html { background-color }/color-scheme), since the native theme already handles those. - Everything remains opt-in: sites without the directive behave exactly as today.
This directly resolves the anti-pattern concern from #15959 — Dark Reader no longer transforms native dark stylesheets, it only applies explicitly documented per-site patches — and it gives the fix database an actual role on native-dark sites. Without it, writing a per-site fix for pixiv is pointless, because no such fix can survive the stand-down.
Design questions (cc @alexanderby)
- Is a hint directive the right shape, or would you prefer fixes to live in a separate stylesheet channel from the start, so stand-down removes only the theme channel?
- Runtime theme flips (e.g. the user switches the site to light mode and
MATCHstops matching): should detection re-run, and should the kept fixes be re-evaluated or removed? - After
DARK_THEME_DETECTED, the background currently treats the tab like a dark-listed site (isURLEnabled()). Should the fixes-only state persist across navigations in the same way? - Fix-rot: the 2020 pixiv entry targeted styled-components hash classes that no longer exist. Is
dynamic-theme-fixesstill the right home for such patches, or should they belong to static themes / a new category?
Screenshots
- insertRule recording of the pixiv.net hydration race: light rule inserted at ~1195 ms, dark variant of the same component at ~1352 ms (attached).
- Console trace of the warm-start sequence: DR theme injected → detector matches
html[data-theme="dark"]→ theme removed → white components visible until hydration completes. - Control: same load with the detector disabled (
detectDarkThemeoff) — no flash, Dark Reader's near-identity transform covers the window.
Additional Context
- Background discussion: #15959 (pixiv.net detector hint; includes the hydration-race analysis and the review conversation with @Myshor, who suggested opening this issue: "Proposing a new engine behavior or config directive ... affects core Dark Reader architecture beyond just pixiv.net").
Related issues (searched before filing; no duplicate of this proposal found):
Same code path, state lifecycle: #15509 (dark-detected / NO DARK THEME state not re-evaluated during in-site navigation), #15913 + #15990 (cold-start race leaving
DETECTOR_HINTS_INDEXundefined — also why detector-related flashes reproduce inconsistently).Hint DSL extensions currently in flight: #15929, #15930 (
MATCH SYSTEM DARK/LIGHT) — this proposal adds a post-verdict directive rather than another matching mode.Verdict-accuracy requests, different problem: #1327, #10792, #13160, #12238, #15033 — these concern whether a site is dark; this proposal concerns what Dark Reader retains after a correct verdict.
Likely unattributed instances of the same symptom: #4180 (Google Search), #13823 (addons.mozilla.org), #3307 (git.sr.ht).
Tested with a dev build of main (~4.9.130), Chrome XX (MV3), Windows 11. Repro: load https://pixiv.net with default settings on a warm start.
Happy to implement the directive and tests if the direction looks acceptable.
Source: darkreader/darkreader