17-01-2024
Deep Dive into CSS :has Selector
Read on dev.toIn the ever-evolving landscape of web development, CSS continues to offer powerful tools that significantly enhance the capability of web designers and developers. A prime example of this progress is the introduction of the :has
selector. Part of the CSS Selectors Level 4 specification, :has brings a new paradigm to CSS, allowing developers to select and style elements in a more contextual and conditional manner.
Technical Overview of :has
The :has
selector is a relational pseudo-class that matches elements based on the presence of specified descendants. Unlike traditional selectors, :has
looks ahead in the DOM structure, enabling parent selectors based on child or descendant elements.
Syntax
The syntax of the :has
selector is as follows:
element:has(selector) {
/* styles */
}
The selector
inside :has()
can be a combination of any valid CSS selector, including class, ID, attribute, pseudo-class, and pseudo-element selectors.
Use Cases and Examples
Responsive Design
.card {
display: grid;
grid-template-columns: 1fr;
gap: 10px;
border: 1px solid #ccc;
padding: 10px;
}
/* Adjust the layout of a card if it contains an image */
.card:has(> img) {
grid-template-columns: 1fr 2fr;
}
<div class="card">
<img src="path_to_image.jpg" alt="Image Description" />
<div>
<h3>Card Title</h3>
<p>Some content here...</p>
</div>
</div>
<!-- Example without an image -->
<div class="card">
<div>
<h3>Another Card Title</h3>
<p>Some other content here...</p>
</div>
</div>
This example demonstrates the ability of :has
to create more responsive and adaptive design, especially useful in scenarios like card layouts where the presence of an image can dictate the layout structure.
Typicallky before :has
we would have to implement more selector rules and say something like class="card card--two-column"
but now instead of adapting out html markup and our css to cater for the varied content we only have to update our css and it can essentially ifgure out what the content is and apply the appropriate styling!
Interactive Content
.menu-item {
background-color: white;
padding: 10px;
border: 1px solid black;
}
/* Style a menu item differently if it contains a submenu */
.menu-item:has(.submenu) {
background-color: lightcoral;
}
.submenu {
display: none;
padding: 5px;
border: 1px solid grey;
}
.menu-item:hover .submenu {
display: block;
}
<ul>
<li class="menu-item">Home</li>
<li class="menu-item">About</li>
<li class="menu-item">
Services
<ul class="submenu">
<li>Web Design</li>
<li>Web Development</li>
</ul>
</li>
<li class="menu-item">Contact</li>
</ul>
The :has
selector can significantly simplify the styling of complex navigation menus, particularly when dealing with nested or dynamic content.
Similar to the previous example before :has
we would have to apply another modifier to menu-item to achieve this.
I have faced issues in the past where I read files from the file system and dynamically generate the navigation. I have had to add logic to the top level <li class="menu-item">
to check if it contains a nested ul
and then if it does apply another class making it <li class="menu-item menu-item--with-children">
. Using this example you can skip writing all the logic to do this and save computing that value with javascript, a nice efficiency win, and a marked improvement on readability for your html markup!
Form Field Validation
/* Highlight a field with invalid input */
input:invalid:has(+ .error-message) {
border-color: red;
}
<form>
<input type="email" placeholder="Enter your email" required />
<span class="error-message">Please enter a valid email address.</span>
<br />
<button type="submit">Submit</button>
</form>
The combination of :has
with other pseudo-classes like :invalid
can enhance form validation feedback, improving user experience.
Typically, looking at react for this example we would have had to invalidate the field and conditionally apply a class to the input or wrap it in another dive and conditionall aplly a class to that. meaining more DOM soup, here we dont need any of that.
The only dynamic thing now would be showing the error, and if that error is visible, we have a appripriately styled input.
Browser Compatibility and Modern Web Practices
As of 2024, the :has
selector enjoys support across major modern browsers, including the latest versions of Chrome, Firefox, Safari, and Edge. The growing trend towards modern web practices and the phasing out of legacy browsers like Internet Explorer has paved the way for the adoption of advanced CSS features.
Emphasising the Shift from Internet Explorer
With the end of Internet Explorer's lifecycle, web developers are no longer constrained by its limitations. This transition allows the full utilisation of modern CSS capabilities, such as the :has
selector, which are unsupported in Internet Explorer.
Technical Considerations
Performance Implications
While the :has
selector opens new avenues in CSS, it's essential to consider its impact on rendering performance. Since :has
is more complex than traditional selectors, it can introduce performance overhead, especially when used extensively or with complex descendant selectors. Developers should balance the use of :has
with performance best practices.
But I'm going to take a stab in the dark here and say that this is more than likely going to be much quicker than writing javascript to achieve the same effect!
Cascading and Specificity
The :has
selector follows the standard rules of cascading and specificity in CSS. Its specificity is calculated based on the compound selectors within the :has()
function. Understanding this is crucial for avoiding unexpected styling conflicts.
Final caveat
ALWAYS check your own browser stats! I have generalised a lot here about the support. Yes it may be fully supported in all modern browsers, but, is that what your users use? before adopting anything make sure that it is supported by your predefined thresholds of support! check out Can I Use for more info on the support!
Conclusion
The CSS :has
selector marks a significant milestone in the evolution of CSS, offering developers more sophisticated tools for styling web pages. Its ability to select parent elements based on child conditions is a powerful addition to the CSS toolkit. With broad browser support and the shift away from legacy browsers, the :has
selector is poised to become a staple in modern web design. As with any advanced feature, it should be used judiciously, with a mindful approach to performance and specificity.