How to Add Dark Mode Support to WordPress Without Plugins

January 6, 2026
How to Add Dark Mode Support to WordPress Without Plugins

Dark mode is no longer a “nice to have”.
Modern operating systems expose a user preference for light or dark color schemes,
and browsers make this preference available via CSS and JavaScript.

This article explains how to add dark mode support to a WordPress theme using code only,
without plugins, without bloated toggles, and without breaking performance or accessibility.

How Dark Mode Works on the Web

Browsers expose the user’s OS-level preference via:

  • CSS media query: prefers-color-scheme
  • JavaScript: window.matchMedia()

Valid values are:

  • light
  • dark

A WordPress theme should:

  • Respect the system preference by default
  • Use CSS variables for maintainability
  • Optionally allow a manual override (stored locally)

Recommended Architecture

The cleanest approach is:

  • Define color tokens using CSS custom properties
  • Override them in prefers-color-scheme: dark
  • Optionally add a class-based override for a toggle

This avoids duplicating entire stylesheets.

1) Define Color Tokens with CSS Variables

Start by defining semantic color variables in :root.
These represent intent, not specific colors.

:root {
  --color-bg: #ffffff;
  --color-text: #111111;
  --color-muted: #666666;
  --color-border: #e5e5e5;
  --color-accent: #0066cc;
}

Use these variables throughout your theme:

body {
  background-color: var(--color-bg);
  color: var(--color-text);
}

a {
  color: var(--color-accent);
}

hr {
  border-color: var(--color-border);
}

2) Add Dark Mode via prefers-color-scheme

Override only the variables inside the dark mode media query.

@media (prefers-color-scheme: dark) {
  :root {
    --color-bg: #0f0f0f;
    --color-text: #f1f1f1;
    --color-muted: #9a9a9a;
    --color-border: #2a2a2a;
    --color-accent: #4da3ff;
  }
}

This automatically enables dark mode for users who prefer it—no JavaScript required.

3) Scope Dark Mode to the Front End Only

You usually do not want to affect the WordPress admin UI.
Keep your CSS in front-end styles only.

If your theme outputs shared CSS for admin and front end, scope it:

body:not(.wp-admin) {
  background-color: var(--color-bg);
}

4) Optional: Add a Manual Dark Mode Toggle (No Plugin)

Some users want to override system preferences.
You can do this with a single class and localStorage.

CSS: Class-Based Override

html.is-dark {
  --color-bg: #0f0f0f;
  --color-text: #f1f1f1;
  --color-muted: #9a9a9a;
  --color-border: #2a2a2a;
  --color-accent: #4da3ff;
}

html.is-light {
  --color-bg: #ffffff;
  --color-text: #111111;
  --color-muted: #666666;
  --color-border: #e5e5e5;
  --color-accent: #0066cc;
}

Class-based overrides should come after the media query in your CSS.

JavaScript: Apply Saved Preference Early

This script runs before rendering to avoid a flash of the wrong theme.

(function () {
  try {
    var mode = localStorage.getItem('color-scheme');
    if (mode === 'dark') {
      document.documentElement.classList.add('is-dark');
    } else if (mode === 'light') {
      document.documentElement.classList.add('is-light');
    }
  } catch (e) {}
})();

Toggle Button Logic

function toggleColorScheme() {
  var root = document.documentElement;
  var isDark = root.classList.toggle('is-dark');

  root.classList.remove(isDark ? 'is-light' : 'is-dark');
  root.classList.add(isDark ? 'is-dark' : 'is-light');

  localStorage.setItem('color-scheme', isDark ? 'dark' : 'light');
}

Attach this to a button in your header or footer.

5) Enqueue the Script Safely in WordPress

Inline scripts that affect rendering should run early.
Use wp_add_inline_script().

<?php
add_action( 'wp_enqueue_scripts', function () {
  if ( is_admin() ) {
    return;
  }

  wp_enqueue_script(
    'theme-main',
    get_template_directory_uri() . '/assets/js/main.js',
    array(),
    '1.0.0',
    true
  );

  $inline = <<<JS
(function () {
  try {
    var mode = localStorage.getItem('color-scheme');
    if (mode === 'dark') {
      document.documentElement.classList.add('is-dark');
    } else if (mode === 'light') {
      document.documentElement.classList.add('is-light');
    }
  } catch (e) {}
})();
JS;

  wp_add_inline_script( 'theme-main', $inline, 'before' );
}, 20 );

6) Images and Media in Dark Mode

Avoid inverting images globally.
Instead:

  • Use transparent PNG/SVG where possible
  • Provide alternate assets for dark backgrounds if needed
  • Avoid filter: invert() on large sections

For icons, SVG with currentColor works best.

7) Accessibility Considerations

  • Maintain sufficient contrast ratios in both modes
  • Do not rely on color alone to convey meaning
  • Ensure focus styles are visible in dark mode

Common Mistakes

  • Duplicating entire stylesheets for dark mode
  • Hardcoding colors instead of using variables
  • Applying dark mode styles to wp-admin unintentionally
  • Triggering layout shifts when toggling modes

Summary

  • Use CSS variables as the foundation of dark mode
  • Enable automatic dark mode with prefers-color-scheme
  • Add a class-based override only if you need a toggle
  • Store user preference in localStorage
  • Keep the implementation lightweight and front-end only

With this approach, you get modern dark mode support in WordPress
without plugins, without bloat, and without sacrificing maintainability.

Avatar

Written by

satoshi

I’ve been building and customizing WordPress themes for over 10 years. In my free time, you’ll probably find me enjoying a good football match.