A Dark Mode Toggle Without JavaScript
Have you ever seen a toggle on a website that allows you to switch between a
dark and light mode? Now it’s possible to implement one without JavaScript,
thanks to :has()
CSS selector.
:has()
premise is simple. Select a parent that has a given selector. Be it
children or sibling using +
or ~
. With Firefox stable adding support for
:has()
last month, the browser support reached 90% according to
caniuse.com!
Without further ado, let’s start with HTML for the toggle:
<!-- ... -->
<body>
<label>
<input id="dark" type="checkbox">
Dark mode
</label>
</body>
<!-- ... -->
I hope you see where this is going! Now, the CSS for this is packed. Packed
with tiny bits and tricks I learned. These are: declaring variables on elements
for an override, using :has()
, and using :checked
state selector.
body {
--fg: black;
--bg: white;
color: var(--fg);
background-color: var(--bg);
}
body:has(#dark:checked) {
--fg: white;
--bg: black;
}
Here is a live example using the above code (and a border):
There are a couple more things that we can do such as:
- Making use of label’s
for
attribute to decouple the label from the checkbox - Hiding the checkbox
- Styling the toggle
- Changing labels based on the theme we are in
@media (prefers-color-scheme: dark)
support
I have created an example page with these improvements. You can view it at tymekdev.github.io/no-js-dark-mode-toggle and its source code on GitHub.