The Future of CSS: Easy Light-Dark Mode Color Switching with light-dark()

By admin
To change a color based on whether

Light Mode or
PRODUCT

Dark Mode used, you’d typically use a prefers-color-scheme

Media Query
ORG

. To make things easier, CSS now comes with a utility function named light-dark() . The function accepts

two
CARDINAL

color values as its arguments. Based on which color scheme you are actively using, it will output the

first
ORDINAL

or the

second
ORDINAL

argument.

~

# Responding to Light or Dark Mode

To change a color value – or any other value for that matter – based on

Light Mode or
PRODUCT

Dark Mode being used, you’d typically use a prefers-color-scheme

Media Query
ORG

to change the value of a Custom Property:

:root { –text-color: #

333
MONEY

; /* Value for Light Mode */ } @media (prefers-color-scheme: dark) { :root { –text-color: #ccc; /* Value for Dark Mode */ } }

When implementing Dark Mode, you typically end up with a bunch of duplicated CSS variables that set the values for each mode. The rest of your CSS then uses these custom properties for the actual declarations.

body { color: var(–text-color); }

~

# Responding to Light or Dark Mode with light-dark()

A new addition to the CSS Color Module Level 5 Specification is the light-dark() function. The function accepts

two
CARDINAL

color values as its arguments. Based on which color scheme you are actively using, it will output the

first
ORDINAL

or the

second
ORDINAL

color argument.

light-dark(<color>, <color>);

As per spec:

This function computes to the computed value of the

first
ORDINAL

color, if the used color scheme is light or unknown, or to the computed value of the

second
ORDINAL

color, if the used color scheme is dark .

The used color scheme is not only based on the user’s

Light/Dark Mode
WORK_OF_ART

setting, but also on the value of the color-scheme property. This similar to how System Colors get computed.

The color-scheme property allows an element to indicate which color schemes it is designed to be rendered with. These values are negotiated with the user’s preferences, resulting in a used color scheme […].

That means, for light-dark() to work, you must also include a color-scheme declaration.

:root { color-scheme: light dark; } :root { –text-color: light-dark(#333, #ccc); /* In Light Mode = return

1st
ORDINAL

value. In Dark Mode = return

2nd
ORDINAL

value. */ }

Because color-scheme is taken into account, that also means that you can override its value per element, to force it into a certain mode:

.dark { color-scheme: dark; /* light-dark() on this element and its children will always return dark */ }

🤔 If this light-dark() seems familiar: Chromium internally sports a -internal-light-dark() which I wrote about before. Based on this functionality, the proposal was made within

the CSS Working Group
ORG

to expose a similar function to authors. The result is light-dark() . Unlike -internal-light-dark() which is for any type of value, light-dark() can only be used for colors.

~

# What about other non- <color> values and responding to other color schemes?

A common type of feedback on light-dark() I get is that it is fairly limited in what it can do: it can only do light/dark and only works with <color> values. That’s correct and is also very much intentional, because it is an intermediary step towards a final solution.

As proposed in the CSS

Working Group
ORG

issue, the end goal is to have a function (tentatively) named schemed-value() in the future. That function can:

Respond to any value of color-scheme .

. Return more than <color> values

It could look something like this:

:root { color-scheme: dark light custom; } body { color: schemed-value(light hotpink, dark lime, custom rebeccapurple); }

But, for now, we “only” have light-dark() and I personally think that’s fine, as it rhymes with

today
DATE

’s reality of what browsers can do:

Narrowing things down in feature scope – from the very broad schemed-value() to the slimmed down light-dark() – allowed the function as it is to be defined

today
DATE

, instead of putting the whole thing on the long track.

The name and syntax of light-dark() is very memorable, easy to use, and – most importantly – offers a solution to a common use-case authors are having

today
DATE

.

💡 When schemed-value() ever becomes a thing, light-dark() would become syntactic sugar for it: light-dark(<color>, <color>); = schemed-value(light <color>, dark <color>);

~

# Browser Support

💡 Although this post was originally published in

October 2023
DATE

, the section below is constantly being updated. Last update:

October 09, 2023
DATE

.

Here is an up-to-date list of browser support for CSS light-dark() :

Chromium (Blink) ❌ No support

Firefox
PERSON

(

Gecko
ORG

) ✅ Supported in Firefox 120.

Safari
PERSON

(WebKit) ❌ No support

The pen embedded below will indicate if the browser you are currently using supports CSS light-dark() or not:

See the Pen

CSS light-dark() Support test by

Bramus
PERSON

(

@bramus
ORG

)

on CodePen.

To stay up-to-date regarding browser support, you can follow these tracking issues:

~

If your browser supports light-dark() , the demo below will show a few <div> s labeled .auto that respond to

Light/Dark
PRODUCT

mode being toggled. The <div> s with the class .light or .dark are forced into their proper mode.

See the Pen

light-dark() Demo by

Bramus
PRODUCT

(

@bramus
ORG

)

on CodePen.

~

# Spread the word

To help spread the contents of this post, feel free to retweet its announcement tweet post / toot post :

To change a color based on

Light Mode or
PRODUCT

Dark Mode, you’d typically use a `prefers-color-scheme`

Media Query
WORK_OF_ART

. To make things easier, CSS now comes with a `light-dark()` utility function. Read https://t.co/uzcTGPo8dY to get to know the details. Browser Support: Firefox

120
CARDINAL

. pic.twitter.com/1rmGkKy2yl —

Bramus
PRODUCT

(

@bramus
ORG

)

October 9
DATE

, 2023

~