DEV Community

Weather Clock Dash
Weather Clock Dash

Posted on

Dark Mode in Firefox Extensions: Respecting System Preferences

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;
  }
}
Enter fullscreen mode Exit fullscreen mode

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);
});
Enter fullscreen mode Exit fullscreen mode

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);
}
Enter fullscreen mode Exit fullscreen mode

Then in CSS:

[data-theme="light"] {
  --bg: #ffffff;
  --text: #1a1a1a;
}

[data-theme="dark"] {
  --bg: #1a1a2e;
  --text: #e0e0e0;
}
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode
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');
  });
});
Enter fullscreen mode Exit fullscreen mode

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: currentColor work, 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);
}
Enter fullscreen mode Exit fullscreen mode

Testing Dark Mode

In Firefox DevTools, you can simulate dark mode without changing your system settings:

  1. Open DevTools
  2. Click the Responsive Design Mode icon (or Ctrl+Shift+M)
  3. 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
Enter fullscreen mode Exit fullscreen mode

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)