← All posts

Tutorials

Dark mode for any website — 4 CSS approaches

Your OS has had dark mode for years. Your browser supports it. Most apps respect it. Then you open some documentation, a blog or a 2014 intranet — and get hit in the face with 100% white background at 11pm. Four methods to fix this once and for all.

Why this problem exists at all

The prefers-color-scheme CSS standard has been in Chrome since 2019 and Safari since 2018. Your OS knows your preference, the browser propagates it, sites should respect it. Should — in practice ~40% of sites ignore it. Reasons: legacy codebase, no budget for a second theme, marketing that wants "brand consistency", the author hasn't gotten to it yet.

If the author won't do it for you — do it yourself. Four methods, in order of increasing elegance.

Method 1 — universal filter (works on anything)

One snippet, fits any site, no analysis of the specific CSS required:

html {
  filter: invert(0.92) hue-rotate(180deg);
  background: #1a1a1a;
}

/* Re-invert images, video and iframes so they look
   natural — otherwise your friends' selfies look like
   the green-skin filter. */
img, video, picture, iframe, svg,
[style*="background-image"] {
  filter: invert(1) hue-rotate(180deg);
}

Trick: invert(0.92) instead of full invert(1) gives a softer black than a hard white→black swap — easier on the eyes. hue-rotate(180deg) corrects colours so orange stays orange (not turn into blue).

Pros: works everywhere, zero analysis.
Cons: drop shadows, gradients, subtle gray tones look weird. Some fixed elements (sticky headers) can desync during scroll.

Method 2 — force prefers-color-scheme: dark

Sites that have dark mode but only respond to OS-level preference. What if you keep the OS in light (because you're designing in Figma) but want GitHub docs dark? Override matchMedia:

// Force prefers-color-scheme: dark via JS
const dark = window.matchMedia('(prefers-color-scheme: dark)');
Object.defineProperty(dark, 'matches', {
  get: () => true,
  configurable: true,
});

// Notify listeners (sites attached via addEventListener)
dark.dispatchEvent(new Event('change'));

This works on any site using window.matchMedia('(prefers-color-scheme: dark)') for detection — all modern React/Vue apps. Won't work on sites that detect once at load (you'd have to refresh), or on static CSS without JS detection.

Method 3 — override CSS custom properties

Sites built on CSS variables (~60% of modern webapps) typically have theme tokens like --bg-primary, --text-primary. Open DevTools, find the names, override:

/* Example: Stripe Dashboard, where everything is variable-based */
:root {
  --color-canvas-default: #0d1117 !important;
  --color-canvas-subtle: #161b22 !important;
  --color-fg-default: #c9d1d9 !important;
  --color-fg-muted: #8b949e !important;
  --color-border-default: #30363d !important;
}

Pros: precise, doesn't mess up images or shadows.
Cons: 5 minutes of digging in DevTools to find variable names. The site must use CSS vars (check: document.documentElement.style in the console).

Method 4 — dedicated per-site dark theme

For sites you spend hours every day on — 30 minutes for a tailored theme is worth it. Selective override:

/* Your generic corp intranet */
body, .page-content, .sidebar, .top-bar {
  background: #1a1a1a !important;
  color: #e0e0e0 !important;
}

.card, .panel, .modal {
  background: #242424 !important;
  border-color: #333 !important;
}

a, a:visited { color: #58a6ff !important; }
a:hover { color: #79c0ff !important; }

input, textarea, select {
  background: #1a1a1a !important;
  color: #e0e0e0 !important;
  border-color: #444 !important;
}

/* Tables — most critical for readability */
table th { background: #2d2d2d !important; color: #fff !important; }
table tr:nth-child(odd) { background: #1f1f1f !important; }
table tr:nth-child(even) { background: #1a1a1a !important; }

Start from the main containers (body, page), drill down to components (cards, modals), end with form elements and tables. Without !important you usually can't win — the site has its own styles at higher specificity.

Common pitfalls

How to plug into JustZix

  1. Install JustZix (2 minutes).
  2. Create a folder called "Dark mode".
  3. Rule "Dark mode everywhere": URL pattern *, CSS = Method 1. Set it inactive by default — toggle on when you need it.
  4. Rule per favourite site: pattern https://myapp.com/*, CSS = Method 3 or 4 (with specific tokens / selectors). Always active.
  5. Toggle the whole folder with one click on the floating button — light/dark in one move.

What's next

The same hierarchy (universal → precise per-site) applies in other rule categories — see Examples and Use cases. Dark mode is just the most obvious case.

Install JustZix for free and finally control how the sites you stare at for 8 hours a day look.

Rate this post

No ratings yet — be the first.

Try it yourself

Install JustZix and paste any snippet from this article. Two minutes from zero to a working rule across all your devices.

Get JustZix

Features · How it works · Examples · Use cases