# Dark Reader: a build-from-source look at the dark mode extension for Chrome, Firefox and Edge

> Dark Reader analyzes pages at runtime and rewrites their colors instead of shipping prebuilt dark stylesheets. This covers how that works, how to build the extension yourself, and where the approach breaks down.

**darkreader/darkreader** — Dark Reader Chrome and Firefox extension

- Repository: https://github.com/darkreader/darkreader
- Website: https://darkreader.org/
- Stars: 22,422 · Forks: 2,749
- Language: TypeScript
- License: MIT
- Published: 2026-09-21 · Updated: 2026-09-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/darkreader-darkreader

## What Dark Reader actually solves, and for whom

Most sites have one color scheme. A reader who wants the other one has three options: a browser-level flag that inverts everything including images, a user stylesheet per site, or nothing. Dark Reader takes the first option and makes it selective. The README describes the extension as analyzing web pages to generate a dark mode that aims to reduce eyestrain, and that word analyze is the whole design: the extension does not apply a fixed filter, it inspects the page and computes colors.

The audience is broader than it looks. Individual users install it from the Chrome Web Store, Firefox Add-ons, the Apple App Store for Safari, the Edge Addons store or the Thunderbird add-ons site, all linked from the README. Site owners are a second, quieter audience. The project publishes an npm package called darkreader, so a website can hand control of its own dark mode to the same engine, which is unusual for an extension. If you have ever tried to convince a team to implement prefers-color-scheme across a legacy codebase, that second use is the interesting one.

## Generated CSS instead of a stylesheet: the mechanism

The extension does not ship a dark theme per site. It runs analysis in the page and produces CSS, then injects that CSS. The README exposes this directly through the API: exportGeneratedCSS returns the generated CSS as a string. That single function tells you most of the architecture. Whatever the engine decides is expressed as stylesheets, which means it can be inspected, and it also means the output is only as good as the analysis that produced it.

Because colors are computed rather than looked up, the extension needs per-site corrections. The repository has a tasks/ping.js script that references dark-sites, detector-hints, dynamic-theme-fixes and inversion-fixes, which is the shape of that correction layer: lists of sites where the default analysis needs help. The README notes that automatic syncing of these site fixes for every user was disabled because GitHub does not allow using it as a content delivery network. New releases carry the fixes instead. There is a manual workaround, which the README describes as a sequence of clicks through Dev tools, Advanced, Preview new design, then Settings, Advanced, and the Synchronize sites fixes setting. That is a lot of steps for a setting, and the fact that it sits behind a preview flag is worth knowing before you go looking for it.

One detail in the README deserves attention if you plan to embed the API: Dark Reader adds a chrome object onto window to stub functions from the webextension-api. Your page will not be untouched.

## Building the extension from source with Node.js

The README says the build script requires a JavaScript runtime, either Node.js or Deno, and recommends Node.js. It states that any version at or above 15 will work. Download the source or check out from git, open a terminal in the root folder, and run the install and build scripts.

```bash
npm install
npm run build
```

The build writes build/release/darkreader-chrome.zip for Chromium-based browsers and build/release/darkreader-firefox.xpi for Firefox. Those are the two artifacts to load unpacked or install from file, depending on the browser. The build script accepts flags; npm run build -- --help lists them, and the package.json shows named shortcuts such as build:firefox and build:plus for a Chrome MV3 build.

Deno is supported but the README calls it experimental and gives one concrete failure: if you hit Too many open files (os error 24), use a newer Deno, preferably built from source or canary. There is also an experimental path for bundling official Firefox store signatures, which the README demonstrates with a version-pinned build. It warns that only Firefox Add-ons store signatures are present in the repository, and that Node.js and TypeScript version compatibility may force you to check out an old revision, build, then return to the recent commit to run only the signature and zip steps. Treat that path as a maintenance exercise, not a routine build.

## Putting Dark Reader on your own site via the npm package

For a site owner, the extension is the wrong tool and the npm package is the right one. Install it, then call the API. The README gives the CommonJS-style example with a brightness, contrast and sepia triple, and an ES module example with the same functions renamed on import.

```javascript
DarkReader.enable({
    brightness: 100,
    contrast: 90,
    sepia: 10
});

DarkReader.disable();

DarkReader.auto({
    brightness: 100,
    contrast: 90,
    sepia: 10
});
```

The auto call is the one that matters for user experience: it enables dark mode when the system color scheme is dark, and DarkReader.auto(false) stops watching. The README also shows exportGeneratedCSS awaited to get the CSS string, and isEnabled for a boolean check. The three numeric settings are not cosmetic knobs in the usual sense; they are the parameters of the generated output, so a site that looks wrong at the defaults has a tuning surface rather than a single switch.

If you would rather not run a build, the README lists a CDN option through unpkg or jsDelivr, and a build-from-source option via npm run api. The prebuilt package and the CDN are the shortest path.

## Where the generated approach fails

