Responsive Components with CSS Container Queries

I’m Ashley, a Frontend Developer here at John Lewis & Partners. I work on the Waitrose Groceries website within the Identity & Account team.

UPDATE

2nd September 2022: The syntax for container queries went through various iterations but is now stable and starting to ship with browsers 🎉 As such I’ve updated this article and associated demo accordingly since it was first published.

A bit of history

Back in the day website layout and styling was simple (we’ll gloss over floats and browser support for now). You just assumed everyone browsed the web on desktop computers and so built your website to fit the average desktop screen.

Then around 2007 things got a bit more complicated. Steve Jobs introduced the iPhone to the world and suddenly people were accessing websites on all sorts of devices with a vast array of screen sizes. This resulted in large websites being displayed on small screens and a whole lot of zooming and scrolling.

A first generation iPhone displaying the BBC homepage showing how the website does not respond to the size of the screen.
The BBC homepage on a 1st generation iPhone.

Gone were the days of designing for a single screen size. Instead you now had to design and build sites that would respond to the available screen size. A few years later, responsive design and media queries were born and all was right in the world of website layout and styling once again.

You wanted to browse a website on the latest smartphone with a screen width of 320 pixels? No problem, we’ll lay things out in a single column. You’ve got the latest hi-resolution monitor? Great, more real estate to play with.

So, what’s the problem?

Well, as ever, things moved on in the world of web design and development with the advent of Javascript based single page applications, design systems, web components, etc. These technologies and methodologies have all provided worthwhile improvements to the world of building enterprise-quality web applications but at the same time have introduced a handful of new challenges to overcome.

Reusable components are a great thing, allowing us to build applications from smaller, atomic building blocks, speeding up development and providing consistency of design and user experience.

However, by their very nature, they can be dropped into an application anywhere the designer and developer choose. And, whilst you can specify styles for the component and use media queries to allow it to respond to the width of the viewport, this still does not take into account the constraints of whatever containing element the component sits within.

If the content is intended to layout at 100% of the width of its container then there’s no problem. However, this is not always the case and, more often than not, it would not be making the best use of the available space.

CSS Container Queries to the rescue!

Much like their older sibling media queries, container queries essentially provide a conditional statement around some styles, which are then implemented or not based on the condition specified. The big difference between media queries and container queries is that media queries are based on the size of the device viewport whereas container queries are based on the size of the containing element.

.my-container {
container: product-container / inline-size;
}
.my-component {
width: 100%;
}
@container product-container (min-width: 240px) {
.my-component {
display: flex;
}
}

What do we mean by the containing element? Well it could be the parent element, but it could also be any ancestor. It is up to the developer to specify, within the CSS, which element is the container and therefore which element to base any conditional logic upon.

In a component library scenario, I personally would envisage the container of a given component being the top level element and then all descendant elements being styled based on the dimensions of this ancestor. Having said that, there’s no reason a complex component might not have multiple containers for different parts of the component.

Demo

https://codesandbox.io/s/wtr-container-queries-szojt

Note: You’ll need to view it in a browser that supports container queries (read on below for details).

I created a demo to give an example use case of how container queries might be used on the Waitrose groceries site, in this case with regards to product pods (a product image, title, price, etc.).

In the demo, I’m showing products in a search results listing, a trolley and a hero banner. These instances all use the same HTML structure and the same product data. However, by using container queries, I can style them differently to make best use of the location each is displayed in.

Three different ways to show a product but all use the same HTML and data.

The search results are fairly standard. They display a thumbnail image of the product, a title, the price, etc. and the ability to add the product to your trolley.

The products already in the trolley are more of just a reference. We want to keep things very minimal so that we don’t clutter the space but they still benefit from the same buttons and logic to change the quantity in the trolley.

And finally, the hero product has lots of space to play with and therefore we can increase the size of the image and font sizes as well as provide a more verbose “Add to trolley” button, a short description and a link to a relevant recipe.

The file to concentrate on is /src/components/Product/Product.module.scss
which applies the styles to the product pods.

At the top you’ll notice the .productContainer class, which is applied to the top level element within the product component. This class applies the containerproperty setting the element to be the container that all descendant container queries will be calculated against.

The container property is shorthand for container-name followed by container-type. container-name is pretty self explanatory whilst in nearly all cases you’ll want to use a container-type of inline-size. I won’t go into the detail of what inline-size means here but if you wish to read more about this property and it’s possible values check out the MDN web docs.

Further down the file you’ll notice various @container rules which contain styles which may, or may not, be applied depending on whether the condition specified has been met.

Note: This is just a simple demo so not everything works, including the fact that, whilst the product pods respond to the space they’re given, somewhat ironically the page as a whole is not responsive so best viewed on a large screen (media queries are so last year 😉 ).

Do we still need media queries?

I think that, to some degree, this will play out as container queries become more mainstream and developers start using them, but the general feeling across the internet seems to be that there will still be a requirement for media queries. The distinction of when to use one over the other will probably be a grey area but as a general rule, media queries would still be used for page scale changes, such as the layout of blocks of content on a page, whereas container queries would be used to change styles within individual components.

Browser Support

Container queries are now stable and starting to ship with latest versions of browsers and so you’ll be able to use them natively in the near future. In the meantime there’s a polyfill written by the people from Chrome that’ll handle your container queries: https://github.com/GoogleChromeLabs/container-query-polyfill

A table showing browser support for container queries
Browser support for CSS Container Queries (as of 2nd September 2022).

For current levels of browser support see caniuse.com.

--

--