
Dark Mode From Tokens
I was sitting in a dimly lit coffee shop at 2:00 AM, squinting at a client’s new site, when the realization hit me like a physical blow: their “modern” theme was basically a flashlight pointed directly into my retinas. It’s one of those things people treat like a luxury feature, but if you’re actually building for the real world, knowing how to support dark mode in a theme is just basic digital empathy. I’ve spent nine years inheriting sites where the developer thought a white background was the only way to exist, and frankly, I’m tired of the eye strain.
I’m not here to sell you on some bloated, over-engineered plugin that’s going to tank your PageSpeed scores. Instead, I’m going to show you how to do this the right way—by making intentional design decisions that respect the user’s system preferences without breaking your CSS. We’re going to look at using CSS variables to swap color palettes cleanly, because if you aren’t coding with a system-first mindset, you’re just adding more technical debt to an already messy pile.
Table of Contents
Decoding the User Preference Media Query

Before you start hacking away at your stylesheet, you need to understand the mechanism that actually triggers the switch. We aren’t building some custom toggle that requires a heavy JavaScript library to function; we’re tapping into the user preference media query. This is a built-in CSS feature that listens to whatever the user has already decided in their OS settings. If they’ve told their Mac or Windows machine that they prefer a dark interface, your theme should be smart enough to listen.
Technically, we’re talking about `@media (prefers-color-scheme: dark)`. It’s elegant, it’s lightweight, and unlike the bloated page builders I’ve had to clean up lately, it doesn’t require a massive footprint to work. When you wrap your dark styles inside this block, you aren’t just adding a feature; you’re respecting the intent of the user.
However, don’t just dump a bunch of dark hex codes into that media query and call it a day. If you want this to be maintainable, you should be utilizing design system color tokens. Instead of hardcoding `#000000` everywhere, you define a variable for your background and a variable for your text. When the media query kicks in, you simply swap the values of those variables. It makes the whole process much less of a headache when you inevitably have to revisit the code six months later.
Building Robust Dark Mode Color Palettes

If you’re still hard-coding hex values like `#FFFFFF` directly into your stylesheets, please stop. I’ve spent enough hours unpicking the mess left behind by developers who treat color like a static afterthought. To do this right, you need to move toward semantic color naming. Instead of calling a variable `–white`, call it `–background-primary`. When the user flips that switch, you aren’t hunting down every instance of a specific shade; you’re simply swapping the values assigned to those functional roles.
This is where your design system color tokens actually earn their keep. I treat my color palettes like a logic puzzle: I define a set of light-mode tokens and a corresponding set for dark mode, then map them to the same semantic names. This approach keeps your CSS lean and prevents that bloated, unreadable spaghetti code I see in so many inherited projects.
The real danger, though, isn’t just making things look “cool” in the dark; it’s failing on the basics. You have to obsess over accessibility contrast ratios during this entire process. A dark grey background might look sleek in your design mockup, but if your text color doesn’t punch through, you’ve built a broken experience. I’d rather have a boring, highly legible site than a “modern” one that’s impossible to read at 11 PM.
Don't Just Invert the Colors and Call It a Day
- Stop using pure black (#000000) for your dark backgrounds; it creates a jarring, high-contrast vibration against white text that’s an absolute nightmare for readability. Stick to deep greys or navy-tinted darks to keep the eye from straining.
- Use CSS custom properties—variables, if you want to be professional—for everything. If I have to hunt through a stylesheet to find where a specific shade of “off-white” is hardcoded, I’m going to lose my mind.
- Test your images and icons because a dark mode that leaves a bright, blinding white logo or a high-exposure product shot in the middle of a dark UI looks amateur and unpolished.
- Check your elevation and shadows. In light mode, we use shadows to show depth; in dark mode, shadows are invisible, so you need to use lighter surface tints to signal that one element is sitting on top of another.
- Don’t forget the “manual override” toggle. Some users have system settings that don’t match their actual preference in the moment, and forcing a design decision on them without an escape hatch is just bad UX.
Stop Guessing and Start Deciding

At the end of the day, implementing dark mode isn’t about adding some flashy toggle that users will eventually ignore; it’s about respecting the user’s environment. We’ve covered the technical necessity of the `prefers-color-scheme` media query and the tedious, necessary work of building a color palette that doesn’t look like a neon nightmare in a dark room. If you skip the heavy lifting of mapping your variables correctly, you’re just adding more bloat to an already messy codebase. You have to actually plan your color relationships before you start writing CSS, or you’ll spend your entire weekend fixing contrast ratios that make people squint. Don’t let your theme become another unusable mess that someone else has to clean up later.
I’ve spent way too many hours un-building sites where the developer thought “dark mode” just meant slapping a black background on everything and calling it a day. It’s lazy, and frankly, it’s bad design. When you take the time to build a theme that responds intelligently to its surroundings, you aren’t just writing code—you’re making a choice about how your work lives in the real world. Build it right the first time so the next person doesn’t have to inherit your technical debt. Make it intentional, make it clean, and for heaven’s sake, make sure it’s easy on the eyes.