Making dark mode defaults work without extra CSS
The prefers-color-scheme media feature has long given developers full control over how pages adapt to a user's light or dark preference. But there's a simpler layer underneath it: the color-scheme CSS property and its matching <meta name="color-scheme"> tag. These let you hand off default rendering to the browser's own user agent (UA) stylesheet — covering form controls, scroll bars, and CSS system colors — so you don't have to restyle everything yourself.
What the user agent stylesheet already does
Every browser ships a UA stylesheet that decides the default look of unstyled content. Chrome's, Firefox's, and Safari's versions agree on the basics, but they differ in important details — particularly around form controls and dark mode. WebKit's UA stylesheet, for instance, checks whether the OS is in dark mode and conditionally applies internal semantic colors like text and -apple-system-control-background instead of hard-coded values:
input,
input:matches([type="password"], [type="search"]) {
-webkit-appearance: textfield;
#if defined(HAVE_OS_DARK_MODE_SUPPORT) &&
HAVE_OS_DARK_MODE_SUPPORT
color: text;
background-color: -apple-system-control-background;
#else
background-color: white;
#endif
/* snip */
}
Those internal names are not valid CSS colors, but the CSS Color Module Level 4 spec provides standardized equivalents. The key ones are Canvas, for the background of application content or documents, and CanvasText, for the text within that content — they should always be used as a pair, never alone. UA stylesheets pick up these semantic colors and adapt them based on the OS theme:
/**
Not actual UA stylesheet code.
For illustrative purposes only.
*/
body {
color: CanvasText;
background-color: Canvas
}
Opting in with color-scheme
The CSS Color Adjustment Module Level 1 specification introduces the color-scheme property so an element can declare which color schemes it supports. The browser then negotiates that list against the user's preference to pick the final rendering, which affects everything the browser draws: form control colors, scroll bars, spellcheck underlines, and the used values of system colors.
The property accepts the following:
normal— the element is unaware of color schemes and renders with the browser's default.[ light | dark ]+— the element advertises support for the listed schemes in order of preference.
Applied to the :root element, color-scheme also adjusts the canvas surface color (the global page background), the initial value of the color property, system colors, and the viewport's scroll bars:
/*
The page supports both dark and light color schemes,
and the page author prefers dark.
*/
:root {
color-scheme: dark light;
}
Browser support for the property is now widespread — Chrome and Edge since 81, Firefox since 96, and Safari since 13.
The faster meta tag route
Honoring the CSS property means the browser has to download and parse a stylesheet first. To get the correct page background immediately — before any CSS is resolved — you can declare the same value in a <meta name="color-scheme"> element:
<!--
The page supports both dark and light color schemes,
and the page author prefers dark.
-->
<meta name="color-scheme" content="dark light">
Since the meta tag and the :root property converge on the same behavior, the meta tag is the recommended starting point: it lets the browser settle on the preferred scheme at parse time, with no network round-trip required.
Pairing with prefers-color-scheme
The two mechanisms may sound redundant, but they target different layers. color-scheme (via CSS or meta tag) dictates the browser's default appearance. prefers-color-scheme drives what you explicitly style in your own CSS. Both are usually needed together. For example, the proprietary -webkit-link color that Chrome and WebKit use for links (rgb(0,0,238)) has a contrast ratio of only 2.23:1 on black, failing WCAG AA and AAA — so a dark theme that relies solely on UA defaults can still be inaccessible until browser vendors fix those internal colors.
Consider a page that sets a <fieldset> background with inline CSS:
<head>
<meta name="color-scheme" content="dark light">
<style>
fieldset {
background-color: gainsboro;
}
@media (prefers-color-scheme: dark) {
fieldset {
background-color: darkslategray;
}
}
</style>
</head>
<body>
<p>
Lorem ipsum dolor sit amet, legere ancillae ne vis.
</p>
<form>
<fieldset>
<legend>Lorem ipsum</legend>
<button type="button">Lorem ipsum</button>
</fieldset>
</form>
</body>
With color-scheme declared as dark light, the browser handles the paragraph text and overall background contrast via the UA stylesheet alone, flipping between dark-on-light and light-on-dark based on the OS setting. Meanwhile, the <fieldset> background follows the developer's own prefers-color-scheme rule, switching between gainsboro and darkslategray:
<fieldset> element's background-color is gainsboro
as per the inlined developer stylesheet.
<fieldset> element's background-color is darkslategray
as per the inlined developer stylesheet.
Form controls like <button> show the same interplay at the system-color level. The UA stylesheet sets the button's color to ButtonText and its background and borders to ButtonFace:
background-color and the various
border-colors are set to the ButtonFace
system color.
Switch the OS theme and you can see those computed values update dynamically — border colors shift from rgba(0, 0, 0, 0.847) to rgba(255, 255, 255, 0.847) while the text color follows ButtonText:
border-top-color
and the border-bottom-color that are both set to ButtonFace
in the user agent stylesheet are now rgba(0, 0, 0, 0.847).
border-top-color
and the border-bottom-color that are both set to ButtonFace
in the user agent stylesheet are now rgba(255, 255, 255, 0.847).
Seeing it in action
A demo on Glitch exercises color-scheme across a wide range of HTML elements. It intentionally keeps the low-contrast default link color visible so you can assess the WCAG violation yourself before overriding it.
color-scheme: light.
color-scheme: dark.
Note the WCAG AA and WCAG AAA
violation
with the link colors.



