The :has()
selector addresses a long-standing limitation in CSS - the inability to style elements based on their contents.
:has()
Baseline 2023 newly available
Supported in Chrome: yes.
Supported in Edge: yes.
Supported in Firefox: yes.
Supported in Safari: yes.
Since December 2023 this feature works across the latest devices and browser versions. This feature might not work in older devices or browsers.
While CSS has always allowed styling children based on their parents, the reverse wasn’t possible until now. This change brings new possibilities for dynamic, content-aware styling.
The :has()
selector functions as a conditional check for element contents. It enables style application based on whether an element contains specific children or siblings:
/* Style cards differently if they contain an image */.card:has(img) { padding: 0; overflow: hidden;}
This straightforward syntax eliminates the need for JavaScript or complex CSS workarounds that were previously necessary.
The :has()
selector becomes more powerful when combined with other selectors for checking states, positions, and combinations:
/* Style paragraphs that have links */p:has(a) { padding-right: 1.5em; background: url(external-link.svg) no-repeat right;}
/* Style form groups with invalid inputs */.form-group:has(input:invalid) { border-left: 3px solid red;}
/* Style headings followed by paragraphs */h2:has(+ p) { margin-bottom: 0.5em;}
The :has()
selector enables adaptive layouts that respond to content structure:
.grid { display: grid; gap: 1rem;}
/* Switch to single column if any card has long content */.grid:has(.card > p:has(+ p)) { grid-template-columns: 1fr;}
/* Add extra spacing when cards have images */.grid:has(.card:has(img)) { gap: 2rem;}
This approach eliminates manual class management or JavaScript intervention for layout adjustments.
The :has()
selector fundamentally changes CSS architecture by enabling content-aware styling without JavaScript. Its ability to style parent elements based on their children opens up robust, maintainable approaches to common layout challenges.
Modern browsers support :has()
well, making it production-ready for contemporary web development. For older browsers, implement fallback styles:
/* Base styles work everywhere */.card { padding: 1rem; }
/* Enhanced styles for browsers supporting :has() */@supports selector(:has(*)) { .card:has(img) { padding: 0; }}
My tip: Start small - replace JavaScript-based style toggles with :has()
selectors. Move gradually to content-aware layouts.