Skip to content

Dark mode

The color-scheme property indicates which color schemes (such as light or dark) your page supports. This informs the browser that it can automatically theme native UI elements—like scrollbars, form controls, and the default canvas background—to match your site’s design and help minimize white flashes during initial loading.

MANDATORY: To help prevent a “flash of un-themed content” (FOUC), place a <meta> tag in your <head> to ensure the browser knows which themes you support before it even starts rendering. While this <meta> tag helps to avoid FOUC by setting the initial canvas color early, it may not completely eliminate flashes in all browsers or loading conditions.

<!-- MANDATORY: Declare support for both light and dark themes -->
<meta name="color-scheme" content="light dark">

2. Apply page-wide color scheme to CSS :root or html

Section titled “2. Apply page-wide color scheme to CSS :root or html”

MANDATORY: Apply the color-scheme property to the html element or the :root pseudo-class. Browsers specifically look to the root element to determine the theme for the entire viewport—including the root scrollbars and the initial “canvas” background. If applied only to the body, these global UI surfaces may remain in light mode because the body does not control the window’s rendering context.

/* MANDATORY: Apply color-scheme to :root or html for viewport-wide theming */
:root {
/* MANDATORY: Automatically adapt native UI to user system preferences */
color-scheme: light dark;
}

You can use the light-dark() function to define color tokens that automatically adapt to different color-scheme values.

It is recommended that you also keep the raw color values in separate custom properties, which makes it easier to combine them in different ways (and makes fallback behavior easier, if needed).

For more control over the colors of built-in UI such as accent-color or scrollbar-color, authors can optionally add their own dynamic colors with use of custom properties and/or the light-dark() function. This function automatically picks the correct color based on the computed color-scheme of the element and eliminates the need for redundant media queries, but is not required for a basic implementation.

:root {
--color-brand-light: oklch(45% 0.23 270);
--color-brand-dark: oklch(85% 0.15 210);
--color-brand-text-light: white;
--color-brand-text-dark: oklch(40% 0.23 270);
--color-brand: light-dark(var(--color-brand-light), var(--color-brand-dark));
--color-brand-text: light-dark(var(--color-brand-text-light), var(--color-brand-text-dark));
/* MANDATORY: Automatically adapt native UI to user system preferences */
color-scheme: light dark;
}
button.primary {
/* These automatically adapt to color scheme */
background-color: var(--color-brand);
color: var(--color-brand-text);
}

OPTIONAL: A number of system colors are available, which also automatically adapt to the used color scheme (and other color modes, e.g. forced colors), such as canvas, canvastext, accentcolor (check support) , buttonborder etc. These are typically too limited to be useful, beyond very specific cases where you need to exactly match certain default browser UI or as fallbacks/defaults.

Even when overriding the system default, it can be useful to use the prefers-color-scheme media query to define different color pairs that take into account the colors of the browser and OS chrome around the page (or of the surrounding page, when the page is used as an iframe).

For example, use a slightly dimmer light theme when the system setting is dark, or a more contrasting dark theme when the system setting is light, so the page is not visually overpowered by the surrounding UI.

Setting color-scheme already adapts browser UI to the used color scheme, but this will use OS defaults and/or system colors that may not perfectly align with the website design. Modern browsers expose several fine-grained customization hooks for these. Do not reimplement native controls simply to customize their appearance without exhausting the customization hooks modern browsers provide.

Some browser UI (e.g. checked checkboxes or sliders) uses an accent color. This resolves to the OS setting by default, but you can use the accent-color property to set it to a color that better aligns with the page, such as the page’s brand color.

html {
accent-color: light-dark(var(--color-accent-light), var(--color-accent-dark));
}

Baseline: accent-color

You can use scrollbar-color together with light-dark() to set custom scrollbar colors that adapt to the color scheme used.

:root {
--color-scrollbar-track: light-dark(#eee, #222);
/* Example values: the thumb needs at least 3:1 contrast against the track */
--color-scrollbar-thumb: light-dark(#6b6b6b, #8a8a8a);
scrollbar-color: var(--color-scrollbar-thumb) var(--color-scrollbar-track);
}

Baseline: scrollbar-color

Most browser UI exposes pseudo-elements to fully customize its appearance, such as:

  • ::placeholder
  • ::spelling-error
  • ::grammar-error
  • ::selection
  • ::search-text
  • ::target-text
  • ::file-selector-button

You can use light-dark() colors on any of these to apply colors that adapt to the used color scheme.

For CSS images, light-dark() can also be used to provide different image values.

IMPORTANT: Both values in light-dark() must be the same type. You cannot combine a color with an image. If one of the two must be an image, use image(<color>) to wrap the color value.

Most color scheme branching should be done in CSS, which automatically adapts to changes.

Any JS reading matchMedia("(prefers-color-scheme: dark)").matches MUST also handle its change event as the system preference can change at any time.

OPTIONAL: Implementing a color-scheme toggle

Section titled “OPTIONAL: Implementing a color-scheme toggle”

If you want to allow users to manually override their system default for this site, you can implement a dark mode toggle. Most sites do not need a persistent toggle in the site UI. Simply defaulting to the system color scheme is usually enough, optionally with a setting in a separate settings surface. See dark-mode-toggle (guide not yet published on this site) for best practices around implementing a dark mode toggle (persistent or not).

You can override the global theme for specific elements by setting color-scheme on them.

For more information and gotchas, see component-specific-light-dark-theme (guide not yet published on this site).

Baseline: color-scheme

Baseline: color-scheme

Baseline: light-dark

Baseline: light-dark-image

Baseline: image-function

Baseline: scrollbar-color

Baseline: accent-color

Compatibility notes: Badges like Baseline: … mark web features referenced by this guide. Baseline status describes availability across the core browser set — it does not mean every browser or older version supports the feature. Feature data as published upstream, synced 2026-11-12.

Open the demo standalone · Demo source on GitHub

Counter-example (negative demo) — what this guide tells you to avoid.


Source: GoogleChrome/modern-web-guidance-src (Apache-2.0), upstream commit 179dbc6, synced 2026-11-12. Local changes: macros expanded to links/badges, first heading de-duplicated, demo and this footer added. This is an independent, unofficial site — not a Google publication.