How Lumen finds a site's own dark theme before inverting
Most dark mode extensions recolor or invert every page. That works everywhere, and it looks worse than a site's own dark theme wherever one exists. Lumen tries the site's own theme first.
Layer 1: tell the page the system is dark
Many sites choose their theme in JavaScript with matchMedia("(prefers-color-scheme: dark)"). A script running in the page's own world replaces that call so it answers the way the user chose. It returns a real MediaQueryList, and it fires change events to listeners registered the old-fashioned ways too, so sites that switch themes live keep working.
Layer 2: switch the CSS
Many themes live only in CSS, inside @media (prefers-color-scheme: dark) rules, and an extension can't make CSS believe the system is dark. So when the system is light, Lumen swaps dark and light inside those media queries, in stylesheets, <link media> attributes and <picture><source media> elements. The dark rules start matching and the light-only ones stop.
Stylesheets from other domains can't be read from a content script, so the service worker fetches them and the dark rules are extracted and applied with chrome.scripting.insertCSS. That keeps them out of document.styleSheets, so the page can't read back the cross-origin text. Two safeguards go with it: the worker refuses localhost and private network addresses, and sends no cookies. Injected styles rank below the page's own, so the copied rules are marked !important, the way they'd override at equal specificity in a real dark mode.
Layer 3: invert, but keep what shouldn't flip
If the page still looks light after a moment (judged by sampling the colors under a grid of points on the page), Lumen applies invert(1) hue-rotate(180deg) contrast(0.9) to the root element. Images, video, canvas and embedded frames get the exact reverse filter so they keep their real colors, and so do large background photos. Because inversion preserves contrast ratios, text that was readable in light stays readable.
One subtle bug worth sharing: when <html> and <body> both have transparent backgrounds, the browser's default white canvas shows through, and the filter on <html> never reaches it, giving a page that's half dark and half white. Lumen detects that case and gives <html> an explicit white background, which then inverts with everything else.
Detecting what the page already does
Computed colors come back in several formats, including lab() and oklab() from modern CSS, so the check understands all of them rather than treating them as transparent.
Takeaway
Check for the site's own answer before computing one. It gives better results and does less work. The code and its tests are small enough to read.
Lumen is one of the five free Ease extensions. No accounts, no tracking.
Learn about Lumen