Skip to content
This repository has been archived by the owner on Mar 14, 2024. It is now read-only.

Add color-scheme article #2481

Merged
merged 28 commits into from
Apr 8, 2020
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
839e9d0
Start work on color-scheme article
tomayac Mar 27, 2020
65de764
No smart quotes
tomayac Mar 27, 2020
10c07ca
More content
tomayac Mar 30, 2020
7563dd6
Review draft
tomayac Mar 31, 2020
6e65fce
Apply suggestions from code review
tomayac Apr 1, 2020
4676afe
Update src/site/content/en/blog/color-scheme/index.md
tomayac Apr 1, 2020
a66cf25
Update src/site/content/en/blog/color-scheme/index.md
tomayac Apr 1, 2020
5aa6dd8
Update src/site/content/en/blog/color-scheme/index.md
tomayac Apr 1, 2020
ffd9527
Update src/site/content/en/blog/color-scheme/index.md
tomayac Apr 1, 2020
9e609c4
Update src/site/content/en/blog/color-scheme/index.md
tomayac Apr 1, 2020
c68b066
Update src/site/content/en/blog/color-scheme/index.md
tomayac Apr 1, 2020
e8921e6
Update src/site/content/en/blog/color-scheme/index.md
tomayac Apr 1, 2020
c2be2ac
Update src/site/content/en/blog/color-scheme/index.md
tomayac Apr 1, 2020
a36ba3e
Update src/site/content/en/blog/color-scheme/index.md
tomayac Apr 1, 2020
c9b2391
Update index.md
tomayac Apr 1, 2020
167fb5f
Update src/site/content/en/blog/color-scheme/index.md
tomayac Apr 1, 2020
fa327f6
Remove embedded demo
tomayac Apr 1, 2020
bff3565
Add bug info
tomayac Apr 1, 2020
7a1af8f
Add HTML issue
tomayac Apr 1, 2020
9a41955
Apply suggestions from code review
tomayac Apr 1, 2020
282b03a
Update src/site/content/en/blog/color-scheme/index.md
tomayac Apr 1, 2020
6b870f8
Update src/site/content/en/blog/color-scheme/index.md
tomayac Apr 1, 2020
821d6af
Update src/site/content/en/blog/color-scheme/index.md
tomayac Apr 1, 2020
4b4b32c
Optimize images
Apr 8, 2020
cdd2e3d
Fix content styling
Apr 8, 2020
b2cb325
Fix figure stylings
Apr 8, 2020
4881893
More image styling fixes
Apr 8, 2020
f26692f
Add alt descriptions to images
Apr 8, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/site/content/en/blog/color-scheme/hero.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
361 changes: 361 additions & 0 deletions src/site/content/en/blog/color-scheme/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,361 @@
---
title: Improved dark mode with the `color-scheme` CSS property and the corresponding meta tag
subhead: |
The `color-scheme` CSS property and the corresponding meta tag
tomayac marked this conversation as resolved.
Show resolved Hide resolved
allow developers to opt their pages in to the theme-specific defaults of the user-agent stylesheet.
authors:
- thomassteiner
date: 2020-03-27
hero: hero.jpg
alt: Pigeons on a wall with a sharp black and white contrast in the background.
description: |
The color-scheme CSS property and the corresponding meta tag
tomayac marked this conversation as resolved.
Show resolved Hide resolved
allow developers to opt their pages in to theme-specific defaults of the user-agent stylesheet,
such as, for example, form controls, scroll bars, as well as CSS system colors.
At the same time, this feature prevents browsers from applying any transformations on their own.
tags:
- post # post is a required tag for the article to show up in the blog.
- css
- dark-mode
- dark-theme
- prefers-color-scheme
- color-scheme
draft: true
---
## Background

### The `prefers-color-scheme` user preference media feature
tomayac marked this conversation as resolved.
Show resolved Hide resolved

The
[`prefers-color-scheme`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme)
user preference media feature gives developers full control over their pages' appearances.
If you're unfamiliar with it read my article
tomayac marked this conversation as resolved.
Show resolved Hide resolved
[`prefers-color-scheme`: Hello darkness, my old friend](/prefers-color-scheme/),
where I documented everything I know about creating amazing dark mode experiences.

One puzzle piece that was only mentioned briefly in the article is
the `color-scheme` CSS property and the corresponding meta tag of the same name.
They both make your life as a developer easier
by allowing you to opt your page in to theme-specific defaults of the user-agent stylesheet,
such as, for example, form controls, scroll bars, as well as CSS system colors.
At the same time, this feature prevents browsers from applying any transformations on their own.

### The user-agent stylesheet

