/ โ† CSS Checker Fixes / Dark Mode
In this guide
  1. How the browser decides between light and dark
  2. Testing both modes without changing your computer
  3. The fix, step by step
  4. A complete, minimal implementation
  5. Edge cases
  6. Keeping dark mode working
  7. Questions and answers

How the browser decides between light and dark

Three separate things are involved, and dark mode problems usually come from one of them being missing or out of step with the others.

  1. The visitor's preference. The operating system or browser tells the page whether the visitor prefers a light or dark appearance. CSS reads this with the prefers-color-scheme media feature.
  2. The page's declaration. The color-scheme property, or a <meta name="color-scheme"> tag, tells the browser which schemes the page is designed for. Without it, the browser assumes the page is light only.
  3. Your colours. The actual background, text, border and accent colours you set in CSS.

The declaration matters more than people expect. When a page declares that it supports dark, the browser switches its own built-in styling to dark as well: the default page background, text inputs, select menus, checkboxes, scrollbars and the default link colours. If you set a dark background yourself but never declare the scheme, the browser still draws light form controls and scrollbars, which is where the familiar white input boxes on a dark page come from.

SymptomUsual cause
White input boxes and scrollbars on a dark pageDark colours set, but no color-scheme declaration
Dark text on a dark backgroundSome text colours hard-coded, so they were not included in the dark theme
White flash before a dark page appearsScheme only applied by a stylesheet or script that arrives after the first paint
Logo or diagram disappearsTransparent image with dark artwork, and no dark version
Toggle choice lost on the next pageChoice not stored, or applied too late in the page load
Embedded widget shows a white boxThe frame's scheme differs from the page, so the browser gives it an opaque background
Shadows and dividers vanishDark shadows on a dark surface; depth needs lighter surfaces or borders instead

Testing both modes without changing your computer

You do not need to switch your whole operating system back and forth. In Chrome or Edge, open DevTools, open the command menu (Ctrl+Shift+P, or Cmd+Shift+P on a Mac), type "Rendering", and in the Rendering panel set Emulate CSS media feature prefers-color-scheme to dark or light. Firefox has buttons for the same thing in the Inspector's rules view. Then walk through the pages that matter:

Also test on a real phone with the system dark theme switched on, and reload with the cache disabled on a throttled connection. The flash of a light page before dark styles arrive often only shows when the network is slow.

The CSS Checker flags dark-mode issues alongside its validation, render-blocking, unused-CSS, specificity, prefix and size checks, so run it before and after to see what it reports for your stylesheets.

The fix, step by step

Step 1

Test the site in both colour schemes

Use the Rendering panel in Chrome or Edge DevTools to emulate a dark preference, then go through your main templates, forms, menus and messages, writing down every unreadable element, white box and missing image. Repeat in light mode to be sure nothing regressed there. Run the CSS Checker on the same pages. This list becomes your checklist, and it shows whether you need full dark support or should declare light only for now.

Step 2

Declare which schemes the site supports

If the site supports both, add color-scheme: light dark to the :root rule and a matching meta tag in the head, so the browser knows before your stylesheet arrives. That switches form controls, scrollbars and the default canvas to match. If you are not ready to support dark properly, declare light only, which is far better than a half-dark page with unreadable parts. Either way, be explicit rather than leaving the browser to guess.

Step 3

Move hard-coded colours into custom properties

Search your stylesheets for hex, rgb and hsl values and named colours, and replace each one with a custom property named for its role, such as --surface, --text, --text-muted, --border and --accent. Aim for a short list of roles rather than one variable per colour you happen to use. Check inline colours in templates and colours set by JavaScript too, because those are the ones most often missed in a dark theme.

Step 4

Give every colour token a dark value

Redefine each role inside a prefers-color-scheme: dark media query, or use the light-dark() function to give both values in one place, which works in current versions of all major browsers. Do not simply invert colours: use a very dark grey rather than pure black for surfaces, lighten muted text so it stays readable, and reduce the saturation of bright accents, which can look harsh against dark backgrounds.

Step 5

Fix logos, images and icons

For logos and diagrams with transparent backgrounds, provide a dark-mode version and swap it with a picture element whose source has a prefers-color-scheme: dark media condition. Make inline SVG icons use currentColor so they follow the text colour automatically. Photographs normally need no change, although a very bright image can be toned down slightly in dark mode with a gentle brightness filter.

Step 6

Add a manual toggle without a flash

If you offer a switch, store the visitor's choice in localStorage and apply it with a tiny script at the very top of the head, before any stylesheet, by setting a data-theme attribute and the color-scheme on the root element. Because it runs before the first paint, the page appears in the right scheme straight away. Offer three options, light, dark and system, and fall back to the system preference when nothing is stored.

Step 7

Check contrast and focus states in both modes

Measure text contrast in each scheme with a contrast checker: WCAG 2.2 asks for at least 4.5 to 1 for normal text and 3 to 1 for large text, and 3 to 1 for the visual boundaries of form controls and focus indicators. Pay attention to muted text, placeholders, disabled buttons and links, which often fail in dark mode. Then re-run the CSS Checker and repeat your test list from step one.

A complete, minimal implementation

This setup for a fictional cafรฉ, Juniper Lane Coffee, follows the system preference by default, lets visitors override it, and avoids a light flash on dark pages.

In the head, before any stylesheet

<meta name="color-scheme" content="light dark">
<script>
  (function () {
    try {
      var t = localStorage.getItem('theme');
      if (t === 'light' || t === 'dark') {
        document.documentElement.dataset.theme = t;
        document.documentElement.style.colorScheme = t;
      }
    } catch (e) {}
  })();
</script>
<link rel="stylesheet" href="/css/site.css">

In the stylesheet

