Expired Queues
Published since 2009 Core-CSS.net → CSSJunction.com → Expired Queues Web development
Developer Toolspoststools

A lightweight theme toggle extension and better than DarkReader

When I visit any website, I often toggle based on my room ambient light. I wanted a simple extension that toggles between dark and light them and remembers that for next time. The extensions (DarkReader for eg) I tried did a lot more than that, so I wrote Shadeflip.

Let me explain why, and the tricks that make it work, and what it does to stay out of website fingerprinting. Take that LinkedIn.

What it does

Click the toolbar extension button, or press Alt+Shift+D (Option+Shift+D on a Mac), and the current site switches between light and dark. Shadeflip saves the choice for that address. If you want dark on all sites, you set dark as the default and switch back the few that look better light.

Trick 1: CSS invert filter for the full page, then invert the photos/media back

Recoloring every element on a page takes work and breaks on some sites. Shadeflip applies one CSS filter to the root element instead:

html[data-xxxxxxxxxx] {
  filter: invert(1) hue-rotate(180deg) !important;
}

invert(1) turns white into black. It would also turn blue links yellow, so hue-rotate(180deg) turns the hues back round, leaving blue roughly blue. Photos and video would come out as negatives, so Shadeflip applies the same filter to them a second time, which roughly cancels it out:

html[data-xxxxxxxxxx] :is(img, video, canvas, iframe, embed, object, svg image, [style*="background-image"]) {
  filter: invert(1) hue-rotate(180deg) !important;
}

small downside

This works on nearly every site. But... tt has two side effect. Some colors drift, so a brand's exact orange won't survive the round trip. And a filter on the whole page can make scrolling slower on very heavy pages.

But again, the sites are now dark and light theme supported. I have encountered fairly many sites that I know doesnt have dark theme yet.

Trick 2: check whether the page is already dark

If you've set dark as your default and a site already has a dark theme, inverting it would make it light. So before it flips anything, Shadeflip looks at the page.

It picks five points on the screen (the center and four points around it) and asks the browser which element is at each one. For each element, it walks up the tree until it finds a background color, then works out the color's relative luminance, the same measure WCAG uses for contrast. If most of the five points come out dark, the page counts as dark and Shadeflip leaves it alone.

Modern CSS has a lot of color formats, including oklch() and color(). Rather than parse them, Shadeflip paints the color into a 1×1 canvas and reads the pixel back. The browser does the conversion.

Trick 3: avoid the white flash on load

A white flash on every page load defeats the point of dark mode. Shadeflip's script starts at document_start, before the page has any content. For sites you've set, it keeps a note of whether the page looked dark or light last time. It reads that note from extension storage as the page starts loading, so on most loads it can apply the flip before the page appears, without waiting to measure it.

The main flip rules come from the background script, which takes a moment. Until they arrive, Shadeflip puts the same invert filter inline on the <html> element and removes it once the proper rules are in. For that moment photos are negatives, which is less jarring than a white page.

Some sites switch their own theme after load. Shadeflip watches the class, style and data-theme attributes on <html> and <body>, and the system's light or dark setting, and checks the page again when any of them change.

Staying out of website fingerprinting

In April 2026, BleepingComputer reported that LinkedIn scans for more than 6,000 Chrome extensions when you visit in a Chromium browser. The results go into a device fingerprint along with details such as your CPU cores, memory, screen size and time zone. LinkedIn told BleepingComputer it uses the scan to protect the platform and its users.

The scan relies on a manifest field called web_accessible_resources. Extensions list files there so web pages can load them. LinkedIn's script tries to fetch a known file from each extension's chrome-extension:// address, and if the fetch works, that extension is installed. BrowserGate's write-up describes a second pass that looks for traces extensions leave in the page, such as chrome-extension:// links.

Shadeflip gives neither pass anything to find:

  • Manifest: it lists no web-accessible resources, so there's no chrome-extension:// file for a page to fetch.
  • Page marker: to mark a flipped page, Shadeflip adds an attribute to <html>. The attribute's name is ten random letters, generated fresh on every page load and never stored. The flip rules key on that name.
  • Stylesheet: the flip rules go only into pages you've flipped, through the browser's scripting.insertCSS API, so they never appear as a <style> tag in the page.

Dark Reader, by comparison, sets a data-darkreader-scheme attribute on <html>. It has the same name on every site, so any site can check for it.

There are limits. A page Shadeflip has flipped can see an invert filter on its root element and an unfamiliar attribute, and could guess that some dark mode extension is running. There's no fixed name that points to Shadeflip, and nothing carries over from one site or one visit to the next.

About Dark Reader

Dark Reader is open source, and a lot of people rely on it. It also has sliders for brightness, contrast, sepia and grayscale, per-site color settings, and a "Pay for using Dark Reader" banner. People who have already paid have asked for a way to hide it. I've never wanted to read a news site in sepia.

Shadeflip has one button and no payment banner.

Try it

Install it from the Chrome Web Store in Chrome, Edge, Brave or another Chromium browser, or from Firefox Add-ons in Firefox.

The code is short enough to read in one sitting, and the How it works page lists every permission and what it's for. If a site looks wrong, open an issue.