Color Mode

Container Queries and Feature Detection

As CSS continues to evolve, new features are constantly being added that change how we approach responsive design and progressive enhancement. Two important capabilities you need to understand are container queries, which allow components to respond to their container size rather than the viewport, and feature queries with @supports, which let you detect browser support for CSS features before using them. Together, these tools enable you to write modern, flexible CSS that works across all browsers.

What are Container Queries?

Traditional media queries respond to the viewport size, which works well for page-level layouts but creates problems for modular components. A card component might look perfect in a wide sidebar but break when placed in a narrow column, even though both contexts exist on the same viewport width. Container queries solve this by allowing elements to respond to the size of their container instead of the viewport, enabling truly reusable components that adapt based on the space available to them.

Setting Up Container Queries

To use container queries, you must first establish a container by setting the container-type property on a parent element. The most common value is inline-size, which means the container tracks its width (or inline dimension in logical properties). You can also give the container a name using container-name, which is helpful when you have nested containers or want to be explicit about which container you are querying.


        .card-container {
            container-type: inline-size;
            container-name: card;
        }
    

The shorthand property container combines both values, with the name first and the type second:


        .card-container {
            container: card / inline-size;
        }
    

Writing Container Queries

Once you have established a container, you use @container queries to apply styles based on that container's size. The syntax is similar to media queries, but instead of @media, you use @container. You can reference the container by name or query the nearest ancestor container.


        /* Query by container name */
        @container card (min-width: 400px) {
            .card-content {
                display: grid;
                grid-template-columns: 200px 1fr;
            }
        }

        /* Query nearest container (no name specified) */
        @container (min-width: 600px) {
            .card-header {
                font-size: 2rem;
            }
        }
    

Practical Container Query Example

Here is a complete example showing how a card component can adapt to different container sizes:


        .card-container {
            container: card / inline-size;
        }

        .card {
            padding: 1rem;
            background: var(--surface);
        }

        .card-content {
            display: flex;
            flex-direction: column;
            gap: 0.5rem;
        }

        @container card (min-width: 400px) {
            .card-content {
                flex-direction: row;
                align-items: center;
                gap: 1rem;
            }

            .card-image {
                width: 150px;
                flex-shrink: 0;
            }
        }

        @container card (min-width: 600px) {
            .card {
                padding: 2rem;
            }

            .card-content {
                gap: 2rem;
            }

            .card-image {
                width: 200px;
            }
        }
    

Notice how the card component responds to its container size, not the viewport. This means the same card component will work correctly whether it is placed in a wide main content area, a narrow sidebar, or a grid layout, automatically adapting to the space available.

Container Query Length Units

The CSS Containment spec also defines container query length units: cqw, cqh, cqi, cqb, cqmin, and cqmax. These units are a percentage of the query container’s size (e.g. cqi is 1% of the container’s inline size), so you can size typography, spacing, or other values relative to the container instead of the viewport. While @container is widely supported now, some advanced features like these length units have not been consistently implemented across browsers.

Use @supports or check current compatibility before relying on container length units in production. For the latest spec and implementation status, see the CSS Containment Module Level 3 draft.

Feature Queries with @supports

Container queries are powerful, but they are a relatively new CSS feature. Not all browsers support them yet, which is where @supports becomes essential. The @supports rule allows you to detect whether a browser understands a specific CSS property and value combination before you use it, enabling you to write progressive enhancement strategies directly in your CSS.

Basic @supports Syntax

The @supports rule works similarly to media queries, but instead of checking viewport dimensions, it checks feature support. Wrap the property-value pair in parentheses:


        @supports (display: grid) {
            .container {
                display: grid;
                grid-template-columns: repeat(3, 1fr);
            }
        }
    

If the browser supports the feature, the styles inside the @supports block apply. If not, the browser ignores the entire block, making it safe to use without breaking older browsers.

Logical Operators

You can combine multiple feature checks using logical operators: and, or, and not.


        /* Check for multiple features with AND */
        @supports (display: grid) and (gap: 1rem) {
            .layout {
                display: grid;
                gap: 1rem;
            }
        }

        /* Check for either feature with OR */
        @supports (backdrop-filter: blur(10px)) or (-webkit-backdrop-filter: blur(10px)) {
            .glass-effect {
                backdrop-filter: blur(10px);
            }
        }

        /* Check that a feature is NOT supported */
        @supports not (display: grid) {
            .container {
                display: flex;
                flex-wrap: wrap;
            }
        }
    

Progressive Enhancement with Container Queries

Now that you understand both container queries and @supports, you can combine them to create a robust progressive enhancement strategy. Start with a solid baseline that works everywhere using traditional media queries, then layer on container queries for browsers that support them.


        /* Base styles that work everywhere */
        .card {
            padding: 1rem;
            background: var(--surface);
        }

        .card-content {
            display: flex;
            flex-direction: column;
            gap: 0.5rem;
        }

        /* Fallback using viewport-based media queries */
        @media (min-width: 768px) {
            .card-content {
                flex-direction: row;
                align-items: center;
            }
        }

        /* Enhanced with container queries where supported */
        @supports (container-type: inline-size) {
            .card-container {
                container: card / inline-size;
            }

            /* Reset the media query styles */
            .card-content {
                flex-direction: column;
            }

            /* Apply container-based responsive styles */
            @container card (min-width: 400px) {
                .card-content {
                    flex-direction: row;
                    align-items: center;
                    gap: 1rem;
                }
            }

            @container card (min-width: 600px) {
                .card {
                    padding: 2rem;
                }

                .card-content {
                    gap: 2rem;
                }
            }
        }
    

