CSScolor-scheme & prefers-color-scheme

color-scheme

color-scheme tells the browser and the operating system which colour schemes your site supports: light, dark, or both. When you declare support for both, the browser adjusts form elements, scrollbars, and default colours to match the user's system preference. Combined with media queries (prefers-color-scheme), it provides the foundation for building modern dark mode support.

The color-scheme property

CSS
/* Declare support for light mode only */
color-scheme: light;

/* Declare support for dark mode only */
color-scheme: dark;

/* Support both — browser/OS chooses based on user preference */
color-scheme: light dark;

/* Support both, with dark as preferred default */
color-scheme: dark light;

/* Global setting — usually on root element */
:root {
  color-scheme: light dark;
}
What color-scheme controls

When you declare color-scheme, the browser automatically styles several things to match:

  • Form inputs, buttons, selects — background and text colours adjust
  • Scrollbars — colours change to match the scheme
  • Text input cursor — changes to be visible against the background
  • Links — default colours may adjust (though custom colours take precedence)
  • System default colours — the browser uses appropriate colours for the scheme

CSS
/* Without color-scheme — form inputs might not adapt */
body {
  /* no color-scheme declaration */
  background: #1a1a1a;
  color: #ffffff;
}

/* Form input might have dark text on dark background — unreadable! */
input {
  /* browser defaults may not adapt */
}

/* With color-scheme — form inputs automatically adapt */
:root {
  color-scheme: light dark;  /* browser knows to adapt form elements */
}

body {
  background: #1a1a1a;
  color: #ffffff;
}

/* Form input background and text automatically adjust for readability */
input {
  /* browser provides appropriate scheme-aware defaults */
}
Detecting user preference: prefers-color-scheme

Use the prefers-color-scheme media query to detect whether the user prefers light or dark mode (from their OS settings).

CSS
/* Light mode — default, usually */
:root {
  --text-color: #000000;
  --bg-color: #ffffff;
  color-scheme: light;
}

/* Detect dark mode preference */
@media (prefers-color-scheme: dark) {
  :root {
    color-scheme: dark;
    --text-color: #ffffff;
    --bg-color: #1a1a1a;
  }
}

body {
  color: var(--text-color);
  background-color: var(--bg-color);
}
Building a complete dark mode

CSS
/* Define colour palette in light mode */
:root {
  --text-primary: #000000;
  --text-secondary: #666666;
  --bg-primary: #ffffff;
  --bg-secondary: #f5f5f5;
  --border-color: #dddddd;
  --accent-color: #0066cc;

  color-scheme: light;
}

/* Override for dark mode */
@media (prefers-color-scheme: dark) {
  :root {
    --text-primary: #ffffff;
    --text-secondary: #aaaaaa;
    --bg-primary: #1a1a1a;
    --bg-secondary: #2a2a2a;
    --border-color: #444444;
    --accent-color: #4da6ff;

    color-scheme: dark;
  }
}

/* Use variables throughout — they adapt automatically */
body {
  color: var(--text-primary);
  background-color: var(--bg-primary);
}

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

.button {
  background-color: var(--accent-color);
  color: var(--text-primary);
}

input {
  background-color: var(--bg-secondary);
  color: var(--text-primary);
  border: 1px solid var(--border-color);
}
Manual dark mode toggle (without OS preference)

You can also support manual theme switching using a class or data attribute, combining it with the OS preference as a fallback.

CSS
/* Light mode — default */
:root {
  --text-color: #000000;
  --bg-color: #ffffff;
  color-scheme: light;
}

/* Manual dark mode class */
[data-theme="dark"] {
  --text-color: #ffffff;
  --bg-color: #1a1a1a;
  color-scheme: dark;
}

/* OS prefers dark, but no manual override */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --text-color: #ffffff;
    --bg-color: #1a1a1a;
    color-scheme: dark;
  }
}

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

JS
// Toggle dark mode manually
const toggle = document.querySelector('.theme-toggle');
toggle.addEventListener('click', () => {
  const current = document.documentElement.getAttribute('data-theme');
  const next = current === 'dark' ? 'light' : 'dark';
  document.documentElement.setAttribute('data-theme', next);

  // Persist to localStorage
  localStorage.setItem('theme', next);
});

// On page load, restore saved preference
const saved = localStorage.getItem('theme');
if (saved) {
  document.documentElement.setAttribute('data-theme', saved);
}
Form elements and color-scheme

When you declare color-scheme, form inputs automatically get readable default colours. This is especially useful for <input>, <select>, <textarea>, and system buttons.

CSS
/* Before color-scheme — form inputs may look wrong in dark backgrounds */
body.dark-mode {
  background: #1a1a1a;
  color: #ffffff;
  /* form inputs still have light backgrounds, text is hard to read */
}

/* After color-scheme — form inputs adapt */
:root {
  color-scheme: light dark;
}

@media (prefers-color-scheme: dark) {
  :root {
    color-scheme: dark;
  }
}

body {
  background: #1a1a1a;
  color: #ffffff;
}

input, textarea, select {
  /* Now have dark backgrounds with light text when color-scheme: dark */
}
Combining with other features

CSS
/* Advanced: custom colours + OS preference */
:root {
  /* Lightness scale using oklch() */
  --brand-hue: 264;
  --brand-lightness: 0.55;
  --brand-color: oklch(var(--brand-lightness) 0.2 var(--brand-hue));

  color-scheme: light dark;
}

@media (prefers-color-scheme: dark) {
  :root {
    --brand-lightness: 0.65;  /* lighter in dark mode */
    color-scheme: dark;
  }
}

.button {
  background-color: var(--brand-color);
  /* Colour automatically adjusts for dark mode */
}

/* High contrast mode support */
@media (prefers-contrast: more) {
  :root {
    --border-width: 2px;
    --text-secondary: var(--text-primary);
  }
}

/* Reduced motion support */
@media (prefers-reduced-motion: reduce) {
  * {
    animation: none !important;
    transition: none !important;
  }
}
Note
Pro tip: Always test your dark mode design in actual dark conditions. Designs that look fine in a bright room often need tweaks when viewed in dim light. Use `prefers-contrast: more` to support users who need higher contrast.
Don't force light mode only
Setting only `color-scheme: light;` is increasingly seen as bad practice. Users who prefer dark mode will have bright white forms and inputs on your dark background, which is inaccessible. Always support both: `color-scheme: light dark;`.
Section complete
You've now covered all aspects of colours and backgrounds in CSS — from basic values through gradients, layering, and modern dark mode support. Next section covers the fundamentals of layout with the display property and box model applications.