We’ve gotten so many new CSS selectors lately that almost feel like magic, but with magic comes confusion. Sometimes it’s surprisingly hard to see the difference between two selectors that look almost identical. Do you know the difference between :has(:not(…)) and :not(:has(…))? A small hint: they are definitely not the same thing if you look at it more closely.
section:has(:not(img)) {
}
section:not(:has(img)) {
}
At first glance, they look similar. But if we rewrite them slightly, the difference becomes clearer:
section:has(*:not(img)) {
}
section:not(section:has(img)) {
}
Now the logic starts to unfold. section:has(:not(img)) selects a section that contains anything that is not an img. On the other hand, section:not(:has(img)) selects a section only if it doesn’t contain an img at all. Same words. Different order. Completely different meaning.
For a CSS nerd like me, this was such a satisfying little aha! moment. I love when something that looks confusing at first suddenly clicks — and you realize CSS is just being very, very precise.
