Reduce Selector Repetition with CSS :is() and :where()

Problem

Applying similar styles to multiple elements requires listing out selectors repeatedly:

.article h1,
.article h2,
.article h3,
.article h4 {
  color: #333;
  line-height: 1.4;
}

This gets tedious fast, especially with nested contexts.

Solution

:is() lets you group selector lists:

.article :is(h1, h2, h3, h4) {
  color: #333;
  line-height: 1.4;
}

/* Group both sides */
:is(.article, .sidebar) :is(h1, h2, h3, h4) {
  line-height: 1.4;
}

:where() has the same syntax but always has zero specificity:

/* :is() - takes the highest specificity argument */
:is(.class, #id) p { } /* specificity: (1,0,1) */

/* :where() - always zero specificity */
:where(.class, #id) p { } /* specificity: (0,0,1) */

This makes :where() ideal for default styles that should be easy to override:

/* Base styles - easily overridable */
:where(.btn) {
  padding: 8px 16px;
  border-radius: 4px;
}

/* Simple class override works */
.my-btn {
  padding: 12px 24px;
}

Key Points

  • Both :is() and :where() group selector lists to reduce repetition
  • :is() adopts the highest specificity among its arguments
  • :where() always contributes zero specificity, making overrides easy
  • Use :where() for resets/libraries, :is() for component styles