Dark Mode in Firefox Extensions: Respecting System Preferences
Firefox users who prefer dark mode shouldn't have to manually toggle it in every extension. Here's how to automatically respect the system preference.
The CSS Media Query
/* Default: light mode */
:root {
--bg: #ffffff;
--text: #1a1a1a;
--card-bg: #f5f5f5;
--border: #e0e0e0;
}
/* Auto dark mode from system */
@media (prefers-color-scheme: dark) {
:root {
--bg: #1a1a2e;
--text: #e0e0e0;
--card-bg: #16213e;
--border: #2d3561;
}
}
Using CSS custom properties makes this effortless — one block of overrides handles the whole page.
JavaScript Detection
For dynamic behavior based on the current theme:
const isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
// Listen for changes (user switches system theme)
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {
const newScheme = e.matches ? 'dark' : 'light';
applyTheme(newScheme);
});
Three-Way Toggle: Auto / Light / Dark
For maximum user control, offer three options:
// Possible values: 'auto', 'light', 'dark'
const THEME_KEY = 'theme';
async function getCurrentTheme() {
const { theme } = await browser.storage.sync.get({ theme: 'auto' });
return theme;
}
function getEffectiveTheme(theme) {
if (theme === 'auto') {
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
}
return theme;
}
async function applyTheme() {
const stored = await getCurrentTheme();
const effective = getEffectiveTheme(stored);
document.documentElement.setAttribute('data-theme', effective);
}
Then in CSS:
[data-theme="light"] {
--bg: #ffffff;
--text: #1a1a1a;
}
[data-theme="dark"] {
--bg: #1a1a2e;
--text: #e0e0e0;
}
This is cleaner than combining CSS media queries with JS overrides.
Animating Theme Changes
Soft transitions prevent jarring flashes:
:root {
transition: background-color 0.2s ease, color 0.2s ease;
}
.card {
transition: background-color 0.2s ease, border-color 0.2s ease;
}
But disable transitions on initial page load to avoid FOUC:
// Apply theme before DOMContentLoaded to prevent flash
async function initTheme() {
const stored = await getCurrentTheme();
const effective = getEffectiveTheme(stored);
// Apply without transition on first load
document.documentElement.style.transition = 'none';
document.documentElement.setAttribute('data-theme', effective);
// Re-enable transitions after a tick
requestAnimationFrame(() => {
document.documentElement.style.transition = '';
});
}
// Call immediately, before parsing the rest of the DOM
initTheme();
Theme Switcher UI
<div class="theme-toggle" role="group" aria-label="Theme">
<button data-theme="light" title="Light mode">☀️</button>
<button data-theme="auto" title="Auto (system)" class="active">🌓</button>
<button data-theme="dark" title="Dark mode">🌙</button>
</div>
document.querySelectorAll('[data-theme]').forEach(btn => {
btn.addEventListener('click', async () => {
const theme = btn.dataset.theme;
await browser.storage.sync.set({ theme });
applyTheme();
// Update active state
document.querySelectorAll('[data-theme]').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
});
});
Colors to Get Right in Dark Mode
Common mistakes when designing dark mode:
- Don't use pure black (#000000) — it looks harsh. Use #1a1a2e or similar.
- Reduce saturation of accent colors — bright colors look garish on dark backgrounds
- Adjust shadow opacity — box shadows need lower opacity on dark backgrounds
-
Check icon colors — SVGs with
fill: currentColorwork, but PNG icons may need separate dark versions
/* Shadows that work in both modes */
:root {
--shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
[data-theme="dark"] {
--shadow: 0 2px 8px rgba(0, 0, 0, 0.4);
}
Testing Dark Mode
In Firefox DevTools, you can simulate dark mode without changing your system settings:
- Open DevTools
- Click the Responsive Design Mode icon (or Ctrl+Shift+M)
- In the toolbar, find the Color scheme toggle
Or toggle via DevTools console:
// Simulate dark mode in DevTools
// DevTools → Console → type:
window.matchMedia('(prefers-color-scheme: dark)').matches
// Returns true/false
Firefox also has a media feature emulation panel in the DevTools inspector.
Weather & Clock Dashboard supports auto/light/dark mode, following your system preference out of the box. Free, MIT licensed.
Top comments (0)