In case you missed it, the CSS :has()
selector is now supported in all major browsers!
It's a very weird (but cool) selector that allows you to select elements that contain a specific thing, like for example a:has(img)
selects all anchor <a>
tags that have an <img>
inside.
I've thought it was really interesting, but I've never actually had a good use-case to use it myself besides... adding captions to images or something. But, that changed today!
On my personal website (which I first made a solid 4 years ago with plain HTML, CSS, and JS, and I admit I haven't really updated since besides some words and links), I have a dark mode toggle. It's probably rarely if ever clicked (I should probably modernize it with some prefers-color-scheme
stuff sometime), but when you do, it does what you expect, it turns on dark mode.
Under the hood, that toggle adds a .dark-mode
class to the <body>
, and in the CSS, we style nearly everything based on that:
html,
body {
/* ... */
background: var(--white);
color: var(--black);
}
body.dark-mode,
body.dark-mode a {
background: var(--black);
color: var(--white);
}
/* ... */
Now, here's the problem: the body doesn't always take up the entire height of the page. I was cleaning up and organizing some code, and when I put the background
and color
properties under html, body
, that meant that the html
was keeping its white background and black text color, even in dark mode.
BUT :has()
came to my rescue! Because it kind of acts like a parent selector, I'm able to say "if the <html>
has .dark-mode
inside of it, that means it should have certain styles." One line change later we had this:
html:has(.dark-mode),
body.dark-mode,
body.dark-mode a {
background: var(--black);
color: var(--white);
}
...and voilà, just like that, all of my problems I've ever had are solved!
Top comments (2)
Interesting Article!
Glad you found a solution that works for you!
Actually, the
background-color
value ofbody
will propagate tohtml
if it istransparent
onhtml
; see [CSS-BACKGROUNDS-3] § 2.11.2. Simply remove the declaration onhtml
and it should (hopefully) work the same!