# Declare color-scheme for Dark Themes

**MUST** · **ID:** `design-color-scheme` · **Category:** design
**Source:** [Vercel](https://github.com/vercel-labs/agent-skills/blob/main/skills/web-design-guidelines/SKILL.md)
**Interactive version:** https://ui-guides-agent-rules.netlify.app/principles/design-color-scheme

> MUST: Set `color-scheme` on `<html>` (`dark`, or `light dark` to follow the OS) so browser-drawn UI — scrollbars, checkboxes, radios, range tracks, date pickers, number spinners — repaints for the theme. This is separate from reading `prefers-color-scheme`, which only tells you what the user wants.

Set the CSS color-scheme property on <html> so native browser UI follows the theme

Your CSS only paints what you render — scrollbars, checkboxes, radios, range tracks, date pickers, and number spinners are drawn by the browser itself, and it draws them light unless told otherwise. The `color-scheme` property is that signal: it switches the user agent's rendering intent, so those native widgets repaint dark for free. This is distinct from reading `prefers-color-scheme` (which tells *you* what the user wants, and prevents a theme flash) — `color-scheme` tells the *browser* what you chose. Set `color-scheme: dark` on the dark theme, or `light dark` on `<html>` to let the UA follow the OS.

## Rule snippet

```tsx
html { color-scheme: light dark; }
html[data-theme="dark"] { color-scheme: dark; }
```

## Bad — do not do this

`design-color-scheme-bad`

```tsx
/**
 * BAD: The page is painted dark by CSS, but `color-scheme` is never declared on
 * <html>. The browser therefore keeps its default light rendering intent, so every
 * piece of *native* UA chrome — scrollbar track/thumb, checkbox, range track, date
 * picker — is drawn light on top of the dark surface.
 *
 * The `colorScheme: 'light'` below is what an undeclared <html> effectively gives you.
 */
export function ColorSchemeBad() {
  return (
    <div className="w-full max-w-md space-y-4">
      <div className="bg-muted rounded-md p-3 font-mono text-xs overflow-x-auto">
        <pre className="text-error">{`html.dark {
  /* every token flipped to dark… */
  /* …but nothing here tells the   */
  /* browser how to paint its own  */
  /* scrollbars and form controls. */
}`}</pre>
      </div>

      {/* A dark-themed page region. No matching color-scheme. */}
      <div
        className="dark bg-background text-foreground border border-border rounded-lg p-4 space-y-3"
        style={{ colorScheme: 'light' }}
      >
        <p className="text-xs font-medium text-muted-foreground">
          Dark page, light native widgets
        </p>

        <div className="h-24 overflow-y-scroll rounded-md border border-border p-2 text-xs leading-5">
          <p>Scroll me. The scrollbar next to this text is still the light-mode one.</p>
          <p>It sits on a dark panel and glares.</p>
          <p>Same for every control below.</p>
          <p>None of them got the memo.</p>
          <p>Because the browser was never told.</p>
        </div>

        <label className="flex items-center gap-2 text-xs">
          <input type="checkbox" defaultChecked />
          Native checkbox
        </label>

        <label className="block space-y-1 text-xs">
          <span>Native range</span>
          <input type="range" defaultValue={40} className="w-full" />
        </label>

        <label className="block space-y-1 text-xs">
          <span>Native date input</span>
          <input type="date" defaultValue="2026-07-14" className="w-full rounded-md p-1" />
        </label>
      </div>

      <p className="text-xs text-error">
        Scrollbar, checkbox, slider and date picker render in light mode on a dark
        page — the browser was never told the page is dark.
      </p>
    </div>
  );
}
```

## Good — do this

`design-color-scheme-good`

```tsx
/**
 * GOOD: `color-scheme: dark` is declared alongside the dark tokens, so the browser
 * renders its own chrome — scrollbars, checkboxes, range tracks, date pickers,
 * spinners — in dark mode too. One CSS property, zero custom widget styling.
 */
export function ColorSchemeGood() {
  return (
    <div className="w-full max-w-md space-y-4">
      <div className="bg-muted rounded-md p-3 font-mono text-xs overflow-x-auto">
        <pre className="text-foreground">{`html.dark {
  /* every token flipped to dark… */
  color-scheme: dark; /* ← tell the UA */
}

/* Or let the UA follow the OS: */
html { color-scheme: light dark; }`}</pre>
      </div>

      {/* Same dark-themed page region, now with a matching color-scheme. */}
      <div
        className="dark bg-background text-foreground border border-border rounded-lg p-4 space-y-3"
        style={{ colorScheme: 'dark' }}
      >
        <p className="text-xs font-medium text-muted-foreground">
          Dark page, dark native widgets
        </p>

        <div className="h-24 overflow-y-scroll rounded-md border border-border p-2 text-xs leading-5">
          <p>Scroll me. The scrollbar is dark, because the UA knows the page is.</p>
          <p>It blends into the panel instead of glaring.</p>
          <p>Same for every control below.</p>
          <p>All of them got the memo.</p>
          <p>One declaration did it.</p>
        </div>

        <label className="flex items-center gap-2 text-xs">
          <input type="checkbox" defaultChecked />
          Native checkbox
        </label>

        <label className="block space-y-1 text-xs">
          <span>Native range</span>
          <input type="range" defaultValue={40} className="w-full" />
        </label>

        <label className="block space-y-1 text-xs">
          <span>Native date input</span>
          <input type="date" defaultValue="2026-07-14" className="w-full rounded-md p-1" />
        </label>
      </div>

      <p className="text-xs text-success">
        Scrollbar, checkbox, slider and date picker all follow the theme — no custom
        widget CSS, just `color-scheme`.
      </p>
    </div>
  );
}
```

## References

- [MDN: color-scheme](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/color-scheme)
- [web.dev: Improved dark mode default styling](https://web.dev/articles/color-scheme)
