# Respect Color Scheme Preference

**MUST** · **ID:** `design-dark-preferences` · **Category:** design
**Source:** [Tailwind](https://tailwindcss.com/docs)
**Interactive version:** https://ui-guides-agent-rules.netlify.app/principles/design-dark-preferences

> MUST: Apply dark class in inline <head> script before first paint to prevent theme flash

Initialize theme from prefers-color-scheme before page renders

If you wait for JavaScript to run before checking the color scheme, users see a flash of the wrong theme (FART). Inline a script in <head> that runs synchronously before render.

## Bad — do not do this

`design-dark-preferences-bad`

```tsx
export function DarkPreferencesBad() {
  return (
    <div className="w-full max-w-md space-y-4">
      <div className="bg-card border border-border rounded-lg p-4">
        <h4 className="font-medium mb-3">Deferred Theme Check</h4>
        <div className="bg-muted rounded-md p-3 font-mono text-xs overflow-x-auto">
          <pre className="text-error">{`// In React useEffect (too late!)
useEffect(() => {
  const prefersDark = matchMedia(
    '(prefers-color-scheme: dark)'
  ).matches;

  if (prefersDark) {
    document.documentElement
      .classList.add('dark');
  }
}, []);`}</pre>
        </div>
      </div>
      <div className="flex items-center gap-4">
        <div className="flex-1 p-3 bg-[var(--ex-dark-pref-light-bg)] border border-[var(--ex-dark-pref-light-border)] rounded-lg text-center text-sm text-[var(--ex-dark-pref-light-text)]">
          <div className="font-medium">Initial</div>
          <div className="text-xs text-[var(--ex-dark-pref-light-subtext)]">Light theme</div>
        </div>
        <div className="text-muted-foreground animate-pulse">...</div>
        <div className="flex-1 p-3 bg-[var(--ex-dark-pref-dark-bg)] border border-[var(--ex-dark-pref-dark-border)] rounded-lg text-center text-sm text-[var(--ex-dark-pref-dark-text)]">
          <div className="font-medium">After JS</div>
          <div className="text-xs text-[var(--ex-dark-pref-dark-subtext)]">Dark theme</div>
        </div>
      </div>
      <p className="text-xs text-error">
        Theme applied after render causes jarring flash (FART)
      </p>
    </div>
  );
}
```

## Good — do this

`design-dark-preferences-good`

```tsx
export function DarkPreferencesGood() {
  return (
    <div className="w-full max-w-md space-y-4">
      <div className="bg-card border border-border rounded-lg p-4">
        <h4 className="font-medium mb-3">Inline Theme Script</h4>
        <div className="bg-muted rounded-md p-3 font-mono text-xs overflow-x-auto">
          <pre className="text-foreground">{`<head>
  <script>
    const theme = localStorage.theme;
    const prefersDark = matchMedia(
      '(prefers-color-scheme: dark)'
    ).matches;

    if (theme === 'dark' ||
        (!theme && prefersDark)) {
      document.documentElement
        .classList.add('dark');
    }
  </script>
</head>`}</pre>
        </div>
      </div>
      <div className="flex items-center gap-4">
        <div className="flex-1 p-3 bg-background border border-border rounded-lg text-center text-sm">
          <div className="font-medium">Page Load</div>
          <div className="text-xs text-success">Correct theme instantly</div>
        </div>
        <div className="text-muted-foreground">=</div>
        <div className="flex-1 p-3 bg-background border border-border rounded-lg text-center text-sm">
          <div className="font-medium">No Flash</div>
          <div className="text-xs text-success">Smooth experience</div>
        </div>
      </div>
      <p className="text-xs text-success">
        Script runs before first paint, preventing theme flash
      </p>
    </div>
  );
}
```

## References

- [Avoiding FART](https://css-tricks.com/flash-of-inaccurate-color-theme-fart/)
- [prefers-color-scheme](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@media/prefers-color-scheme)
