CSS Selector: is where has
the selectors :is(), :where(), :has() are new, supported by all major browsers since 2023.
:is(s1, s2, etc)-
matches any compound selector s1, s2 etc.
meaning is basically the same as just
s1, s2, etc :where(s1,s2, etc)-
same as the
:isbut contributes no specificity. e:has(s1,s2, etc)-
match e if it contains descendant s1 or s2 etc.
ai answer
CSS Pseudo-Classes Tutorial: :is(), :where(), and :has()
These three powerful pseudo-classes (introduced in recent years) make CSS more expressive, concise, and maintainable. They are part of the Selectors Level 4 specification.
1. :is() – The "Matches Any" Selector
:is() (formerly :matches()) lets you group selectors. It matches an element if any of the selectors inside it match.
Syntax
:is(selector1, selector2, selector3) { ... }
Example 1: Simplifying Headings
/* Before */ h1, h2, h3, h4, h5, h6 { color: red; } /* After. much cleaner */ :is(h1, h2, h3, h4, h5, h6) { color: red; }
Example 2: Complex combinations
/* Style links that are either in nav or are buttons */ :is(nav a, button.link, .card a) { color: royalblue; text-decoration: underline; }
Specificity
:is() takes the highest specificity of the selectors inside it.
:is(#header, .nav) a { } /* specificity = 0,1,0 (from #header) */
2. :where() – The "Zero-Specificity" Selector
:where() works exactly like :is(), but it always contributes zero specificity (0,0,0).
This is incredibly useful for writing non-intrusive base styles that are easy to override.
Example: Base link styles
/* These won't block more specific rules */ :where(a) { color: #0066cc; } /* This will override the :where() rule easily */ .nav a { color: #ff6600; /* wins without !important */ }
Real-world use case: Component libraries
/* Library styles */ :where(.button) { padding: 0.75rem 1.5rem; border-radius: 9999px; } /* User can easily override */ .my-custom-button { padding: 1rem 2rem; /* overrides without fighting specificity */ }
Key Difference Summary:
| Feature | :is() |
:where() |
|---|---|---|
| Specificity | Highest inside list | Always 0,0,0 |
| Use case | Grouping + power | Non-intrusive base styles |
| Override difficulty | Higher | Very easy |
3. :has() – The Relational / "Parent" Selector
:has() is the long-awaited parent selector. It selects an element if it contains (has) a matching descendant or child.
Syntax
parent:has(child) { ... }
Example 1: Style cards that contain images
.card:has(img) { border: 2px solid #4ade80; padding: 1rem; }
Example 2: Highlight sections with headings
section:has(h2) { background: #f8fafc; padding: 2rem; border-left: 5px solid #3b82f6; }
Example 3: Advanced – Adjacent siblings
/* Style a form only if it has an invalid field */ form:has(input:invalid) { border: 2px solid #ef4444; background: #fef2f2; } /* Style a list item only if it contains a checked checkbox */ li:has(input[type="checkbox"]:checked) { text-decoration: line-through; color: #666; }
Example 4: :has() with :not()
/* Articles without images */ article:not(:has(img)) { max-width: 60ch; }
Practical Combined Examples
/* 1. Style navigation items that contain a badge */ nav li:has(.badge) { position: relative; } /* 2. Zero-specificity headings in articles */ article :where(h1, h2, h3, h4) { scroll-margin-top: 5rem; } /* 3. Complex card variations */ .card:is(:has(img), :has(video)) { display: grid; grid-template-columns: 1fr 2fr; } /* 4. Dark mode cards that contain light content */ .card:has(.light-content) { background: #1e2937; color: white; }
Tips & Best Practices
- Performance:
:has()can be slower than other selectors because it looks at descendants. Use it wisely (avoid on very broad selectors like*). - Specificity: Prefer
:where()for utility/base layers. - Chaining: You can combine them:
article:has(:is(h2, h3)) :where(p) { ... } - Negation:
:has()works great with:not():.card:not(:has(.price)) { ... }
Interactive Demo Idea
Try this HTML + CSS combo:
<div class="card"> <h3>Card Title</h3> <img src="..." alt=""> </div> <div class="card"> <h3>No Image Card</h3> </div>
.card { padding: 1rem; border: 1px solid #ddd; margin: 1rem 0; } .card:has(img) { background: #ecfdf5; } .card:where(:has(h3)) { border-color: #3b82f6; }
CSS, Selectors
- CSS Selector: Index
- CSS Selector: Tutorial
- CSS Selector: Match Tag Name
- CSS Selector: Match Attribute
- CSS Selector: Match Root Element
- CSS Selector: Match Parent
- CSS Selector: Match Children
- CSS Selector: Match Sibling
- CSS Selector: Pseudo-Class
- CSS Selector: Pseudo-Element
- CSS Selector: Negation
- CSS Selector: is where has
- CSS Selector: Syntax Shortcuts
- CSS Selector: Simple, Compound, Combinator, Complex