🌞
CSS 2024

The :has() Selector

Finally, a parent selector in CSS! Style elements based on what they contain. It's like magic, but better - it's native CSS.

Chrome 105+ Firefox 121+ Safari 15.4+ Edge 105+

What is :has()?

The :has() pseudo-class lets you select an element if it contains another element that matches a selector. Think of it as "select a parent that has a specific child" - something we've been dreaming about for years!

❌ The Old Way

// JavaScript required!
const cards = document.querySelectorAll('.card');
cards.forEach(card => {
    if (card.querySelector('.badge')) {
        card.classList.add('has-badge');
    }
});

✅ The New Way

/* Pure CSS! */
.card:has(.badge) {
    border: 2px solid gold;
    background: linear-gradient(
        to right, 
        rgba(255, 215, 0, 0.1), 
        transparent
    );
}

Interactive Examples

Form Validation Styling

Please enter a valid email
Password is required

Try typing to see validation in action!

/* Show error only when input is invalid */
.form-field:has(input:invalid:not(:placeholder-shown)) {
    .error-message {
        display: block;
        color: #ef4444;
    }
    
    input {
        border-color: #ef4444;
    }
}

/* Green tick for valid inputs */
.form-field:has(input:valid:not(:placeholder-shown))::after {
    content: '✓';
    color: #10b981;
    position: absolute;
    right: 1rem;
    top: 2.5rem;
}

Card with Badge

NEW

Premium CSS Course

Master modern CSS with this brilliant course

CSS Basics

Start your journey with the fundamentals

SALE

Advanced Techniques

Take your skills to the next level

/* Style cards that contain badges */
.product-card:has(.badge) {
    border: 2px solid var(--badge-colour);
    position: relative;
    overflow: visible;
}

/* Different styles for different badges */
.product-card:has(.badge.new) {
    --badge-colour: #10b981;
    background: linear-gradient(135deg, 
        rgba(16, 185, 129, 0.1) 0%, 
        transparent 50%);
}

.product-card:has(.badge.sale) {
    --badge-colour: #ef4444;
    animation: pulse 2s infinite;
}

Navigation with Dropdown

/* Style nav items that have dropdowns */
nav li:has(.dropdown) {
    position: relative;
}

/* Add dropdown arrow to items with submenus */
nav li:has(.dropdown) > a::after {
    content: ' ▾';
    font-size: 0.8em;
}

/* Show dropdown on hover */
nav li:has(.dropdown):hover .dropdown {
    display: block;
    animation: slideDown 0.3s ease-out;
}

Image Gallery with Captions

/* Style figures that have captions differently */
.gallery-item:has(figcaption) {
    background: var(--colour-surface);
    border-radius: var(--radius-lg);
    overflow: hidden;
    box-shadow: var(--shadow-md);
}

.gallery-item:not(:has(figcaption)) {
    border-radius: var(--radius-md);
    opacity: 0.9;
}

/* Blur images without captions on hover */
.gallery-grid:hover .gallery-item:not(:has(figcaption)) {
    filter: blur(2px);
    opacity: 0.7;
}

Article with Media

CSS Grid: The Complete Guide

Everything you need to know about CSS Grid in one comprehensive guide.

Flexbox in 5 Minutes

Quick video tutorial on mastering Flexbox layouts.

CSS Art

Creating Art with Pure CSS

Discover how to create stunning visuals using only CSS.

/* Different layouts for articles with media */
.blog-article:has(img, video) {
    display: grid;
    grid-template-columns: 200px 1fr;
    gap: 1.5rem;
    align-items: start;
}

.blog-article:has(video) {
    background: linear-gradient(
        135deg, 
        rgba(147, 51, 234, 0.1) 0%, 
        transparent 50%
    );
    border-left: 4px solid #9333ea;
}

/* Articles without media stay simple */
.blog-article:not(:has(img, video)) {
    padding: 1.5rem;
    background: var(--colour-surface);
}

Advanced Patterns

Sibling Combinations

/* Style when followed by specific element */
h2:has(+ p) {
    margin-bottom: 0.5rem;
}

/* Grid when contains both images and text */
.container:has(img):has(p) {
    display: grid;
    grid-template-columns: 1fr 1fr;
}

Quantity Queries

/* Different layout based on child count */
.gallery:has(> :nth-child(5)) {
    grid-template-columns: repeat(3, 1fr);
}

/* Single column if only one child */
.list:has(> :only-child) {
    max-width: 600px;
    margin: 0 auto;
}

State-based Styling

/* Highlight row with checked checkbox */
tr:has(input:checked) {
    background: rgba(16, 185, 129, 0.1);
}

/* Disable form if any field is invalid */
form:has(input:invalid) button[type="submit"] {
    opacity: 0.5;
    cursor: not-allowed;
}

Browser Support & Fallbacks

Browser Version Status
Chrome/Edge 105+ ✅ Fully Supported
Firefox 121+ ✅ Fully Supported
Safari 15.4+ ✅ Fully Supported
Mobile Browsers Latest ✅ Widely Supported

Progressive Enhancement

/* Base styles for all browsers */
.card {
    border: 1px solid #e5e7eb;
}

/* Enhancement for browsers with :has() support */
@supports selector(:has(*)) {
    .card:has(.premium-badge) {
        border-color: gold;
        background: linear-gradient(
            135deg, 
            rgba(255, 215, 0, 0.1) 0%, 
            transparent 50%
        );
    }
}

Pro Tips

Performance

Keep :has() selectors simple. Complex selectors can impact performance, especially with many elements.

🎯

Specificity

:has() doesn't increase specificity by itself - it's the selectors inside that count.

🔍

Testing

Always test in browsers that don't support :has() to ensure graceful degradation.

Tea Break Tip

Like a good cuppa, :has() makes everything better. Use it wisely and your CSS will thank you!

Ready for More Modern CSS?

The :has() selector is just the beginning. Explore more cutting-edge CSS features!