This approach ensures your site works for everyone. Browsers without container query support use the media query fallback and get a perfectly functional responsive design. Browsers with container query support get the enhanced component-based responsive behavior. Both experiences are good, but the enhanced version provides better modularity and reusability.

Other Common @supports Use Cases

While container queries are an excellent example of when to use @supports, many other modern CSS features benefit from feature detection. Here are patterns you will encounter frequently in modern CSS development.

CSS Nesting

Native CSS nesting is now supported in modern browsers, but older browsers still need separate rule declarations.


        /* Fallback with separate rules */
        .navigation {
            background: var(--nav-bg);
        }

        .navigation a {
            color: var(--nav-link);
            padding: 0.5rem 1rem;
        }

        .navigation a:hover {
            background: var(--nav-hover);
        }

        /* Enhanced with native nesting where supported */
        @supports (selector(&)) {
            .navigation {
                background: var(--nav-bg);

                & a {
                    color: var(--nav-link);
                    padding: 0.5rem 1rem;

                    &:hover {
                        background: var(--nav-hover);
                    }
                }
            }
        }
    

Subgrid

CSS Grid's subgrid value allows nested grids to align with their parent's grid tracks, but browser support is still growing.


        .product-list {
            display: grid;
            grid-template-columns: repeat(3, 1fr);
            gap: 2rem;
        }

        /* Base product card layout */
        .product-card {
            display: grid;
            grid-template-rows: auto 1fr auto;
            gap: 1rem;
        }

        /* Enhanced alignment with subgrid */
        @supports (grid-template-columns: subgrid) {
            .product-card {
                grid-template-rows: subgrid;
                grid-row: span 3;
            }
        }
    

Modern Color Functions

Functions like color-mix(), oklch(), and oklab() provide powerful color manipulation capabilities but require fallbacks.


        .button {
            /* Fallback using standard custom properties */
            background: var(--button-bg);
            color: var(--button-text);
        }

        .button:hover {
            background: var(--button-bg-hover);
        }

        /* Enhanced with color-mix() for dynamic shades */
        @supports (background: color-mix(in oklch, red, blue)) {
            .button {
                background: var(--button-bg);
                color: var(--button-text);
            }

            .button:hover {
                background: color-mix(in oklch, var(--button-bg) 80%, black 20%);
            }
        }
    

The Ever-Evolving Nature of CSS

CSS is not a static language. The CSS Working Group continuously develops new features, and browser vendors implement them at different speeds. This creates an opportunity: you can start using bleeding-edge features immediately if you plan appropriate fallbacks with @supports. Your code remains functional for everyone while progressively enhancing the experience for users with modern browsers.

This means your @supports checks are not permanent. A feature that requires detection today might be universally supported in a year or two. As browser support improves, you can gradually simplify your code by removing @supports blocks and promoting the enhanced styles to your base layer. This is part of the natural lifecycle of progressive enhancement.

Checking Browser Support

Before using @supports, check current browser compatibility for the feature you are detecting. Two excellent resources for this are caniuse.com, which provides clear browser support tables and usage statistics, and MDN Web Docs, which includes compatibility data alongside feature documentation. MDN is particularly useful because it reflects the latest updates and provides detailed information about feature implementations across browsers.

Practical Considerations

While @supports is a powerful tool, it should be used thoughtfully. Not every CSS property needs feature detection. Widely supported features like Flexbox, basic Grid, and custom properties can be used without @supports checks in most modern development contexts. @container can be used without @supports as long as newer features such as container query length units (cqi, cqw, etc.) are not required. Save @supports for features where browser support is genuinely inconsistent or where you are adopting cutting-edge capabilities before they achieve universal support.

When working with a team, document your @supports usage in your CSS comments or style guide. Future developers will appreciate knowing why certain features have detection checks and what the fallback strategy entails. This documentation becomes especially important as browser support evolves and you need to decide when to remove the feature queries.


        /**
         * Container Queries - Added March 2024
         * Fallback: Media queries (viewport-based)
         * Remove @supports when baseline support reaches 95%
         * Check: https://caniuse.com/css-container-queries
         */
        @supports (container-type: inline-size) {
            /* Container query styles */
        }
    

Key Concepts

Container queries represent a fundamental shift in how we approach responsive design, allowing components to respond to their container size rather than the viewport. This enables truly modular, reusable components that adapt to the space available to them. Feature queries with @supports let you adopt these modern capabilities safely by detecting browser support before using them and providing appropriate fallbacks for browsers that are not ready yet. Together, these tools enable you to write forward-compatible CSS that takes advantage of cutting-edge features while maintaining solid fallbacks. Remember that @supports checks are part of your progressive enhancement strategy: establish a working baseline, then layer on enhancements where supported. As CSS continues to evolve and browser support improves, your feature queries will naturally become simpler over time, eventually disappearing as features achieve universal support.