:where(), :is(), :has(), :not()

The Lesser-Used CSS Selectors

June 28, 2025

Modern CSS gives us a powerful set of tools for writing efficient styles. Some of these selectors are underutilized, but they can be very useful. Among them are the functional pseudo-class selectors such as :where(), :is(), :has(), and :not().

:where() for zero-specificity

What it does: Allows you to group selectors together without adding specificity

Why it matters: Prevents specificity conflicts, making it great for base styles and resets

:where(h1, h2, h3) {
  margin-block: 1rem;
  font-weight: 600;
}

:is() for specificity-aware grouping

What it does

Similar to :where(), but does contribute specificity based on the most specific selector inside.

Why it matters

Writing DRY code without specificity surprises when you do want the grouped selectors to hold weight in the cascade.

:is(.btn, .button) {
  @apply bg-blue-500 text-white;
}

:has() for parent-child relationships

What it does

Selects an element if it contains something matching the selector inside :has()

Use it for

  • Applying styles based on child content
  • Styling a form field differently if it has an error message
  • Replace common JS interactions like showing error styles or toggling layouts directly in CSS, with no extra classes or listeners needed
.card:has(img) {
  padding: 0;
}
.input-wrapper:has(.error-message) {
  border-color: var(--error);
}

:not() for negation

What it does

Excludes elements matching a selector.

Use it for

Writing exceptions and fallbacks.

/* Default style */
.btn {
  @apply bg-primary/50 text-primary-foreground/40;
}

/* Exception: only apply full styling if not disabled */
.btn.primary:not(.disabled) {
  @apply bg-primary text-primary-foreground;
}

Why this matters in utility-first CSS (like Tailwind)

Utility-first CSS frameworks like Tailwind are built on the idea of composing styles from small, reusable utilities.

This approach offers several benefits:

  • :where() prevents base styles from clashing with utility classes
  • :is() reduces duplication when writing custom components or variants
  • :has() enables parent-based styles that previously required JS
  • :not() provides fine-grained control when excluding specific conditions

© 2025 Caleb Durenberger. All rights reserved.