By WebNest Studio Engineering Team
CSS Tutorial
Container Queries
Media queries let you style elements based on the size of the browser viewport, but that has always been an awkward fit for reusable components: a card component might sit in a wide main column on one page and a narrow sidebar on another, and its ideal layout depends on the space it actually has — not on the overall screen size. Container queries solve exactly this problem by letting an element respond to the size of its containing element instead of the viewport.
Container queries are a genuinely modern CSS feature (broadly supported in evergreen browsers since 2023) and represent one of the most requested capabilities in CSS's history, because component-based design (React, Vue, design systems) had outgrown what viewport-based media queries could express.
Why it matters
A media query asks how wide the window is, but a reusable component needs to know how wide its own container is: the same card may sit in a narrow sidebar or a wide main column on the same screen. Container queries answer that, and they are the most significant addition to responsive design in years. All current browsers support them.
Declaring a Containment Context
To make an element queryable by its children, you opt it in with container-type on the parent: container-type: inline-size (the common case, tracking width) or size (tracking both width and height). An optional container-name lets you target a specific container by name when queries could otherwise be ambiguous.
Writing an @container Rule
Inside a rule, @container (min-width: 400px) { ... } applies its nested styles whenever the nearest ancestor with a containment context is at least 400px wide — regardless of how wide the browser window itself is. This is the key difference from @media, which only ever looks at the viewport.
Why This Beats Media Queries for Components
A card component styled with container queries looks correct whether it's dropped into a full-width page, a narrow sidebar, or a three-column grid, because its internal layout reacts to its own available space rather than a global breakpoint. This makes components genuinely reusable across contexts — which is exactly the promise component libraries and design systems are built on — without the component needing to know or guess where it will be placed.
Example
A card that switches from stacked to side-by-side layout based on its container width, not the viewport
<div class="card-container">
<div class="card">
<img src="avatar.jpg" alt="">
<div class="card-body">
<h3>Asha Patel</h3>
<p>Frontend Engineer</p>
</div>
</div>
</div>
<style>
.card-container {
container-type: inline-size;
container-name: card;
}
.card {
display: flex;
flex-direction: column;
}
@container card (min-width: 350px) {
.card {
flex-direction: row;
align-items: center;
gap: 16px;
}
}
</style>
When .card-container is narrower than 350px (e.g. in a sidebar), the avatar image stacks above the text. When the same markup is placed in a wider container of 350px or more (e.g. a main content column), the layout automatically switches to a side-by-side row with the avatar next to the text — with no JavaScript and no change to the viewport width.
Common Mistakes
- Writing an @container rule without first declaring container-type on an ancestor — without a containment context declared somewhere up the tree, the @container query has nothing to measure and its styles never apply.
- Confusing @container with @media and expecting it to react to window resizing directly — it reacts to the named container's size, which may or may not track the viewport.
- Setting container-type: inline-size on the same element you are trying to style with @container — the containment context must be on an ancestor, not the element being conditionally styled.
- Forgetting that older browsers (pre-2023) do not support container queries, and shipping a component with no fallback layout for those environments if long-tail browser support matters for the project.
Exercise
Make main a query container. By default the heading is 1.25rem. When main itself is at least 400px wide, the heading grows to 2rem and the button gets larger padding of 1rem by 2rem.
Expected result: the heading and the button grow when main reaches 400px wide, whatever the width of the window.
/* The page contains: <main> with an <h1>, a <p> and a <button> */
main {
/* TODO: make main a container that can be queried on its inline size */
}
h1 {
font-size: 1.25rem;
}
/* TODO: when main is at least 400px wide, enlarge the heading
and give the button more padding */
Show hints
- The container is declared with
container-type: inline-size. - The query is written
@container (min-width: 400px) { ... }and styles the descendants of the container, not the container itself.
Show solution
/* The page contains: <main> with an <h1>, a <p> and a <button> */
main {
container-type: inline-size;
}
h1 {
font-size: 1.25rem;
}
@container (min-width: 400px) {
h1 {
font-size: 2rem;
}
button {
padding: 1rem 2rem;
}
}
Quick quiz
1. What does a container query respond to?
- A. The width of the browser window
- B. The size of the screen
- C. The user's font size
- D. The size of an ancestor element declared as a container
Show answer
D. That is the difference from a media query, which looks at the viewport.
2. Which declaration turns an element into a query container for its width?
- A. container-type: inline-size
- B. display: container
- C. query: width
- D. contain: all
Show answer
A. inline-size is the width in horizontal writing modes.
3. Can the rules inside @container style the container element itself?
- A. Yes, always
- B. No; they apply to its descendants
- C. Only its width
- D. Only with !important
Show answer
B. A container cannot depend on its own size, so the query targets what is inside it.
Interview questions
What is the difference between a media query and a container query?
Show answer
A media query tests the viewport and other features of the device, so it suits decisions about the overall page layout. A container query tests the size of a specific ancestor element, so a component can adapt to the space it has been given. The same card can then be wide in the main column and compact in a sidebar on one screen, which media queries cannot express.
Why must the container be declared explicitly?
Show answer
For a query on size to work, the browser has to be able to size the container without looking at its contents, otherwise the contents could change the size that the query depends on. container-type: inline-size applies that containment for the inline direction. Naming a container with container-name lets a query target a particular ancestor when several containers are nested.
Key Points to Remember
- Container queries style an element based on the size of an ancestor container, not the viewport.
- container-type: inline-size (or size) on a parent opts it into being queryable; container-name optionally scopes queries to that container.
- @container (min-width: ...) { ... } applies styles once the named container reaches that size.
- They make components genuinely portable across different layout contexts, which media queries alone could never achieve.
Practice in the playground
Write and run your own code for this topic, or start from the exercise or an example. Shows a live preview of your HTML, CSS and JavaScript. Your code for each topic is kept in this browser.
Run the examples
Change an input, predict the result, then compare it with the output. Explain why the result changes.
Written by
WebNest Studio Engineering Team
Practical lessons from developers working with Java, Python, React, databases and production systems.
Led by Vansh Duggal, Founder, WebNest Studio · Full-stack & software engineering