🧠 CSS :has() β€” The Parent Selector We’ve Been Waiting For

The CSS :has() pseudo-class is one of the most powerful modern additions to CSS. It allows you to style an element based on its children, descendants, or even its siblings. This means CSS finally behaves like a **parent selector**, enabling patterns that were previously possible only with JavaScript.

>>β€œ:has() gives CSS superpowers β€” dynamic, reactive styling without JavaScript.”

Note

βœ” Works like a parent selector
βœ” Selects an element that contains another selector
βœ” Enables state-based UI patterns
βœ” Part of CSS Selectors Level 4

πŸ“¦ Syntax

:has() syntax

selector:has(selector) {
  /* styles */
}

The element before :has() is the one being styled; the selector inside :has() is the condition.

🎨 1. Style a Parent if It Contains a Specific Child

Highlight card if it has an image

.card:has(img) {
  border-color: #3498db;
  background: #eef7ff;
}

The .card gets styled only if it contains an <img>.

🎨 2. Style a Form Group If an Input Is Invalid

Invalid form group

.form-group:has(input:invalid) {
  border-left: 4px solid red;
}

No JS needed for highlighting error fields.

🎨 3. Style a Section Based on Hover of a Child

Parent reacts to child hover

.menu:has(.menu-item:hover) {
  background: #f0f0f0;
}

🎨 4. Select Siblings Using :has()

Style label when input is checked

label:has(+ input:checked) {
  font-weight: bold;
}

The label changes when its sibling input is checked.

🎨 5. Accordion / Toggle Behavior Without JavaScript

CSS-only accordion

.accordion:has(input:checked) .content {
  max-height: 300px;
  opacity: 1;
}

A pure CSS accordion powered by :has().

🎨 6. Change Layout If a Section Contains Many Items

Dynamic layout

.grid:has(.item:nth-child(5)) {
  grid-template-columns: repeat(3, 1fr);
}

If 5 or more items exist, switch the layout.

🎨 7. Add Styles When Element Has No Children (inverse)

Empty state handling

.todo-list:not(:has(li)) {
  background: #f9f9f9;
  color: #aaa;
  text-align: center;
}

πŸ§ͺ Real-World Use Cases

1️⃣ Valid/Invalid Form with No JS

Valid state

.input-group:has(input:valid) {
  border-color: green;
}

2️⃣ Navbar with Active Link

Active menu highlight

nav:has(a.active) {
  border-bottom: 3px solid #3498db;
}

3️⃣ Card with CTA Button

Card highlight

.card:has(.primary-btn) {
  box-shadow: 0 0 10px rgba(0,0,0,0.1);
}

⚠️ Performance & Best Practices

  • ❌ Avoid using :has() in global selectors like *:has()
  • ❌ Don’t over-nest selectors inside :has()
  • βœ” Great for UI components, not general resets
  • βœ” Use it for specific conditions, not broad patterns

Note

:has() is powerful but can be expensive if misused β€” apply it thoughtfully to keep CSS performant.

πŸ”₯ Summary

The :has() selector brings parent-based styling to CSS, unlocking powerful UI patterns without JavaScript. It enables dynamic, responsive, and reactive styling based purely on HTML structure.

>>β€œCSS :has() is the future β€” reactive styling with zero JavaScript.”