:root {
  color-scheme: light dark;
  --surface:    light-dark(#ffffff, #16181d);
  --surface-2:  light-dark(#f4f1ec, #22252c);
  --text:       light-dark(#1f2328, #e6e6e3);
  --text-muted: light-dark(#5b6168, #a9adb3);
  --border:     light-dark(#d9d4cc, #3a3e46);
  --accent:     light-dark(#8a3b12, #f0a36b);
}
:root[data-theme="light"] { color-scheme: light; }
:root[data-theme="dark"]  { color-scheme: dark; }

body   { background: var(--surface); color: var(--text); }
.card  { background: var(--surface-2); border: 1px solid var(--border); }
a      { color: var(--accent); }
input, select, textarea { accent-color: var(--accent); }

Because light-dark() picks its value from the element's used colour scheme, setting color-scheme on the root is all the toggle needs to do; every token follows. If you must support browsers older than the ones that introduced light-dark(), define the light values on :root and repeat the dark values inside a prefers-color-scheme: dark media query and a [data-theme="dark"] rule instead.

Swapping a logo

<picture>
  <source srcset="/img/logo-light-on-dark.svg" media="(prefers-color-scheme: dark)">
  <img src="/img/logo.svg" alt="Juniper Lane Coffee" width="180" height="48">
</picture>
โš ๏ธ The picture media condition follows the system preference, not your toggle. If you offer a manual switch, use an inline SVG with currentColor for the logo, or swap the image in CSS based on the data-theme attribute, so the logo follows the visitor's choice.

Edge cases

Keeping dark mode working

  1. Ban raw colour values outside the token definitions with a Stylelint rule, so new components cannot bypass the theme.
  2. Add dark-mode screenshots to your visual checks, or at least test new pages in both schemes before they go live.
  3. Keep the token list short and named by role, so a designer can adjust the whole dark theme in one place.
  4. Re-run the CSS Checker after theme or plugin updates, which often bring in hard-coded colours.
  5. Check contrast whenever brand colours change; a new accent colour that works on white may fail on dark grey.

For accessibility checks beyond colour, see the CSS Checker Guide and the other fixes in the CSS Checker Fixes hub.

Questions and answers

1. Do I have to support dark mode on my website?

No. There is no rule saying a site must offer a dark theme, and search engines do not rank pages on it. What matters is that the page is readable for everyone. A light-only site that declares itself light only is perfectly acceptable. The problems come from half-supported dark mode, where some colours change and others do not, leaving unreadable text or white boxes. If you support dark mode, support it fully and test it; otherwise declare light only.

2. What does the color-scheme property actually do?

It tells the browser which colour schemes your page is designed for. With color-scheme set to light dark, the browser adapts its own built-in styling to the visitor's preference: the default page background and text colour, form controls, scrollbars and default link colours. It also decides which value the light-dark() function returns. It does not change the colours you set yourself. Use the property on the root element and a matching meta tag in the head, so it applies before your stylesheet loads.

3. Why are my form fields white in dark mode?

Because the browser still thinks the page is light. Form controls such as text inputs, drop-downs and checkboxes are drawn with the browser's own styles, which follow the page's declared colour scheme, not the background colour you set. Add color-scheme: light dark to the root element and a color-scheme meta tag, and the controls switch to a dark appearance automatically. If you have styled the controls yourself, give them colours from your theme's custom properties as well.

4. How do I stop a white flash when the page loads in dark mode?

Make sure the browser knows the page is dark before the first paint. Put the color-scheme meta tag near the top of the head, so the default canvas is dark even before your stylesheet arrives. If you have a manual toggle, apply the stored choice with a very small script in the head, before any stylesheet, rather than in a script at the end of the page. Test on a slow connection, where the flash is most visible.

5. Should I use light-dark() or a prefers-color-scheme media query?

Both work, and they can be combined. The light-dark() function keeps each colour's light and dark values together in one declaration, and it follows the used colour scheme, which makes a manual toggle simple because you only change color-scheme on the root. It is supported in current versions of all major browsers. A media query approach works in older browsers too, but needs extra rules to support a toggle. For most new work, light-dark() on custom properties is the tidiest option.

6. What contrast do I need in dark mode?

The same as in light mode. WCAG 2.2 asks for a contrast ratio of at least 4.5 to 1 for normal body text, 3 to 1 for large text, and 3 to 1 for the visual boundaries of form controls and focus indicators. Dark themes often fail on muted grey text, placeholder text, disabled buttons and thin borders. Measure each colour pairing in both schemes with a contrast checker rather than judging by eye, because screens and eyesight vary a great deal.

7. Why does my logo disappear in dark mode?

Most likely it is a transparent image with dark artwork, so on a dark background there is nothing to see. Provide a light version of the logo for dark mode and swap it with a picture element that uses a prefers-color-scheme media condition, or use an inline SVG whose colours come from currentColor or your custom properties. The SVG approach also works with a manual theme toggle, which the picture element does not follow, since it only reads the system preference.

Check your stylesheets in both schemes

The CSS Checker flags dark-mode issues alongside CSS validation against W3C standards, render-blocking and unused rules, specificity issues, prefix gaps and stylesheet size, so you can see what to fix first.

Open CSS Checker Fixes โ†’
๐Ÿ’ฌ Got a problem?
Related Guides: All CSS Checker Fixes  ·  Fixing CSS Specificity Conflicts  ·  Fixing CSS Validation Errors  ·  CSS Checker Guide  ·  Learning Hub

About aiwebpageseo

aiwebpageseo.com is a data-driven SEO and AEO (Answer Engine Optimisation) platform providing a free suite of technical SEO tools. Rather than relying on AI-theorised assumptions, the platform analyses live URL performance, delivering objective diagnostics, page speed metrics, CLS debugging, and site crawl data alongside actionable technical tutorials.