Imagine you’re building a responsive layout for a website showcasing pets. Your grid works perfectly at different screen sizes, but then, you receive requirements to embed a pet carousel into a sidebar. The grid breaks because resizing the carousel's parent container isn’t accounted for in your design. Sound familiar?
This is where Container Queries shine- a game-changer for responsive design. They’ve been available for a few years now but still seem to be largely under-utilised. While media queries let you design based on the viewport size, container queries let you design based on the size of individual containers. In this article, we’ll dive deep into container queries, why they matter, and how to use them effectively.
What Are Container Queries?
#Essentially; Container queries are CSS rules that allow you to style elements based on the size of their parent container rather than the viewport.
Traditionally, the way we handled styling of elements in different screen sizes was by using Media Queries.
_10/* min-width is referencing the size of the screen */_10@media (min-width: 40rem) {_10 .mobile {_10 display: none;_10 }_10}
Media queries are great; they’re also still really useful. But their limitation is that they are concerned with high-level, global properties, things like the viewport dimensions of the screen. Container Queries give us finer grain control as they’re only concerned with local properties such as the computed width
or height
of parent elements.
_10/* min-width is referencing the size of the parent element */_10@container (min-width: 40rem) {_10 .mobile {_10 font-size: 1.5rem;_10 }_10}
The Essence Of Container Queries
#Simple enough right? Let’s dive into a example of how we can use container queries in a basic React project.
import React from "react"; import "./styles.css"; import Container from "./Container"; export default function App() { return ( <Container> <div className="card"> <h2>Responsive Card</h2> <p>This card adjusts based on its container size.</p> </div> </Container> ); }
In this example, we create a Container
component that renders child components. We then create a CSS class .container
which has a max-width
of 450px with container-type
CSS property to enable container queries and then define rules for .card
elements based on the container size in styles.css
Finally in our app App
we add the card element inside the container and the card’s styles dynamically adjust as the container resizes. By resizing the editor pane in the above example you can see container queries in action.
Containment Context
#To register a containment context, you use the container-type
property. This property not only makes an element a container but also decides which of its properties can be queried.
Info
A containment context is when an element acts like a box that controls certain things inside it. You can think of two main types: size containment, which deals with how big things are and style containment, which focuses on how things look.
Size containment
We can measure and track an element’s dimensions like width, height, aspect ratio using container size queries, but only if we register the element as a container. This tracking takes some processing power. A few elements are fine, but constantly tracking every element on the page; especially during resizing or scrolling would slow things down. That’s why elements don’t have size containment by default. To enable it, we use the container-type
property in CSS. The three possible values are normal
, size
, and inline-size
.
_10@container (min-width: 300px) {_10 .card { font-size: 1rem; }_10}
Style containment
This lets us check and use certain style properties of an element through container style queries. For now, we can only query custom properties like --theme: dark
, but soon we’ll be able to check for things like an element’s background-color
or display
values. Unlike size containment, style containment doesn’t require extra processing, so all elements have it by default, meaning you don’t need to register anything for style containment.
_10@container style(--theme: dark) {_10 .card { color: #fff; }_10}
You can also target a query to target both!
_10@container (width > 300px) and style(--theme: dark) {_10 .card {_10 font-size: 1rem;_10 color: #fff;_10 }_10}
Understanding Container Query Properties
#We initialise container queries via several CSS properties:
container-name
: Registers an element with a unique name that can be queried for styling based on its size.
_14.parent {_14 container-name: sidebar;_14}_14_14.child {_14 display: flex;_14 flex-direction: column;_14}_14_14@container sidebar (width > 10rem) {_14 .child { _14 flex-direction: row;_14 }_14}
Tip
Use descriptive class names and comments to manage styles effectively when using container queries.
container-type
: Defines the dimension that the container can be queried on. It can be one of three options:
container-type: size
The size of the container is computed in isolation, ignoring the sizes of its child elements. This ensures the container's dimensions are predictable and independent of its content. It’s Perfect for components that need to adapt dynamically to both their width and height, such as flexible cards or grid items.
container-type: inline-size
The inline size of the container is computed independently, ignoring the size of its child elements. Ideal for layouts that primarily depend on width changes, such as sidebar navigation or responsive column elements.
container-type: normal
This is the default value. The element does not become a query container for container size queries (size or inline-size). However, it remains a query container for container style queries, allowing styles to be scoped and isolated. This is Suitable when you only need style-based containment but don’t need to query the element’s dimensions.
_14.parent {_14 container-type: inline-size;_14}_14_14.child {_14 display: flex;_14 flex-direction: column;_14}_14_14@container (width > 10rem) {_14 .child { _14 flex-direction: row;_14 }_14}
container
: Is a shorthand property that combines both container-name
and container-type
into a single declaration.
_14.parent {_14 container: sidebar / inline-size;_14}_14_14.child {_14 display: flex;_14 flex-direction: column;_14}_14_14@container sidebar (width > 10rem) {_14 .child { _14 flex-direction: row;_14 }_14}
In the next example, we register a new container named card-grid
that can be queried by its inline-size
.
import React from "react"; import "./styles.css"; export default function App() { return ( <div className="card-grid"> <div className="card">Card 1</div> <div className="card">Card 2</div> <div className="card">Card 3</div> </div> ); }
Using Container Query Length Units
#With the introduction of container queries comes the addition of several new units specifically for container queries. These units allows us to make precise adjustments to element styles based on their parent container's size.
cqw
(Container Query Width): Represents 1% of the query container's width. This is useful when you want an element to scale proportionally to the container’s width, such as setting its width or horizontal padding: width: 50cqw;
makes the element’s width 50% of the container’s width.
cqh
(Container Query Height): Represents 1% of the query container's height. Use this for height-based styles like vertical padding or overall element height: height: 20cqh;
makes the element’s height 20% of the container’s height.
cqi
(Container Query Inline Size): Represents 1% of the container's inline size, which corresponds to width in horizontal writing modes (like English) and height in vertical writing modes (like Japanese): border-radius: 10cqi;
ensures the border radius adapts to the inline size, making designs more responsive to different writing directions.
cqb
(Container Query Block Size): Represents 1% of the container’s block size, which corresponds to height in horizontal writing modes and width in vertical writing modes: padding: 2cqb;
adjusts the padding based on the container’s block size.
cqmin
(Container Query Minimum Size): Represents the smaller value of either cqi
or cqb
. This is ideal for scaling styles that should adapt to the more constrained dimension of the container: font-size: 5cqmin;
keeps text legible by scaling it based on the container’s smallest dimension.
cqmax
(Container Query Maximum Size): Represents the larger value of either cqi
or cqb
. Use this for styles that should expand proportionally to the larger dimension of the container: font-size: 6cqmax;
scales text dynamically with the container’s largest dimension.
This approach ensures a more modular especially towards dynamic layouts. We can see how powerful these units illustrated in this example.
import React from "react"; import "./styles.css"; export default function App() { return ( <div className="container"> <div className="box">Resize Me</div> </div> ); }
Conclusion
#Container queries bring a new level of adaptability to component-based design.
It may sometimes feel like they are a solution looking for a problem. However; it’s important to understand that container queries and media queries exist to solve very specific use cases.
It’s with the new addition of container queries we are offered finer grain control over the behaviour of how the user interface responds. They allow us to design our code in a more modular way by only having to think about how elements behave with respect to their parent elements rather than the entire viewport.
With that being said- don’t be afraid to play around with the examples above to get to grips with the awesome power of container queries and have a think about what cases you can use them in your latest apps.
Happy coding!
Reinforce Your Learning
One of the best ways to reinforce what your learning is to test yourself to solidify the knowlege in your memory.
Complete this 9 question quiz to see how much you remember.
Thanks alot for your feedback!
The insights you share really help me with improving the quality of the content here.
If there's anything you would like to add, please send a message to:
[email protected]Was this article this helpful?
About the author
Danny Engineering
A software engineer with a strong belief in human-centric design and driven by a deep empathy for users. Combining the latest technology with human values to build a better, more connected world.