Before I continue, let me briefly describe what a user-agent stylesheet is.
Most of the times, you can think of the word *user agent* (UA)
tomayac marked this conversation as resolved.
Show resolved Hide resolved
as a fancy way to say *browser*.
The UA stylesheet determines the default look and feel of a page.
As the name suggests, a UA stylesheet is something that depends on the UA in question.
You can have a look at
[Chrome's](https://chromium.googlesource.com/chromium/blink/+/master/Source/core/css/html.css)
(and Chromium's) UA stylesheet and compare it to
[Firefox's](https://dxr.mozilla.org/mozilla-central/source/layout/style/res/html.css) or
[Safari's](https://trac.webkit.org/browser/trunk/Source/WebCore/css/html.css) (and WebKit's).
Typically, UA stylesheets agree on the majority of things.
For example, they all make links blue, general text black, and background color white,
but there are also important (and sometimes annoying) differences,
for instance, how they style form controls.

Have a closer look at
[WebKit's UA stylesheet](https://trac.webkit.org/browser/trunk/Source/WebCore/css/html.css)
and what it does regarding dark mode.
(Do a full text search for "dark" in the stylesheet.)
The default provided by the stylesheet changes based on whether dark mode is on or off.
To illustrate this, here is one such CSS rule using the
[`:matches`](https://css-tricks.com/almanac/selectors/m/matches/)
pseudo class and WebKit-internal variables like `-apple-system-control-background`,
as well as the WebKit-internal preprocessor directive `#if defined`:

```css
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 */
}
```

You will notice some non-standard values for the `color` and `background-color` properties above.
Neither `text` nor `-apple-system-control-background` are valid CSS colors.
They are WebKit-internal *semantic* colors.

Turns out, CSS has standardized semantic system colors.
They are specified in
[CSS Color Module Level 4](https://drafts.csswg.org/css-color/#css-system-colors).
For example,
[`Canvas`](https://drafts.csswg.org/css-color/#valdef-system-color-canvas)
(not to be confused with the `<canvas>` tag)
is for the background of application content or documents,
whereas
[`CanvasText`](https://drafts.csswg.org/css-color/#valdef-system-color-canvastext)
is for text in application content or documents.
The two go together and should not be used in isolation.

UA stylesheets can use their own proprietary, or the standardized semantic system colors,
tomayac marked this conversation as resolved.
Show resolved Hide resolved
to determine how HTML elements should be rendered by default.
If the operating system is set to dark mode or uses a dark theme,
`CanvasText` (or `text` respectively) would be conditionally set to white,
tomayac marked this conversation as resolved.
Show resolved Hide resolved
and `Canvas` (or `-apple-system-control-background`) would be set to black.
The UA stylesheet then assigns the following CSS only once, and covers both light and dark mode.

```css
/**
Not actual UA stylesheet code.
For illustrative purposes only.
*/
body {
color: CanvasText;
background-color: Canvas
}
```

## The `color-scheme` CSS property
tomayac marked this conversation as resolved.
Show resolved Hide resolved

The [CSS Color Adjustment Module Level&nbsp;1](https://drafts.csswg.org/css-color-adjust/)
specification introduces a model and controls
over automatic color adjustment by the user-agent to handle user preferences,
such as dark mode, contrast adjustment, or specific desired color schemes.
tomayac marked this conversation as resolved.
Show resolved Hide resolved

The [`color-scheme`](https://drafts.csswg.org/css-color-adjust/#color-scheme-prop)
property defined therein allows an element to indicate
which color schemes it is comfortable being rendered with.
These values are negotiated with the user's preferences, resulting in a chosen color scheme
that affects user interface (UI) things such as the default colors of form controls
and scroll bars, as well as the used values of the CSS system colors.
The following values are currently supported:

- *`normal`* Indicates that the element is not aware of color schemes at all,
and so the element should be rendered with the browser's default color scheme.

- *`[ light | dark ]+`* Indicates that the element is aware of and can handle
the listed color schemes, and expresses an ordered preference between them.

{% Aside 'note' %}
Providing both keywords indicates that the first scheme is preferred (by the author),
but the second is also acceptable if the user prefers it instead.
{% endAside %}

In this list, `light` represents a light color scheme,
with light background colors and dark foreground colors,
whereas `dark` represents the opposite, with dark background colors and light foreground colors.

{% Aside 'warning' %}
Per the specification, the allowed additional value `light only` indicates that the element
must be rendered with a light color scheme if possible,
even if the user's preference is for a different color scheme.
Authors *should not* use this value, and should instead ensure their page renders well
with whatever color scheme the user prefers.
{% endAside %}

For all elements, rendering with a color scheme should cause the colors used
in all browser-provided UI for the element to match with the intent of the color scheme.
Examples are scroll bars, spellcheck underlines, form controls, etc.

{% Aside 'note' %}
The `color-scheme` CSS property can be used on both the `:root` level,
as well as on an individual per-element level.
{% endAside %}

On the `:root` element, rendering with a color scheme
additionally must affect the surface color of the canvas (that is, the global background color),
the initial value of the `color` property, and the used values of the system colors,
and should also affect the viewport's scroll bars.

```css
/*
The page supports both dark and light color schemes,
and the page author prefers dark.
*/
:root {
color-scheme: dark light;
}
```

## The `color-scheme` meta tag

Honoring the `color-scheme` CSS property requires the CSS to be first downloaded
(if it is referenced via `<link rel="stylesheet">`) and to be parsed.
To aid user-agents in rendering the page background with the desired color scheme *immediately*,
a `color-scheme` value can also be provided in a
[`<meta name="color-scheme">`](https://drafts.csswg.org/css-color-adjust/#color-scheme-meta)
element.

```html
<!--
The page supports both dark and light color schemes,
and the page author prefers dark.
-->
<meta name="color-scheme" content="dark light">
```

## Using `color-scheme` in practice
tomayac marked this conversation as resolved.
Show resolved Hide resolved

Since both the meta tag and the CSS property (if applied to the `:root` element)
eventually result in the same behavior, I always recommend specifying the color scheme
via the meta tag, so the browser can adopt to the preferred scheme faster.

{% Aside 'warning' %}
tomayac marked this conversation as resolved.
Show resolved Hide resolved
While for absolute baseline pages no additional CSS rules are necessary,
in the general case you should always combine `color-scheme` with `prefers-color-scheme`.

For example, the proprietary WebKit CSS color `-webkit-link`, used by WebKit and Chrome
for the classic link blue `rgb(0,0,238)`,
has an insufficient contrast ratio of 2.23:1 on a black background and
[fails](https://webaim.org/resources/contrastchecker/?fcolor=0000EE&bcolor=000000)
both the WCAG&nbsp;AA as well as the WCAG&nbsp;AAA
[requirements](https://www.w3.org/WAI/WCAG21/Understanding/conformance#levels).
{% endAside %}

## Interplay with `prefers-color-scheme`
tomayac marked this conversation as resolved.
Show resolved Hide resolved

The interplay of the `color-scheme` CSS property and the corresponding meta tag
with the `prefers-color-scheme` user preference media feature may seem confusing at first.
In fact, they play together really well.
The most important thing to understand is that `color-scheme`
exclusively determines the default appearance,
whereas `prefers-color-scheme` determines the stylable appearance.
To make this clearer, assume the following page:

```html
<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>
```

The inline CSS code on the page
sets the `<fieldset>` element's `background-color` to `gainsboro` in the general case,
and to `darkslategray` if the user prefers a `dark` color scheme
according to the `prefers-color-scheme` user preference media feature.

Via the `<meta name="color-scheme" content="dark light">` element,
the page tells the browser that it supports a dark and a light theme,
with a preference for a dark theme.

Depending on whether the operating system is set to dark or light mode,
the whole page appears light on dark, or vice versa, based on the user-agent stylesheet.
There is *no* additional developer-provided CSS involved to change the paragraph text
or the background color of the page.

Note how the `<fieldset>` element's `background-color` changes
based on whether dark mode is enabled, following the rules
in the developer-provided inline stylesheet on the page.
It is either `gainsboro` or `darkslategray`.

<figure>
<img src="light-styles.png" width="3440" height=" 1386">
<figcaption>
<strong>Light mode:</strong> Styles specified by the developer and the user-agent.
The text is black and the background is white as per the user-agent stylesheet.
The <code>&lt;fieldset&gt;</code> element's <code>background-color</code> is <code>gainsboro</code>
as per the inlined developer stylesheet.
</figcaption>
</figure>

<figure>
<img src="dark-styles.png" width="3440" height=" 1386">
<figcaption>
<strong>Dark mode:</strong> Styles specified by the developer and the user-agent.
The text is white and the background is black as per the user-agent stylesheet.
The <code>&lt;fieldset&gt;</code> element's <code>background-color</code> is <code>darkslategray</code>
as per the inlined developer stylesheet.
</figcaption>
</figure>

The `<button>` element's appearance is controlled by the user-agent stylesheet.
Its `color` is set to the
[`ButtonText`](https://drafts.csswg.org/css-color/#valdef-system-color-buttontext)
system color, and its `background-color` and the four `border-color`s are set to the system color
[`ButtonFace`](https://drafts.csswg.org/css-color/#valdef-system-color-buttonface)

<figure>
<img src="light-buttonface.png" width="3440" height=" 1386">
<figcaption>
<strong>Light mode:</strong> The <code>background-color</code> and the various
<code>border-color</code>s are set to the <a href="https://drafts.csswg.org/css-color/#valdef-system-color-buttonface">ButtonFace</a>
system color.
</figcaption>
</figure>

Now note how the `<button>` element's `border-color` changes.
The *computed* value for the `border-top-color` and the `border-bottom-color`
switches from `rgba(0, 0, 0, 0.847)` (blackish) to `rgba(255, 255, 255, 0.847)` (whitish),
since the user-agent updates `ButtonFace` dynamically based on the color scheme.
The same applies for the `<button>` element's `color`
that is set to the corresponding system color `ButtonText`.

<figure>
<img src="light-computed.png" width="3440" height=" 1386">
<figcaption>
<strong>Light mode:</strong> The computed values of the <code>border-top-color</code>
and the <code>border-bottom-color</code> that are both set to <code>ButtonFace</code>
in the user-agent stylesheet are now <code>rgba(0, 0, 0, 0.847)</code>.
</figcaption>
</figure>

<figure>
<img src="dark-computed.png" width="3440" height=" 1386">
<figcaption>
<strong>Dark mode:</strong> The computed values of the <code>border-top-color</code>
and the <code>border-bottom-color</code> that are both set to <code>ButtonFace</code>
in the user-agent stylesheet are now <code>rgba(255, 255, 255, 0.847)</code>.
</figcaption>
</figure>

## Demo

You can see the effects of `color-scheme` applied to a large number of HTML elements
in the demo embedded below, or
[directly on Glitch](https://color-scheme-demo.glitch.me/).
Remember that you need to switch your operating system to dark mode to enjoy this demo.

The demo on purpose shows the WCAG&nbsp;AA and WCAG&nbsp;AAA
[violation](https://webaim.org/resources/contrastchecker/?fcolor=0000EE&bcolor=000000)
with the link colors that was mentioned in the
[warning above](#using-color-scheme-in-practice).
tomayac marked this conversation as resolved.
Show resolved Hide resolved

<div class="glitch-embed-wrap" style="height: 420px; width: 100%;">
<iframe
src="https://glitch.com/embed/#!/embed/color-scheme-demo?path=index.html&previewSize=100"
title="color-scheme-demo on Glitch"
allow="geolocation; microphone; camera; midi; vr; encrypted-media"
style="height: 100%; width: 100%; border: 0;"
loading="lazy">
</iframe>
</div>

## Acknowledgements

The `color-scheme` CSS property and the corresponding meta tag were implemented by
[Rune Lillesveen](https://github.com/lilles).
Rune is also a co-editor of the CSS Color Adjustment Module Level&nbsp;1 specification.
Hero image by
[Philippe Leone](https://unsplash.com/@philinit?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)
on [Unsplash](https://unsplash.com/photos/dbFfEBOCrkU).
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 3 additions & 7 deletions src/site/content/en/blog/prefers-color-scheme/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -352,22 +352,17 @@ In the code sample above, you will probably have noticed a property
[`color-scheme`](https://drafts.csswg.org/css-color-adjust-1/#propdef-color-scheme)
with the space-separated value `light dark`.

{% Aside 'warning' %}
The `color-scheme` property is still [in development](https://crbug.com/925935)
and it might not work as advertised, full support in Chrome will come later this year.
{% endAside %}

This tells the browser which color themes my app supports
and allows it to activate special variants of the user agent stylesheet,
which is useful to, for example, let the browser render form fields
with a dark background and light text, adjust the scrollbars,
with a dark background and light text, adjust the scroll bars,
or to enable a theme-aware highlight color.
The exact details of `color-scheme` are specified in
[CSS Color Adjustment Module Level&nbsp;1](https://drafts.csswg.org/css-color-adjust-1/).

{% Aside 'note' %}
🌒 Read up more on
[what `color-scheme` actually does](https://medium.com/dev-channel/what-does-dark-modes-supported-color-schemes-actually-do-69c2eacdfa1d).
[what `color-scheme` actually does](color-scheme).
{% endAside %}

Everything else is then just a matter of defining CSS variables
Expand Down Expand Up @@ -790,6 +785,7 @@ Resources for the `prefers-color-scheme` media query:
- [Media Queries Level&nbsp;5 spec](https://drafts.csswg.org/mediaqueries-5/#prefers-color-scheme)

Resources for the `color-scheme` meta tag and CSS property:
- [The `color-scheme` CSS property and meta tag](/color-scheme)
- [Chrome Platform Status page](https://chromestatus.com/feature/5330651267989504)
- [Chromium bug](http://crbug.com/925935)
- [CSS Color Adjustment Module Level&nbsp;1 spec](https://drafts.csswg.org/css-color-adjust-1/)
Expand Down