HuntExams Academy logo
HTML/CSSIntermediate#css#variables#responsive

How would you implement a dark mode toggle using CSS?

Answer

Combine CSS custom properties for theme colors with either the prefers-color-scheme media query for automatic OS-based detection, or a data attribute/class toggled by JavaScript for manual user control, overriding the variables.

Example

:root { --bg: white; --text: black; }
[data-theme="dark"] { --bg: #1a1a1a; --text: #f0f0f0; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) { --bg: #1a1a1a; --text: #f0f0f0; }
}
body { background: var(--bg); color: var(--text); }

Detailed explanation

I would implement dark mode with CSS custom properties for theme colours, an operating-system preference as the default, and JavaScript for an explicit user override. Custom properties let components use semantic tokens such as --bg, --text, --surface, and --border instead of hard-coded colours. Changing these tokens updates the interface consistently without duplicating every component’s CSS. The important design decision is precedence: a saved user choice should win over the system preference, while users without an override should follow their device settings.

A minimal stylesheet is: :root { --bg: #fff; --text: #111; color-scheme: light; } :root[data-theme="dark"] { --bg: #1a1a1a; --text: #f0f0f0; color-scheme: dark; } @media (prefers-color-scheme: dark) { :root:not([data-theme]) { --bg: #1a1a1a; --text: #f0f0f0; color-scheme: dark; } } body { background-color: var(--bg); color: var(--text); }. Set data-theme on the html element. An explicit light value keeps the root defaults, and the media query applies only when no theme attribute exists. The color-scheme property also lets browsers adapt supported native controls and other browser-rendered UI; it does not replace your theme colours.

For manual control, attach a click handler to a real button and update document.documentElement.dataset.theme to either 'dark' or 'light'. When no override exists, use window.matchMedia('(prefers-color-scheme: dark)').matches to determine the currently effective theme before toggling. Save explicit choices in localStorage and restore them with a small early script, ideally before the first paint, to avoid a flash of the wrong theme. Handle storage failures gracefully. A separate 'Use system theme' action can remove both the attribute and saved preference.

Under the hood, the cascade selects custom-property declarations on the root, and those properties inherit into descendants where var() resolves them. With no override, the media query responds automatically to system-theme changes. Interviewers will also look for readable contrast, visible focus indicators, themed links and borders, and accessible toggle state. Test both themes across components rather than assuming that changing the body colours covers the whole page.

Key points to remember

  • Use semantic CSS custom properties so components share consistent theme tokens.
  • Apply prefers-color-scheme only when no explicit user theme is set.
  • Toggle data-theme on the html element with JavaScript for manual control.
  • Persist explicit choices and restore them early to minimise theme flashing.
  • Use color-scheme for native UI, and verify contrast, focus states, and toggle accessibility.

Common mistakes candidates make

  • Allowing the system dark-mode media query to override an explicit light-mode selection.
  • Hard-coding component colours or changing only the body, leaving unreadable controls and surfaces.
  • Checking only the data attribute when toggling, ignoring that dark mode may already be active through the system preference.

Likely follow-up questions

Can dark mode work without JavaScript?

Yes, prefers-color-scheme can automatically select colours using the operating-system preference. A conventional manual toggle with a persistent choice requires JavaScript; CSS-only checkbox techniques do not provide equivalent persistence.

How should the toggle be made accessible?

Use a native button with a stable label such as 'Dark mode' and set aria-pressed to reflect whether dark mode is active. Keep that state synchronised with the effective theme, including system changes when no override exists.

Why use custom properties instead of separate dark-mode styles for every component?

Custom properties centralise shared colours while letting components retain the same styling rules. You can still add targeted dark-mode selectors for exceptions such as logos, illustrations, or shadows.

Interview tip: Explain the precedence rule first, show the token-based CSS, and then describe JavaScript control, persistence, and accessibility.

Related Interview Questions

1
HTML/CSSIntermediate#css#position

What is the difference between absolute and fixed positioning in terms of containing block?

Open
2
HTML/CSSBeginner#css#flexbox

What is the CSS 'gap' property and how does it simplify spacing in flex/grid layouts?

Open
3
HTML/CSSBeginner#css#selectors

What is the difference between class selectors and ID selectors, and why avoid IDs for styling?

Open