CSS color-scheme-dependent colors with light-dark()

System colors have the ability to react to the current used color-scheme value. The light-dark() function exposes the same capability to authors.

System Colors in CSS

In CSS you can use many colors from one of the many color spaces. For example, you can use named colors, hex colors, color functions linked to a specific color space, the more generic color() function.

For example, the named color cornflowerblue can also be represented as #6495ED, or hsl(218.54deg 79.19% 66.08%), or color(display-p3 0.43 0.58 0.9).

In addition to these various names and formats, CSS includes colors described as system colors, specified in CSS Color Module Level 4. These system colors are colors defined by the browser and are represented by a keyword.

For example, the system color Canvas–not to be confused with the <canvas> element–represents the "background of application content or documents". It pairs nicely with, and is also meant to be used in conjunction with, CanvasText which represents the "text in application content or documents".

In CSS, you use them as follows:

body {
  color: CanvasText;
  background-color: Canvas;
}

By default, CanvasText results in a color close to black and Canvas is a color that's close to white. The actual implementation depends on the browser. For example, CanvasText in Chrome results in #121212 whereas Safari has it specified as the slightly lighter #1e1e1e.

A hidden power of these system colors is that they can respond to the computed value of the color-scheme property. For example, the values for CanvasText and Canvas get flipped around when the used color-scheme is dark.

:root {
  color-scheme: dark;
}

body {
  color: CanvasText;
  background-color: Canvas;
}

In the following demo, you can change the value of color-scheme set on :root and see how the page responds.

  • When set to light dark, it indicates that the element supports both light and dark mode. The choice of which value is used depends on the value of the prefers-color-scheme media condition.
  • When set to light, it indicates that the element supports a light color scheme.
  • When set to dark, it indicates that the element supports a dark color scheme.
A page that allows you to change the value for color-scheme. Upon changing, the colors of the page change when going from light to dark or vice versa, even though the declarations on the body element remain the same.

Introducing light-dark()

Browser Support

  • Chrome: 123.
  • Edge: 123.
  • Firefox: 120.
  • Safari: 17.5.

Source

Up until now, reacting to the used color-scheme value was something that was reserved for the system colors. Thanks to light-dark(), specified in CSS Color Module Level 5, you now also have the same capability.

light-dark() is a function that accepts two arguments, both of which must be a <color>. One of both is picked depending on the used color scheme.

  • If the used color scheme is light or unknown then the computed value of the first value gets returned.
  • If the used color scheme is dark then the computed value of the second color is returned.

The result of light-dark() is a <color>. It can be used in CSS anywhere a <color> is accepted. For example in the color and background-color properties, but also in a function like linear-gradient().

In the following example, the used background-color is #333 in dark mode, or #ccc in light mode (or an unknown mode).

:root {
  color-scheme: light dark;
}

body {
  background-color: light-dark(#ccc, #333);
}

Note that for light-dark() to work correctly, you need to specify a color-scheme. Since that property inherits, you typically set it on :root but if you want you can choose to set it on a specific element.

Practical application

In the following example, a few custom properties represent colors on the page. To cater for dark mode, the values of those custom properties get overwritten by a different value in a prefers-color-scheme media condition.

:root {
  --primary-color: #333;
  --primary-background: #e4e4e4;
  --highlight-color: hotpink;
}

@media (prefers-color-scheme: dark) {
  :root {
    --primary-color: #fafafa;
    --primary-background: #121212;
    --highlight-color: lime;
  }
}
A page that responds to light or dark mode via prefers-color-scheme.
The color values are changed in CSS using a media query.

Thanks to light-dark(), this code can be simplified. Because color-scheme is set to light dark on :root, the values of these colors automatically change when changing your OS from light to dark mode and vice versa.

:root {
  color-scheme: light dark;
  --primary-color: light-dark(#333, #fafafa);
  --primary-background: light-dark(#e4e4e4, #121212);
  --highlight-color: light-dark(hotpink, lime);
}
A page that responds to light or dark mode using prefers-color-scheme.
The color values are changed using light-dark().

As an added bonus, it's possible to force a certain subtree of the DOM to use only light or dark mode by setting color-scheme to either dark or light. In the following example, this is applied to :root.

A page that responds to light or dark mode using prefers-color-scheme.
The color values are changed using light-dark().
Using one of the options you can force a light or dark mode. This is achieved by manipulating the color-scheme value.