What is Dark mode?
Dark mode: Dark mode is a color scheme that renders light text on dark surfaces, chosen by the user or taken from the operating system's preference.
Dark mode replaces a light interface with one built on dark surfaces and light text. Operating systems expose the user's choice, and browsers pass it to CSS through the prefers-color-scheme media feature. Most apps honor that preference by default and add a manual toggle with three options: light, dark and system. Done well, dark mode is not an inversion of the light theme. It is a second set of carefully chosen values for the same roles.
How it works
There are two common strategies. The media query strategy follows the OS automatically. The class strategy puts a class such as dark on <html> so the user can override the system, which is what apps with a toggle use. In Tailwind CSS v4 you point the dark: variant at the class and redefine tokens under it:
@import "tailwindcss";
@custom-variant dark (&:is(.dark *));
:root {
--bg: oklch(0.985 0.002 264);
--fg: oklch(0.185 0.012 268);
color-scheme: light;
}
.dark {
--bg: oklch(0.145 0.004 270);
--fg: oklch(0.965 0.003 270);
color-scheme: dark;
}
The color-scheme property tells the browser to render its own UI, such as scrollbars, form controls and the default canvas, in the matching scheme.
Design guidelines
- Avoid pure black. Near-black surfaces soften the glare of white text and leave room for depth below the page color.
- Lightness shows elevation. Shadows barely read on dark backgrounds, so raised surfaces get slightly lighter instead.
- Rebalance accents. A brand color tuned for white usually needs more lightness and a little less chroma on dark surfaces. OKLCH makes that adjustment predictable.
- Recheck contrast. Every text and border pairing needs its own WCAG contrast check in both themes.
- Theme images and charts. Screenshots, logos and chart palettes often need dark variants.
Common pitfalls
The classic bug is the flash: a server-rendered page arrives light, then turns dark once JavaScript reads the saved preference. It is a form of FOUC. The fix is a tiny blocking script in <head> that sets the class before the first paint, plus suppressHydrationWarning on <html> so React tolerates the changed attribute during hydration. Dark mode in Next.js without the flash walks through the full setup, including the system option and theme-color.
How MiniDev UI handles it
Dark mode in MiniDev UI redefines semantic tokens under .dark; components carry no dark: overrides for their core colors. Shadows, highlights and materials are retuned per theme too. ThemePicker is a three-way light, dark and system control that toggles the class and reports changes, so you can wire it to your own persistence.