ReviseAlgo Logo

Modern CSS

Dark Mode

Master CSS Dark Mode, covering prefers-color-scheme media queries, class-based triggers, CSS custom property themes, and theme persistence.

Last Updated: July 15, 2026 • 10 min read

1. Learning Objectives

In this lesson, you will master implementing light and dark themes using CSS. By the end of this topic, you will be able to:

  • Implement automatic theme matching using the prefers-color-scheme media query.
  • Build class-based theme triggers (like a .dark class toggle).
  • Structure a CSS custom property theme system using semantic variables.
  • Persist user theme selections using JavaScript and localStorage.
  • Avoid visual glitches (such as color flash during page load).

2. Overview

CSS Dark Mode allows web applications to adapt their styling to the user's system preferences or a custom theme toggle. This is implemented using the prefers-color-scheme media query for system theme matching, or by toggling a class (like .dark) on the root HTML element. Both approaches rely on CSS variables to update colors dynamically without modifying individual component files.

3. Why This Topic Matters

Well-designed theme systems keep pages readable and prevent visual bugs:

  • Static Color Variable Trap: Using physical color names for variable definitions (e.g. --white: #fff) makes theme toggles difficult to maintain. You must define variables semantically based on their function (e.g. --bg-primary), so that the variable can hold white in light mode and dark gray in dark mode.
  • Flash of Light Theme: Toggling dark mode using client-side JavaScript can cause a visual glitch where the light theme flashes briefly on page load before the dark styles apply. Loading and executing the theme preference script early in the head tag prevents this flash.

4. Real-World Analogy

Think of CSS variable theme systems like **adjusting lighting settings on an electronic dashboard**:

  • Static Design (Opaque layout): Printing speedometer labels in permanent black ink. The labels are readable during the day, but become invisible in the dark unless you shine a light directly on them.
  • Dynamic Design (Semantic variables): Displaying speedometer labels using back-lit displays. You link the labels to a semantic lighting slot (e.g. `LabelColor`).
    • Day mode: The display glows black on a white background.
    • Night mode: The display switches to glow white on a black background. The dashboard design itself remains unchanged.

5. Core Concepts

A dark mode system relies on three main components:

  • prefers-color-scheme: A media query that matches the user's operating system theme preference (e.g. `@media (prefers-color-scheme: dark)`).
  • Semantic CSS Custom Properties: Defining theme variables semantically based on their function (e.g. --bg-primary, --text-primary) rather than their color values (e.g. --white).
  • Class-Based Toggles: Toggling a class (like .dark) on the <html> element using JavaScript. This allows users to manually override their system preferences.
  • localStorage Persistence: Saving the user's theme selection in browser storage so that their preference is remembered on future visits.
Trigger Method Target Syntax Typical Application
System Match @media (prefers-color-scheme: dark) Applies dark mode styles automatically based on the user's OS settings.
Class Override html.dark Applies dark mode styles when the user toggles a theme switch button.

6. Syntax & API Reference

Below is a standard CSS variable theme configuration supporting both system preferences and class-based overrides:

7. Visual Diagram

This diagram displays how the browser checks theme preferences and applies styles:

8. Live Example — Full Working Code

A sample HTML document showcasing an interactive dark mode toggle that saves the user's preference in localStorage:

9. Interactive Playground

Try It Yourself Challenges:

  1. Click the "Switch Theme" button to toggle between light and dark themes.
  2. Refresh the browser page and verify that your selected theme persists.

10. Common Mistakes

Mistake Why it happens Wrong Correct
Defining variables by color names Defining variable names based on colors (e.g. --white) instead of their function. This makes it difficult to adjust values for dark mode. --white: #1a202c; (in dark mode) --bg-primary: #1a202c; (semantic naming)
Executing scripts late (Light Theme Flash) Loading the theme check script at the bottom of the body tag, causing the default light theme to display briefly before dark styles are applied. Script loaded defer/async at bottom. Execute the theme check inline in the head tag.

11. Best Practices

  • Use semantic variable names: Define CSS variables based on their function (e.g. --bg-primary, --text-body) rather than their color values (e.g. --white).
  • Execute theme checks early to prevent flashes: Place the theme preference script inline in the head tag to prevent a light theme flash on page load.
  • Avoid pure black backgrounds: Pure black backgrounds (#000000) can cause visual strain when paired with bright white text. Use dark gray tones (like #121212 or #1a202c) for a softer dark mode background.
  • Support transitions for theme changes: Apply a brief transition (e.g. transition: background-color 0.3s ease) to background and text colors to make theme switching look smooth.

12. Browser Compatibility

Feature Chrome Firefox Safari Edge
prefers-color-scheme media query Supported (76+) Supported (67+) Supported (12.1+) Supported (79+)

13. Interview Questions

🟢 Q1: Why should you run the theme check script inline in the head tag instead of loading it at the bottom of the body?

Answer: If the script is loaded at the bottom of the body, the browser will render the page using the default light theme before loading and executing the script. When the script finally runs and adds the dark class, the page flashes from light to dark. Executing the script inline in the head tag ensures the browser applies the dark class before the page starts rendering, preventing the flash.

14. Debugging Exercise

Identify why the dark mode styles fail to apply to the main panel, and fix the CSS configuration:

View Solution

Diagnosis: The .main-panel class uses a static color code (#fff) for its background instead of the theme variable (var(--color-main)). Because it references a static value, changes to the theme variables are ignored. To resolve this, reference the theme variable.

Fixed CSS:

15. Practice Exercises

Exercise 1: Theme Switcher Toggle Switch

Build a theme switcher switch button. Set up custom variables, style the button for light and dark states, and use JavaScript to toggle classes.

16. Scenario-Based Challenge

The Dark Mode Flash Prevention Challenge:

A project dashboard has a dark mode toggle. When users refresh the page, the default light theme flashes briefly before the user's dark mode preference applies. Propose an implementation plan using head scripts to resolve this visual flash.

17. Quick Quiz

Q1: Which media query matches the user's operating system theme preference?

A) @media (color-scheme: dark)

B) @media (theme: dark)

C) @media (prefers-color-scheme: dark)

Answer: C — The prefers-color-scheme media query matches the user's system theme preference.

18. Summary & Key Takeaways

  • • Use semantic variable names (e.g. --bg-primary) rather than color values (e.g. --white) to make theme systems easy to manage.
  • • Run theme preference check scripts inline in the head tag to prevent light theme flashes on page load.

19. Cheat Sheet

Media Query Syntax Visual Layout Action
@media (prefers-color-scheme: dark) { ... } Applies dark mode styles if the user's OS preference matches dark.