Analysis cannot know intent. A page that uses a light background to represent paper, a canvas, or a product photo is not a page that wants its colors recomputed. The extension has a correction layer precisely because the general case does not hold, and the README's own framing of site fixes as something that ships with each release rather than arriving continuously means a broken site stays broken until the next version, unless the user goes through the preview-flag sequence to enable synchronization.

The second limitation is structural. The engine injects CSS into pages, which is what makes it work everywhere and also what makes it invasive. The README states that the API adds a chrome object onto window to stub webextension-api functions. If you are embedding the package, that is a global your page did not have before. If you are a user, the extension is by definition reading and modifying every page you visit, which is the honest answer to the recurring question of whether it is safe: it is MIT-licensed open source, and its job requires page access.

The third is the build. The README's signature-bundling section admits that reproducing an old signed build may require checking out an old revision because of Node.js and TypeScript compatibility. Anyone expecting to rebuild a historical artifact exactly should read that paragraph first.

## The alternative: hand-written dark themes

The obvious alternative is a site's own dark theme, driven by prefers-color-scheme, with colors chosen by a designer. The difference is not quality, it is who decides. A hand-written theme knows which element is a photo and which is a surface, because a person made that distinction. Dark Reader does not have that information and infers it.

The second alternative is a filter-based inversion, which is what browsers and simpler extensions do. That approach is deterministic and cheap, and it degrades images and brand colors uniformly. Dark Reader's analysis exists to avoid that uniform degradation, at the cost of a correction list and a build step.

For a site owner the choice is close to a fork in the road. If you have design capacity, write the theme. If you do not, DarkReader.auto() with tuned brightness, contrast and sepia is a real option that costs one dependency, and it will follow the system setting without you implementing the media query. What you give up is the guarantee that your colors are your colors.

## Licence, maintenance and what upgrading costs

The project is MIT-licensed, which is permissive and places few obligations on reuse. The practical implication for a site owner embedding the npm package is that the licence is not the constraint; the generated CSS is. If you rely on exportGeneratedCSS output, you are depending on the analysis producing the same result across versions, and nothing in the README promises that.

The repository is not archived, and the last push was on 2026-09-17, days before this was written. Releases are frequent: v4.9.132 on 2026-09-15, v4.9.131 on 2026-09-14, v4.9.130 on 2026-09-01. That cadence is the upgrade story and also the cost. Users on the store versions get fixes when the store publishes. Users who built from source get them when they rebuild. Site owners pinning the npm package get them when they bump the version and re-check their pages, because a change to the analysis can change the CSS your site emits.

The repository layout is a normal TypeScript project: src/, tests/, tasks/, docs/, plus an eslint-plugin-local.js and an eslint.config.js, with npm run lint covering src and tasks. Contributing is documented in CONTRIBUTING.md, and the README points general questions at the help page and at GitHub Discussions rather than issues.

## Frequently asked questions

The questions below are answered strictly from the README, package.json and the repository listing. Where the documentation does not cover something, the answer says so.

## Conclusion

Adopt Dark Reader if you want dark mode on sites that will never ship one, and you accept that the result is generated rather than designed. Do not adopt it if you need your site's own dark theme to be the only one, or if you cannot accept a content script running on every page. Before building from source, check that your Node.js version is at or above 15 and that npm install completes, because the build script is the only supported path to the unpacked extension. The API route is the cleaner option for site owners: npm install darkreader and DarkReader.enable() is a smaller commitment than maintaining a fork of the extension.

## FAQ

### Is Dark Reader safe to use?

The project is open source and MIT-licensed, and the README states its purpose is to analyze web pages and generate a dark mode. That analysis requires access to page content, which is inherent to how the extension works rather than an optional permission.

### Do I have to pay to use Dark Reader?

The README presents Dark Reader as an open-source MIT-licensed browser extension and links to free listings on the Chrome Web Store, Firefox Add-ons, the Apple App Store, Edge Addons and Thunderbird. No paid tier is mentioned.

### How do I activate Dark Reader on Chrome?

Install it from the Chrome Web Store link in the README, or build it yourself and load the resulting build/release/darkreader-chrome.zip. The README does not describe a separate activation step beyond the extension being enabled.

### How do I install Dark Reader on Firefox?

Install it from the Firefox Add-ons listing, or build from source with npm install followed by npm run build, which produces build/release/darkreader-firefox.xpi. The README also documents an experimental path for bundling official Firefox store signatures into the bundle.

### What is darkreader.org?

It is the project's homepage, listed as the homepage for the darkreader/darkreader repository. The README directs most questions to the help page hosted there before suggesting GitHub Discussions.

## Sources

- [darkreader/darkreader on GitHub](https://github.com/darkreader/darkreader)
- [License: MIT](https://github.com/darkreader/darkreader/blob/main/LICENSE)
- [Project website](https://darkreader.org/)
- [README](https://github.com/darkreader/darkreader/blob/main/README.md)
- [Releases](https://github.com/darkreader/darkreader/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/darkreader-darkreader
