← Blog/Chrome Extension

Build a Dark Mode Chrome Extension — Toggle Dark Mode on Any Website

Learn how to build a Chrome Extension that injects CSS to toggle dark mode on any website. Uses chrome.scripting to inject styles, and chrome.storage to remember your preference per domain.

📅 2026-06-262 min read📚 Ebook #04

How Dark Mode Extensions Work

Browser dark mode extensions work by injecting CSS into the current webpage. The most effective technique uses the CSS filter property to invert all colors and then rotate the hue to correct for unnatural-looking colors.

The CSS Filter Trick

This single CSS rule turns any website dark:

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

/* Un-invert images so they look normal */
img, video, canvas, picture {
  filter: invert(1) hue-rotate(180deg) !important;
}

The invert(1) flips all colors — white becomes black, black becomes white. The hue-rotate(180deg) corrects the color shift that inversion causes, so photos and graphics look natural.

chrome.scripting — Injecting CSS from the Popup

The popup cannot directly modify a webpage's styles. It needs to use chrome.scripting.insertCSS:

// Turn dark mode ON for the current tab
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
  chrome.scripting.insertCSS({
    target: { tabId: tabs[0].id },
    css: `html { filter: invert(1) hue-rotate(180deg) !important; }`
  });
});

// Turn dark mode OFF
chrome.scripting.removeCSS({
  target: { tabId: tabs[0].id },
  css: `html { filter: invert(1) hue-rotate(180deg) !important; }`
});

Remember Preference Per Domain

The extension should remember whether dark mode is on or off for each website separately:

// Save preference for this domain
function savePref(domain, enabled) {
  chrome.storage.local.get(['prefs'], (result) => {
    const prefs = result.prefs || {};
    prefs[domain] = enabled;
    chrome.storage.local.set({ prefs });
  });
}

// Load preference when popup opens
function loadPref(domain, callback) {
  chrome.storage.local.get(['prefs'], (result) => {
    const prefs = result.prefs || {};
    callback(prefs[domain] || false);
  });
}

This is a preview. The full ebook includes the complete extension with toggle button animation, domain extraction, whitelist management, and handling edge cases like iframes.

Ready to Build This Yourself?

This article is a preview. The full ebook has complete code, detailed explanations, troubleshooting tips, and bonus sections — all in a downloadable PDF.

Buy Full Ebook — $1.50 in $YFIN
Pay with $YFIN on BNB Smart Chain · 30% burned permanently