#15992·darkreader

[Feature Request] Keep dynamic fixes active after dark theme detector stand-down ("theme off, fixes on")

Author: k6G52m4Dz75WCreated Sep 11, 2026Updated Sep 15, 2026
Labelsenhancement

Prerequisites

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_THEME handler). The fix database is structurally empty exactly where it is needed.
  • The NO DARK THEME hint forces Dark Reader to keep transforming — rejected in #15959 as an anti-pattern, and rightly so.
  • The TARGET/MATCH hint 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 insertRule recorder: light rule at ~1195 ms, dark variant of the same component at ~1352 ms.
  • Out of the box (detectDarkTheme defaults to true), both the generic path (runCheck() checks document.documentElement.dataset.theme === 'dark' in src/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 before canCheckForStyle() would let runCheck() 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.configKEEP 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_DETECTEDCLEAN_UP sequence.
  • 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)

  1. 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?
  2. Runtime theme flips (e.g. the user switches the site to light mode and MATCH stops matching): should detection re-run, and should the kept fixes be re-evaluated or removed?
  3. 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?
  4. Fix-rot: the 2020 pixiv entry targeted styled-components hash classes that no longer exist. Is dynamic-theme-fixes still 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 (detectDarkTheme off) — 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_INDEX undefined — 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.