CSSDark Mode (prefers-color-scheme)

Dark Mode (prefers-color-scheme)

Most operating systems let a user choose a light or dark theme system-wide, and prefers-color-scheme is the media query that exposes that choice to CSS. Building dark mode support well is less about writing an entirely separate dark stylesheet and more about designing your color system — with custom properties — so light and dark are just two different sets of values for the same variable names.

Detecting the OS theme preference

CSS
/* Applies only when the user's OS is set to a dark theme */
@media (prefers-color-scheme: dark) {
  body {
    background: #121212;
    color: #f0f0f0;
  }
}
A clean architecture: variables + media query
Define every themeable color once as a custom property, then redefine that same set of properties inside the dark-mode media query. Every rule that consumes colors through var() automatically follows the active theme — you never touch those rules again when adding or adjusting a theme.

CSS
:root {
  --color-bg: #ffffff;
  --color-text: #1a1a1a;
  --color-muted: #595959;
  --color-border: #dddddd;
  --color-accent: #2a6df4;

  color-scheme: light;
}

@media (prefers-color-scheme: dark) {
  :root {
    --color-bg: #121212;
    --color-text: #f0f0f0;
    --color-muted: #b3b3b3;
    --color-border: #3a3a3a;
    --color-accent: #6fa1ff;

    color-scheme: dark;
  }
}

body {
  background: var(--color-bg);
  color: var(--color-text);
}

.card {
  background: var(--color-bg);
  border: 1px solid var(--color-border);
}

.card__meta {
  color: var(--color-muted);
}

a {
  color: var(--color-accent);
}
The color-scheme property

Setting color-scheme (covered fully on the color-scheme & prefers-color-scheme page) tells the browser which theme your page currently supports, so it can adapt things you don't directly style — form control chrome, scrollbars, and the text-selection color — to match. Without it, dark-mode pages often end up with a jarring white <select> or input floating in an otherwise dark UI.

CSS
:root {
  color-scheme: light dark; /* supports both; browser follows the active theme */
}
Worked example: a themed component

CSS
<div class="notice">
  <p>Your changes have been saved.</p>
</div>

:root {
  --notice-bg: #eaf4ff;
  --notice-border: #a9c9f5;
  --notice-text: #0b3d91;
  color-scheme: light;
}

@media (prefers-color-scheme: dark) {
  :root {
    --notice-bg: #10233d;
    --notice-border: #274b7a;
    --notice-text: #9cc3ff;
    color-scheme: dark;
  }
}

.notice {
  background: var(--notice-bg);
  border: 1px solid var(--notice-border);
  color: var(--notice-text);
  padding: 1rem;
  border-radius: 8px;
}
Note
`prefers-color-scheme` reflects the OS setting, not a per-site choice the user made on your page. Many sites layer a manual toggle on top — storing the user's explicit preference (e.g. in `localStorage`) and applying it via a class or `data-theme` attribute that takes priority over the media query. That combination is covered in more depth on [CSS Theming with Custom Properties](/css/css-theming).
Next
Build a full multi-theme system on [CSS Theming with Custom Properties](/css/css-theming), and make sure your color choices in both themes clear the bar on [Color Contrast & Readability](/css/color-contrast).