Foundations
Themes and density
Three themesLink to the section Three themes
Light
Warning: Two people are editing this page
Dark
Warning: Two people are editing this page
High contrast
Warning: Two people are editing this page
Light is warm paper with an amber accent. Dark keeps the warmth with a lighter amber. High contrast is black with white lines and a yellow accent, for low vision and bright sunlight; text reaches 7:1 and control borders 4.5:1.
How the theme is chosenLink to the section How the theme is chosen
Set data-theme to light, dark or contrast on the root element. With no attribute, the operating system decides: prefers-contrast: more gives high contrast, then prefers-color-scheme: dark gives dark. The attribute also works on any element, which themes only that part of the page, as the previews on this site do.
<html data-theme="dark">
…
<aside data-theme="contrast">Only this panel is high contrast</aside>
</html>In React
ThemeProvider keeps the reader’s choice in local storage and sets the attribute. ThemeScript applies a stored choice before the first paint. ThemeToggle is the control, as a segmented group or a menu.
<ThemeProvider>
<ThemeToggle
label={t('theme')}
variant="menu"
options={{ light: t('light'), dark: t('dark'), contrast: t('contrast'), system: t('system') }}
/>
</ThemeProvider>With Tailwind
Components never need dark:. App code that does gets a variant that follows the attribute, for dark and high contrast alike, so an app that uses it sets the attribute explicitly.
Contrast in every themeLink to the section Contrast in every theme
Computed from the token values. The tests check these pairs and more: 4.5:1 for text and 3:1 for control borders and focus in light and dark, 7:1 and 4.5:1 in high contrast.
| Pair | Light | Dark | High contrast |
|---|---|---|---|
| Text on the pagefg on bg | 17.66:1AAA | 15.87:1AAA | 21.00:1AAA |
| Secondary text on the pagemuted on bg | 5.76:1AA | 7.15:1AAA | 16.83:1AAA |
| Secondary text on a cardmuted on surface | 5.86:1AA | 6.77:1AA | 16.83:1AAA |
| Accent text on the pageaccent-text on bg | 6.97:1AA | 10.35:1AAA | 14.88:1AAA |
| Text on a primary buttonaccent-fg on accent | 5.02:1AA | 10.00:1AAA | 14.88:1AAA |
| Success text on its tintok on ok-soft | 5.02:1AA | 7.53:1AAA | 10.96:1AAA |
| Information text on its tintinfo on info-soft | 5.77:1AA | 6.70:1AA | 9.88:1AAA |
| Warning text on its tintwarn on warn-soft | 5.56:1AA | 8.29:1AAA | 10.82:1AAA |
| Error text on its tintdanger on danger-soft | 5.75:1AA | 5.92:1AA | 7.67:1AAA |
| Text in the dark sidebarnav-fg on nav | 12.59:1AAA | 13.73:1AAA | 21.00:1AAA |
| Control border on a cardborder-strong on surface | 3.80:1AA, non-text | 3.64:1AA, non-text | 21.00:1AA, non-text |
| Focus ring on the pagefocus on bg | 4.94:1AA, non-text | 10.35:1AA, non-text | 14.88:1AA, non-text |
DensityLink to the section Density
data-density="compact" gives the smaller controls of the desktop apps. It changes the control heights only, so text stays readable, and the compact sizes still clear the 24px target size. The density switch at the top of this site sets it on the whole page.
Reader settingsLink to the section Reader settings
Two classes on the root carry the reader settings of the apps. a11y-reduce-motion stops motion like the system setting does, and a11y-strong-focus draws a 3px focus ring. AccessibilityProvider and AccessibilitySettings in the theme group manage them, with text size and underlined links.