As web development evolves, CSS continues to introduce powerful selectors that make our stylesheets more efficient, readable, and maintainable. Today, let's dive deep into four modern CSS selectors that are revolutionizing how we write styles: :is()
, :has()
, :where()
, and :not()
.
1. The :is() Selector - Simplifying Complex Selectors
What is :is()?
The :is()
pseudo-class selector allows you to group multiple selectors together, reducing repetition and improving code maintainability.
Syntax
:is(selector1, selector2, selector3) {
/* styles */
}
Use Cases and Examples
Before :is() (Traditional Approach):
h1 a,
h2 a,
h3 a,
h4 a,
h5 a,
h6 a {
color: blue;
text-decoration: none;
}
.sidebar p,
.content p,
.footer p {
line-height: 1.6;
}
After :is() (Modern Approach):
:is(h1, h2, h3, h4, h5, h6) a {
color: blue;
text-decoration: none;
}
:is(.sidebar, .content, .footer) p {
line-height: 1.6;
}
Benefits Compared to Other Approaches:
- Reduced Code Duplication: Write less CSS with cleaner syntax
- Better Maintainability: Add or remove selectors in one place
- Improved Readability: Clearer intent and structure
- Forgiving Selector List: Invalid selectors don't break the entire rule
2. The :has() Selector - The "Parent Selector" Revolution
What is :has()?
The :has()
pseudo-class selects elements that contain specific child elements or match certain conditions. It's often called the "parent selector" because it can style parents based on their children.
Syntax
parent:has(child) {
/* styles */
}
Use Cases and Examples
Styling Cards Based on Content:
/* Style cards differently if they contain images */
.card:has(img) {
border: 2px solid gold;
padding: 20px;
}
/* Style articles that have both headings and videos */
article:has(h2):has(video) {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
Form Enhancement:
/* Style form groups that contain invalid inputs */
.form-group:has(input:invalid) {
border-left: 4px solid red;
}
/* Style labels when their associated input is focused */
label:has(+ input:focus) {
color: blue;
font-weight: bold;
}
Navigation States:
/* Highlight navigation sections with active links */
nav:has(a.active) {
background-color: #f0f8ff;
}
Benefits Compared to JavaScript Solutions:
- Pure CSS Solution: No JavaScript required for parent-child relationships
- Performance: CSS-native implementation is faster than DOM traversal
- Declarative: More intuitive than imperative JavaScript code
- Real-time: Automatically updates when DOM changes
3. The :where() Selector - Specificity Control
What is :where()?
The :where()
pseudo-class functions similarly to :is()
but with zero specificity, making it perfect for creating default styles that are easy to override.
Syntax
:where(selector1, selector2, selector3) {
/* styles with zero specificity */
}
Use Cases and Examples
Creating Overridable Default Styles:
/* Reset styles with zero specificity */
:where(h1, h2, h3, h4, h5, h6) {
margin: 0;
font-weight: normal;
}
/* These can be easily overridden */
h1 {
font-weight: bold; /* This will override the :where() rule */
}
Component Library Development:
/* Library default styles (easy to override) */
:where(.btn) {
padding: 8px 16px;
border: 1px solid #ccc;
background: white;
}
/* User can override without !important */
.my-custom-btn {
background: blue; /* This works without specificity wars */
}
Benefits Compared to Other Approaches:
- Specificity Management: Prevents specificity wars in large applications
- Framework-Friendly: Perfect for CSS frameworks and component libraries
- Future-Proof: Makes styles more maintainable and extensible
- Clean Overrides: No need for
!important
to override default styles
4. The :not() Selector - Precise Exclusions
What is :not()?
The :not()
pseudo-class selects elements that do not match specific selectors, allowing for precise exclusions.
Syntax
:not(selector) {
/* styles */
}
Enhanced Modern Syntax (Level 4):
:not(selector1, selector2, selector3) {
/* styles */
}
Use Cases and Examples
Styling All But Specific Elements:
/* Style all buttons except the primary one */
button:not(.primary) {
background: gray;
color: white;
}
/* Style all list items except the first and last */
li:not(:first-child, :last-child) {
border-bottom: 1px solid #eee;
}
Form Styling:
/* Style all inputs except checkboxes and radios */
input:not([type="checkbox"], [type="radio"]) {
padding: 8px;
border: 1px solid #ccc;
}
Layout Exceptions:
/* Add margin to all sections except those with .no-margin */
section:not(.no-margin) {
margin-bottom: 2rem;
}
Benefits Compared to Override Approaches:
- Cleaner Code: No need to apply styles and then override them
- Better Performance: More efficient than applying and removing styles
- Logical Structure: Code intent is clearer and more semantic
- Reduced CSS Bloat: Fewer override rules needed
Combining Selectors - The Real Power
The true magic happens when you combine these selectors:
/* Complex but readable selector combinations */
:is(.sidebar, .footer):has(h3):where(:not(.minimal)) {
border: 2px solid #ddd;
padding: 1rem;
}
/* Style form sections that have invalid inputs but aren't disabled */
.form-section:has(input:invalid):not(.disabled) {
background-color: #fee;
}
Browser Support and Progressive Enhancement
- :is(): Excellent support across modern browsers
- :has(): Good support in modern browsers (Chrome 105+, Firefox 103+, Safari 15.4+)
- :where(): Excellent support in modern browsers
- :not(): Universal support (enhanced syntax in modern browsers)
Best Practices
- Use :is() for grouping when you need normal specificity
- Use :where() for defaults that should be easily overridable
- Use :has() for parent-child relationships instead of JavaScript
- Use :not() for exclusions rather than override patterns
- Combine selectors thoughtfully - readability matters
Conclusion
These modern CSS selectors represent a significant leap forward in styling capabilities. They reduce code duplication, improve maintainability, and enable styling patterns that previously required JavaScript. By mastering :is()
, :has()
, :where()
, and :not()
, you'll write more efficient, readable, and maintainable CSS.