Stackvora.tech

Modern CSS in 2026: Container Queries, :has, Nesting

The landscape of CSS development has evolved significantly, offering developers more powerful and intuitive tools for crafting responsive and maintainable…

The landscape of CSS development has evolved significantly, offering developers more powerful and intuitive tools for crafting responsive and maintainable user interfaces. As we look towards 2026, several key features have cemented their place as essential components of modern CSS workflows, moving beyond experimental flags into widespread browser adoption. This article explores three such pillars: Container Queries, the :has() pseudo-class, and native CSS Nesting, detailing their practical applications and how they collectively simplify complex styling challenges.

These features enable more modular, component-driven styling, reducing the reliance on JavaScript for layout logic and minimizing the need for complex, fragile media queries. Understanding their proper implementation is crucial for any developer aiming to build robust and scalable frontend architectures.

Container Queries: Element-Based Responsiveness

Traditional responsive design relies heavily on viewport-based media queries (e.g., @media (min-width: 768px)), which dictate styles based on the browser window size. While effective for global layout changes, this approach becomes cumbersome and often impractical for component-level responsiveness. Container Queries, standardized under the CSS Containment Module Level 3, solve this by allowing elements to query the characteristics of their nearest ancestor with an established containment context, enabling truly independent, self-adapting components.

To use container queries, you must first establish a containment context on a parent element. This is typically done using the container-type and optionally container-name properties.

Establishing Containment Context

  • container-type: size: Queries can be based on the inline size (width), block size (height), or both. This is the most common and powerful type.
  • container-type: inline-size: Queries only based on the inline dimension (width in a left-to-right writing mode).
  • container-name: : Assigns a name to the container, allowing for more specific targeting with @container (query). This prevents unintended queries if you have multiple nested containers.

Once a container is established, you use the @container at-rule, similar to @media, but with queries against the container's dimensions.


.card-wrapper {
    container-type: inline-size;
    container-name: card-container; /* Optional, but recommended for clarity */
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: 1.5rem;
    padding: 1rem;
}

