</> ConvertAtlas Developer Tools
Level 2 · Intermediate — Layout & Responsive Lesson 26 Intermediate 13 min

Dark Mode Basics

Add a clean light/dark theme with CSS variables, prefers-color-scheme and a manual toggle.

Dark mode is expected on modern sites, and CSS variables make it surprisingly easy. The trick is to theme with variables, not hard-coded colours — then switching themes is just swapping variable values.

Theme with variables, not fixed colours

The foundation: define every colour as a CSS variable on <code>:root</code>, and use <code>var()</code> everywhere. Now the whole site's colours live in one place, ready to swap.

HTML
<div class="box"><h3>Themed card</h3><p>Colours come from variables.</p></div>
CSS
:root{--bg:#fff;--text:#0f172a;--muted:#64748b}
body{background:var(--bg)}
.box{background:var(--bg);color:var(--text);border:1px solid #e2e8f0;border-radius:12px;padding:18px}
.box p{color:var(--muted)}
Result
All colours reference variables — ready to theme.

Respecting the system preference

Many users set a system-wide dark preference. The <code>prefers-color-scheme</code> media query lets you honour it automatically — override your variables inside it.

css
@media (prefers-color-scheme: dark) {
  :root {
    --bg: #0f172a;
    --text: #f1f5f9;
    --muted: #94a3b8;
  }
}

A manual toggle

To let users choose, override the variables on a data attribute or class set on the root element (e.g. <code>data-theme="dark"</code>). A tiny bit of JavaScript flips the attribute; CSS does the rest.

HTML
<div data-theme="dark" class="demo"><p>Dark themed via data-theme</p></div>
CSS
.demo{--bg:#fff;--text:#0f172a;background:var(--bg);color:var(--text);padding:18px;border-radius:10px}
.demo[data-theme=dark]{--bg:#0f172a;--text:#f1f5f9}
Result
Overriding variables on [data-theme="dark"].
Common mistake
Don't
.card { background: #fff; color: #000; }
Do
.card { background: var(--bg); color: var(--text); }

Hard-coding colours throughout, then trying to add dark mode later — you would have to rewrite every rule. Theme with variables from the start.

Best practice

Theme with semantic variables (--bg, --text, --surface) rather than literal colours, honour prefers-color-scheme for first load, and offer a manual toggle that persists the choice. Always check contrast in both themes.

Try it yourself

Define colour variables on :root, build a card that uses them, then create a dark override on [data-theme="dark"]. Toggle the attribute in your head to see it switch.

Quick quiz

1. What makes theming easy?

2. Which respects the system dark preference?

3. How does a manual toggle usually work?

Key takeaways
  • Theme with CSS variables, not hard-coded colours.
  • prefers-color-scheme honours the system dark setting.
  • A manual toggle overrides variables on a root attribute/class.
  • Check contrast in both light and dark themes.