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.
Implementation
Section titled “Implementation”1. Declare supported schemes in HTML
Section titled “1. Declare supported schemes in HTML”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;}3. Define light and dark color tokens
Section titled “3. Define light and dark color tokens”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.
OPTIONAL: Tailor color pairs to context
Section titled “OPTIONAL: Tailor color pairs to context”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.
Fine-grained browser UI customization
Section titled “Fine-grained browser UI customization”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.
Setting the accent color
Section titled “Setting the accent color”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
Scrollbar colors
Section titled “Scrollbar colors”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
Further customization
Section titled “Further customization”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.
4. Varying images for light and dark mode
Section titled “4. Varying images for light and dark mode”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.
JS-based theme detection
Section titled “JS-based theme detection”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).
OPTIONAL: Component-specific overrides
Section titled “OPTIONAL: Component-specific overrides”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).
Known issues to be aware of
Section titled “Known issues to be aware of”Baseline: color-scheme
Fallback strategies
Section titled “Fallback strategies”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.
Live demo
Section titled “Live demo”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.