.product-card {
    border: 1px solid #ccc;
    padding: 1rem;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

/* Style the product-card content based on its parent's width */
@container card-container (min-width: 400px) {
    .product-card {
        flex-direction: row;
        align-items: center;
    }
    .product-card img {
        max-width: 150px;
        margin-right: 1rem;
    }
}

@container card-container (max-width: 300px) {
    .product-card h3 {
        font-size: 0.9em;
    }
    .product-card .price {
        font-size: 0.8em;
    }
}

In this example, the .product-card adjusts its layout and font sizes not based on the viewport width, but based on the actual width available to its parent .card-wrapper. This is invaluable for component libraries where components might appear in various layout contexts (e.g., a sidebar, a main content area, a modal).

The :has() Pseudo-Class: The "Parent Selector" and Beyond

For years, CSS developers have longed for a "parent selector" – a way to style an element based on its children. The :has() pseudo-class, part of CSS Selectors Level 4, finally delivers this capability and much more. It matches an element if any of the selectors passed into :has() matches at least one element when starting from the element itself and "looking inside" its descendants.

The syntax is straightforward: A:has(B) selects element A if it contains element B. This opens up a vast array of styling possibilities that previously required JavaScript or redundant classes.

Common Use Cases for :has()

  • Styling a parent based on its child's state:
  • 
    /* Highlight a form field's label when its input is focused */
    .form-group:has(input:focus) label {
        color: var(--primary-color);
        font-weight: bold;
    }
    
    /* Style a list item differently if it contains an image */
    li:has(img) {
        background-color: #f0f0f0;
        padding: 0.5rem;
    }
        
  • Styling siblings based on a common parent's child:
  • 
    /* If a card has an alert, make the adjacent footer sticky */
    .card:has(.alert) + .card-footer {
        position: sticky;
        bottom: 0;
        background: yellow;
        padding: 0.5rem;
    }
        
  • Conditional Layouts without JavaScript:
  • 
    /* Grid layout with a sidebar: if the main content area has a `.promo` element,
       adjust the grid template to accommodate it differently. */
    .layout:has(.main-content .promo) {
        grid-template-areas: "header header"
                             "nav promo"
                             "main promo"
                             "footer footer";
    }
        
  • Styling based on element count:
  • 
    /* Style a flex container differently if it has exactly 3 children */
    .gallery:has(li:nth-child(3):last-child) {
        justify-content: center;
    }
        

The :has() pseudo-class significantly reduces the need for "component modifier" classes managed by JavaScript, leading to cleaner markup and more robust, declarative styling. Browser support is strong across all major engines (Chrome 105+, Firefox 109+, Safari 16.4+).

Native CSS Nesting: Simplified Stylesheet Structure

For years, CSS preprocessors like Sass and Less have offered nesting as a core feature, allowing developers to write more organized and readable stylesheets by grouping related rules. Native CSS Nesting (CSS Nesting Module Level 1) brings this capability directly to the browser, eliminating the build step requirement for this specific feature and streamlining development workflows.

The syntax for native nesting is similar to preprocessors, using an ampersand (&) to refer to the parent selector.

Key Features and Syntax

  • Basic Element Nesting:
  • 
    .card {
        background-color: white;
        border-radius: 8px;
        box-shadow: 0 2px 4px rgba(0,0,0,0.1);
        padding: 1.5rem;
    
        h3 {
            color: #333;
            margin-bottom: 0.5rem;
        }
    
        p {
            color: #666;
            line-height: 1.6;
        }
    }
        
  • Nesting Pseudo-classes and Pseudo-elements:
  • 
    .button {
        background-color: var(--primary-color);
        color: white;
        padding: 0.8rem 1.2rem;
        border: none;
        border-radius: 4px;
        cursor: pointer;
    
        &:hover {
            background-color: var(--primary-dark-color);
        }
    
        &::before {
            content: "✨ ";
        }
    }
        
  • Nesting Media and Container Queries:
  • This is particularly powerful, allowing queries to be contextually defined within the component's styles.

    
    .header {
        background-color: #f8f8f8;
        padding: 1rem 2rem;
        display: flex;
        justify-content: space-between;
        align-items: center;
    
        .logo {
            font-size: 1.8rem;
            font-weight: bold;
        }
    
        nav {
            ul {
                list-style: none;
                padding: 0;
                display: flex;
                gap: 1rem;
            }
    
            a {
                text-decoration: none;
                color: #333;
            }
        }
    
        /* Nested media query */
        @media (max-width: 768px) {
            flex-direction: column;
            align-items: flex-start;
    
            nav {
                margin-top: 1rem;
            }
        }
    
        /* Nested container query (if .header had container-type set) */
        @container (max-width: 400px) {
            .logo {
                font-size: 1.5rem;
            }
        }
    }
        

Browser support for native CSS Nesting is now widely available (Chrome 112+, Firefox 117+, Safari 16.5+). It's important to note that the current specification requires a qualifying rule before the opening brace of a nested style rule, which means nested tag names or attribute selectors must be prefixed with & (e.g., & input {}). However, this specific restriction is being relaxed in upcoming revisions (often referred to as "relaxed parsing" or "loose parsing") to allow more intuitive nesting like div {} directly inside a parent rule.

Synergies and Advanced Patterns

The real power of these features emerges when they are used in conjunction. Consider a complex card component that needs to adapt not only to its container's size but also based on the presence of certain internal elements and user interaction states.


.card-grid {
    container-type: inline-size;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
    gap: 1.5rem;
}

.product-item {
    border: 1px solid #ddd;
    border-radius: 8px;
    padding: 1rem;
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    transition: all 0.2s ease-in-out;

    /* Nesting */
    h3 {
        font-size: 1.25rem;
        color: #222;
        margin-bottom: 0.25rem;
    }

    .price {
        font-weight: bold;
        color: #007bff;
    }

    .add-to-cart {
        background-color: #28a745;
        color: white;
        padding: 0.6rem 1rem;
        border-radius: 4px;
        text-align: center;
        &:hover { /* Nested pseudo-class */
            background-color: #218838;
        }
    }

    /* :has() to style the item if it's out of stock */
    &:has(.badge.out-of-stock) {
        opacity: 0.7;
        pointer-events: none; /* Make it unclickable */
        .add-to-cart {
            background-color: #6c757d;
            cursor: not-allowed;
            &:hover {
                background-color: #6c757d; /* No hover effect */
            }
        }
    }

    /* Container Query for item layout */
    @container (min-width: 400px) {
        flex-direction: row;
        align-items: center;
        .product-image {
            max-width: 120px;
            margin-right: 1rem;
        }
        .details {
            flex-grow: 1;
        }
    }

    /* Combining :has() and container queries */
    @container (max-width: 300px):has(.badge.discount) {
        /* If item is small AND has a discount, make the discount badge more prominent */
        .badge.discount {
            font-size: 0.9em;
            padding: 0.2em 0.5em;
            background-color: orange;
            color: white;
            border-radius: 3px;
        }
    }
}

This example demonstrates how a .product-item component can dynamically reconfigure itself based on its available container width (via @container), indicate its interactive state based on internal content (e.g., .badge.out-of-stock via :has()), and keep its styles organized and readable through native nesting. This significantly reduces the complexity of managing responsive behaviors across a large design system.

Common Pitfalls and Troubleshooting

  • Containment Context for Container Queries: For @container to work, the parent element must explicitly define a container-type. Forgetting this is the most common error. Use browser developer tools to inspect the computed styles of your container to confirm container-type is applied.
  • :has() Performance: While highly optimized, very complex :has() selectors (e.g., div:has(> section:has(> ul:has(> li:has(a:hover))))) can theoretically have performance implications. Profile your styles if you notice jank, but for typical use cases, it's not an issue.
  • Native Nesting Syntax Quirks: Early implementations of native nesting (pre-relaxed parsing) might require the & for non-element selectors (e.g., &.modifier, &::pseudo). Be aware of your target browser versions. As of late 2024, most evergreen browsers support the more relaxed syntax where .modifier {} or div {} can be nested directly. If you encounter issues, prepend the &.
  • Specificity and Source Order: These new features don't change how CSS specificity or the cascade work. Nested rules still follow the standard cascade rules based on their expanded, non-nested form. Be mindful of selector specificity when combining these features.
  • Debugging: Browser developer tools have caught up quickly. Inspecting elements will show you which container queries are active and why, and the rules applied by :has() or nested selectors are generally clearly indicated.

Back to the knowledge base · Ask the AI assistant