:is() vs. :where()
Back in March, I wrote about the :is() pseudo-class and how it can be used to reduce selector complexity.
Today, I wanted to talk about another pseudo-class that does the same thing, :where(), how they’re different, and when to use one versus the other.
Let’s dig in!
How :where() works
You use the :where() pseudo-class exactly the same way you’d use :is(). Pass in a comma-separated list of selectors, and it groups them together.
For example, this…
.bg-primary-dark h1,
.bg-primary-dark h2,
.bg-primary-dark h3,
.bg-primary-dark h4,
.bg-primary-dark h5,
.bg-primary-dark h6,
.bg-secondary h1,
.bg-secondary h2,
.bg-secondary h3,
.bg-secondary h4,
.bg-secondary h5,
.bg-secondary-dark h6,
.bg-secondary-dark h1,
.bg-secondary-dark h2,
.bg-secondary-dark h3,
.bg-secondary-dark h4,
.bg-secondary-dark h5,
.bg-secondary-dark h6,
.bg-primary-dark a:not(.btn),
.bg-secondary a:not(.btn),
.bg-secondary-dark a:not(.btn) {
color: #ffffff;
}Becomes this…
:where(
.bg-primary-dark,
.bg-secondary,
.bg-secondary-dark
) :where(
h1,
h2,
h3,
h4,
h5,
h6,
a:not(.btn)
) {
color: #ffffff;
}What’s the difference between :is() and :where()?
The :is() pseudo-class takes on the highest specificity of the selectors in it, while :where() always has a specificity of 0.
For example…
h1has a specificity of0.0.1#main > .btnhas a specificity of1.1.0.sandwichhas a specificity of0.1.0
If I pass them collectively into :is(), the specificity of all of the selectors would be 1.1.0, the highest specificity of the group.
/* specificity: 1.1.0 */
:is(h1, #main > .btn, .sandwich) {
/* ... */
}If I pass them collectivity into :where(), the specificity of all of the selectors would be 0.
/* specificity: 0 */
:where(h1, #main > .btn, .sandwich) {
/* ... */
}Should you use :is() or :where()?
There are no hard-and-fast rules, but a few useful guidelines…
- If you want your selectors to specifically have the lowest specificity possible, use
:where(). - If all of the selectors you’re using have the same specificity and you want them to keep their natural specificity, use
:is(). - If you’re using mixed-specificity selectors and want them all to have the highest possible specificity, use
:is().
A lot of unexpected side-effects can, in my opinion, be mitigated with through better CSS conventions.
Consider this example…
/* specificity: 1.1.0 */
:is(h1, #main > .btn, .sandwich) {
/* ... */
}That is, frankly, probably some badly written CSS.
That kind of combination of selectors and overrides implies that you might need to hoist your selectors up to a higher level, adjust how things are showing up in the cascade, or reach for some utility classes.