Responsive design evolved: exploring the power of CSS @container queries

Responsive design evolved: exploring the power of CSS @container queries

Let’s check out an exciting development in the world of web design – CSS container queries. We all know that relying on the size of the viewport has been a part of responsive web design (RWD) for quite some time. But as RWD and modular layouts evolve, new requirements have emerged that traditional CSS media queries can’t fully address. So what’s the solution? Enter container queries, a native CSS technology that’s about to revolutionize responsive web design.

Syntax and functionality of container queries

So what exactly are container queries? Well, they allow us to define custom breakpoints for individual components. Instead of relying solely on the width or height of the entire browser window, components can now react based on the size of their parent container. This separation of page layout and component layout brings simplicity and cleaner code to the table.

Let’s take a look at the basic syntax using the @container rule. Let’s say we have an element whose background we want to change from black to white when its container (parent) exceeds 512 pixels in size. Here’s a snippet of code:

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

.child {
    background: #000;
}

@container (min-inline-size: 512px) {
    .child {
        background: #fff;
    }
}

In this example, the .container represents the parent element we want to target. By using the container-type: inline-size property, we indicate that the width of the container is what matters for our adjustment.

Now, you may be wondering about the values and syntax involved. The delcaration container-type: inline-size tells the browser that the container can change its size on the inline axis – in our example, its width. Among other things, there is also the size value, which can be used for the combination of block and inline size. The exact specifications are based on the CSS logical properties and depend on the reading direction of the layout. Also, the container media query is not defined with min-width (which still would work tho), but with min-inline-size. It’s a good idea to familiarize yourself with CSS local properties if you haven’t already, because we’ll be encountering them more and more.

And if you want to disable or reset a container query, you can just use a normal value. In the future, we may even see style queries in addition to the size queries we’ve explored so far.

Example of a multi-column layout with CSS container queries

Now let’s dive into a practical example that combines container queries with a multi-column layout. Here’s the HTML structure we’ll be working with:

<div class="wrapper">
    <div class="container">
        <div class="child">Damn, I'm good!</div>
    </div>

    <div class="container">
        <div class="child">I'm gonna get medieval on your asses!</div>
    </div>

    <div class="container">
        <div class="child">Suck it down!</div>
    </div>

    <div class="container">
        <div class="child">Hail to the king, baby!</div>
    </div>
</div>

To create our multi-column layout, we assign an intrinsic CSS grid to the outer <div class=”wrapper”> element. As the CSS grid cells themselves can’t be used as CSS containers, we have to nest them inside the .container element. Here’s the related CSS:

.wrapper {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(512px, 100%), 1fr));
    align-content: flex-start;
	margin: 0;
	gap: 16px;
}

This is followed by the basic styling of the example. This CSS is only shown for the sake of completeness and is irrelevant for the functionality of the container query layout:

.child {
    --example-color: #000;
    --example-strokewidth: 2px;
    color: var(--example-color);
    border: var(--example-strokewidth) solid;
}

.child-header {
    aspect-ratio: 20/2;
    background: var(--example-color);
}

.child-content {
    padding: 1em;
	flex: 1 0 60%;
}

Now the exciting part! By using the .container element and setting its container-type property to inline-size, we can define a rule with @container (min-inline-size: 512px) that directly reacts to its size on the inline axis. Whenever the .container element exceeds 512 pixels in size, we reassign the CSS custom property for color and change the layout from grid to flexbox. Cool, right?

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

@container (min-inline-size: 512px) {
    .child {
        --example-color: #f05;
        display: flex;
    }
	
	.child-header {
        flex: 1 0 40%;
    }
}

Playground. Resize to see effects.

Nice title

Damn, I’m good!

Nice title

I’m gonna get medieval on your asses!

Nice title

Suck it down!

Nice title

Hail to the king, baby!

New CSS container query units

Container queries also introduce new CSS units similar to the old but gold viewport units vw and vh, such as cqw for the width of a container or cqh for its height:

  • cqw = a query container’s width.
  • cqh = a query container’s height.
  • cqi = a query container’s inline size.
  • cqb = a query container’s block size.
  • cqmin = the smaller value of either cqi or cqb.
  • cqmax = the larger value of either cqi or cqb.

These units open up endless possibilities. For example, we can dynamically change the font size based on the container size using the CSS clamp() function. Check it out:

<div class="container">
    <div class="child">Shake it, baby!</div>
</div>
.container {
    container-type: inline-size;
}

.child {
    font-size: clamp(1em, 5cqi, 2em);
}

With this code, whenever the size of the .container element changes, the font size of the .child element adjusts accordingly. The best part? It doesn’t matter what the widht and height of the browser’s viewport is. Pretty neat, if you ask me!

Playground. Resize to see effects.

Resizing the container changes the font size of the child accordingly.

Custom container query names

Now let’s explore container query names. Normally, elements respond to their direct parent containers in a HTML structure. But with named containers, we can refer to non-direct parent elements and customize components using different properties. So a child element could for example react to a resize of another element five levels above. To create a named container, we use the container-name property. You can even chain multiple names, like this:

.container {
    container-type: inline-size;
    container-name: fancy-container another-container; /* Names can be freely chosen */
}

/* Addressing the 'fancy-container' container */
@container fancy-container (min-inline-size: 512px) {
    duke: nukem;
}

Now let’s put theory into practice! Here’s an example where a component is nested twice in HTML:

<div class="outer"≫
    <div class="inner">
        <div class="component">Why so serious, Sam?</div≫
    </div>
</div>

Both parent elements are converted to containers with different names and types. The .outer container responds to the block size (height) using container-type: size, while the .inner container responds to the width, as in the previous examples:

.outer {
    container-type: size;
    container-name: outer-container;
}

.inner {
    container-type: inline-size;
    container-name: inner-container;
}
@container outer-container (min-block-size: 50vb) {
    .component {
        background: #000;
        color: #fff;
    }
}

@container inner-container (min-inline-size: 80cqi) {
    .component {
        border: 2px dashed #f05;
    }
}

Here’s how the container queries work in this case: the component resizes when .outer container’s block size (height) occupies at least 50% of the browser window (50vb). Additionally, the component changes when .inner takes up 80% of the inline size (width) of .outer (80cqi).

Playground. Resize to see effects.

Outer container

Inner container

Component

The container query shorthand

By the way, there’s a shorthand notation for container queries. It combines the container-name and container-type properties on a single line, separated by a slash. You write the name first, then the type:

/* Longhand notation */
.container {
    container-name: fancy-name;
    container-type: inline-size;
}

/* Shorthand notation */
.container {
    container: fancy-name / inline-size;
}

Browser Support

Unless you’re forced to support older or even stone-age browsers, there’s nothing to stop you from start using CSS container queries today, or at least from playing around with it. Check the latest browser support via caniuse.com.

Conclusion

In summary, container queries will have a profound impact on web design and component development, much as media queries have in the past. The demand for such solutions is extraordinary, as evidenced by developments such as intrinsic web design. Combined with CSS :has() and @layers, container queries provide a powerful way to create complex and robust layouts with minimal code and no JavaScript required. Get ready to expand your web design skills – and stay curious and continue to explore the fascinating world of modern CSS!


Hero image: Hero image: Generated with DALL·E 3 on October 28, 2023 at 4:01 PM and edited with the Generative Fill tool in Adobe Photoshop.

Reply article Reply answer

By sending the reply, your message, your chosen name and the current time will be saved.
Nothing else. No IP address, no email address, no cookies.