Creating Dark Modes With Only CSS

Published December 11th, 2019 2m read time

I've seen a lot of buzz recently concerning dark modes and how to create them. The coming of dark mode in iOS seems to have triggered this, but I feel like everyone's going about it in the wrong way. Some people are creating solutions that check the prefers-color-scheme media query in CSS, but the majority of JavaScript-focused developers are taking a JS-only approach. They're using JS to programmatically check if the query is matched instead of just writing CSS to handle it. If it matches, they apply either a light class or a dark class. This works, but it seems unnecessary. If you're curious what the JS approach looks like, here you go.

(function() {
	window.__onThemeChange = function() {};
	function setTheme(newTheme) {
		window.__theme = newTheme;
        preferredTheme = newTheme;
        document.body.setAttribute('data-theme', newTheme);
    	window.__onThemeChange(newTheme);
    }
    var preferredTheme;
	try {
		preferredTheme = localStorage.getItem('theme');
	} catch (err) { }
    window.__setPreferredTheme = function(newTheme) {
		setTheme(newTheme);
    	try {
    		localStorage.setItem('theme', newTheme);
    	} catch (err) {}
    }
    var darkQuery = window.matchMedia('(prefers-color-scheme: dark)');
    darkQuery.addListener(function(e) {
    	window.__setPreferredTheme(e.matches ? 'dark' : 'light')
    });
    setTheme(preferredTheme || (darkQuery.matches ? 'dark' : 'light'));
})();

I think the better solution to the problem is to lean into CSS custom properities. This can even be done with Tailwind CSS by creating a few classes that handle the most common color issues, and then flip those inside a media query that checks prefers-color-scheme. You get immediate theme handling that doesn't require parsing or execution of any JavaScript.

.text-default {
	@apply text-gray-900;
}

.bg-default {
	@apply bg-gray-100;
}

@media(prefers-color-scheme: dark) {
    .text-default {
      @apply text-gray-100;
    }  	
    
    .bg-default {
      @apply bg-gray-900;
    }
}

Yes, you'll be leaving IE11 theme support in the bin, but IE11 shouldn't be a top priority anyway. If it is, I doubt light and dark modes is an issue. Hit me up on Twitter if you have thoughts.