CSS @container Queries: Size and Style Container Queries

CSS @container queries let components respond to the size or style of the element that contains them, instead of the whole viewport. That makes responsive design more reusable, because a card, sidebar, or banner can adapt wherever it is placed.

Quick answer: Use @container when you want a component to change based on its parent container, not the browser window. Size queries adapt to available space, and style queries adapt to computed container styles.

Difficulty: Intermediate

You'll understand this better if you know: basic CSS selectors, media queries, and how layout boxes such as flex and grid affect element size.

1. What Is @container Queries?

@container queries are CSS rules that conditionally apply styles when a container meets specific requirements. The most common requirements are:

Unlike @media, which responds to the viewport, container queries respond to the size or style of an ancestor element that has been declared as a query container.

2. Why @container Queries Matters

Container queries solve one of the biggest problems in responsive UI design: components often need to work in multiple layouts. A card may live in a narrow sidebar, a wide content area, or a dashboard grid, and the component should not need separate viewport-based rules for each place.

They matter because they help you build:

They are especially useful when the same component appears in different widths on the same page. A viewport query may be too broad, but a container query can react to the actual space available.

3. Basic Syntax or Core Idea

Before a container query can work, the element you want to query must be marked as a query container. For size queries, the usual setup is container-type.

Simple size container query

This example makes a card respond to its container width. The container is declared with container-type: inline-size, which means the query can read the container’s inline direction size, usually its width in horizontal writing modes.

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

.card {
  display: grid;
  gap: 1rem;
  grid-template-columns: 1fr;
}

@container (min-width: 40rem) {
  .card {
    grid-template-columns: 12rem 1fr;
  }
}

The wrapper becomes the container, and the card changes layout when that container is at least 40rem wide. This is the core idea of size-based container queries.

Named container query

When there are several possible containers in the ancestor chain, you can give one a name and query it directly.

.layout {
  container-name: content-area;
  container-type: inline-size;
}

@container content-area (min-width: 60rem) {
  .article {
    padding: 2rem;
  }
}

The name makes the rule more precise and easier to maintain in a complex layout.

Style container query

Style queries let you react to a computed style on the container. They are useful for themes, variants, and component states that are already represented in CSS.

.panel {
  container-type: normal;
  --panel-theme: dark;
}

@container style(--panel-theme: dark) {
  .panel-title {
    color: white;
  }
}

Style queries are not about width or height. They check whether the container’s computed style matches the condition you wrote.

4. Step-by-Step Examples

Example 1: A responsive card

This pattern is common in dashboards and grids. The card starts stacked, then becomes two columns when there is enough room.

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

.product-card {
  display: grid;
  gap: 1rem;
  grid-template-columns: 1fr;
}

@container (min-width: 30rem) {
  .product-card {
    grid-template-columns: 10rem 1fr;
    align-items: start;
  }
}

This works well because the component adapts to its actual slot, not the entire screen.

Example 2: Typography that adapts to container width

Sometimes the layout does not need to change, but the text size or spacing should. Here, a heading becomes larger only when its container is wide enough.

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

.article-body h2 {
  font-size: 1.25rem;
}

@container (min-width: 45rem) {
  .article-body h2 {
    font-size: 1.75rem;
  }
}

This keeps large headings from appearing in cramped spaces, while allowing better hierarchy in wider areas.

Example 3: Style query for a variant

Style queries are useful when a component already exposes its state through CSS custom properties. The panel below switches child styles based on a theme token.

.notice {
  container-type: normal;
  --notice-variant: warning;
}

@container style(--notice-variant: warning) {
  .notice-icon {
    color: orange;
  }
}

This lets CSS react to the container’s computed style without adding extra classes for every variant.

Example 4: Using a named container in a nested layout

In deeply nested layouts, the nearest container may not be the one you want. Naming the outer content region helps the query stay stable.

.page {
  display: grid;
  grid-template-columns: 18rem 1fr;
}

.content {
  container-name: main-area;
  container-type: inline-size;
}

@container main-area (min-width: 50rem) {
  .sidebar-note {
    display: block;
  }
}

The named container avoids accidental matches against a smaller nested wrapper.

5. Practical Use Cases

6. Common Mistakes

Mistake 1: Forgetting to declare a query container

Container queries only work if an ancestor is explicitly turned into a container. A common beginner mistake is writing the @container rule but never setting container-type or container-name on the parent.

Problem: The query never matches because no ancestor has been made into a size container, so the rule appears to do nothing.

.card-wrapper {
  display: block;
}

@container (min-width: 40rem) {
  .card {
    grid-template-columns: 12rem 1fr;
  }
}

Fix: Mark the ancestor as a container first.

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

@container (min-width: 40rem) {
  .card {
    grid-template-columns: 12rem 1fr;
  }
}

The corrected version works because the browser now has a real container to measure.

Mistake 2: Querying the wrong container

If a component is nested inside several wrappers, the nearest container may be the wrong one. That can make a rule fire too early or never fire at all.

Problem: The query matches a small nested wrapper instead of the wider content region, causing unexpected layout changes.

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

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

@container (min-width: 50rem) {
  .component {
    padding: 2rem;
  }
}

Fix: Name the intended container and target it directly.

.outer {
  container-name: page-area;
  container-type: inline-size;
}

.inner {
  container-type: normal;
}

@container page-area (min-width: 50rem) {
  .component {
    padding: 2rem;
  }
}

The named query removes ambiguity and makes the breakpoint apply to the intended layout region.

Mistake 3: Expecting style queries to work like size queries

Style queries compare computed style values, not arbitrary visual states. They are not a replacement for media queries or for querying unsupported properties.

Problem: The query checks for a style value that the container does not expose in a way the browser can query, so the condition never becomes true.

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

@container style(color: red) {
  .panel-title {
    font-weight: 700;
  }
}

Fix: Query a computable custom property or a supported style value that the container actually controls.

.panel {
  container-type: normal;
  --panel-tone: alert;
}

@container style(--panel-tone: alert) {
  .panel-title {
    font-weight: 700;
  }
}

The fixed version works because the query uses a style token designed for CSS-driven variation.

7. Best Practices

Practice 1: Use size queries for components, not entire pages

Container queries shine when a reusable component needs to adapt inside many layouts. If the whole page is changing, a media query may still be the simpler choice.

/* Good: component responds to its own wrapper */
.widget {
  container-type: inline-size;
}

@container (min-width: 32rem) {
  .widget {
    display: grid;
  }
}

Using them this way keeps component CSS portable and reduces page-specific breakpoints.

Practice 2: Name containers when nesting gets complex

In large layouts, naming the relevant container makes your CSS easier to read and reduces accidental matches.

.dashboard-main {
  container-name: dashboard;
  container-type: inline-size;
}

@container dashboard (min-width: 70rem) {
  .insight-panel {
    display: grid;
  }
}

Names help future you understand exactly which region controls the rule.

Practice 3: Keep fallback styles outside the query

Always write a sensible base layout first, then enhance it inside the container query. That way, the component still works when the container condition is not met or when support is unavailable.

.media-card {
  display: block;
  gap: 1rem;
}

@container (min-width: 36rem) {
  .media-card {
    display: grid;
    grid-template-columns: 8rem 1fr;
  }
}

A strong default prevents broken layouts and keeps the enhancement progressive.

8. Limitations and Edge Cases

One subtle issue is that a container can only be queried if it participates in the correct containment model. If you choose the wrong container type, the query may never behave as expected.

9. Practical Mini Project

Here is a small but complete example of a reusable feature card that changes layout based on its own container width. The card lives inside a wrapper that acts as the query container.

.feature-section {
  container-type: inline-size;
  max-width: 48rem;
  margin: 0 auto;
  padding: 1rem;
}

.feature-card {
  display: block;
  padding: 1rem;
  border: 1px solid #ccc;
  border-radius: 0.75rem;
}

.feature-media {
  width: 100%;
  aspect-ratio: 16 / 9;
  background: #e8eef7;
}

.feature-copy {
  margin-top: 1rem;
}

@container (min-width: 35rem) {
  .feature-card {
    display: grid;
    grid-template-columns: 14rem 1fr;
    gap: 1.5rem;
  }

  .feature-copy {
    margin-top: 0;
  }
}

This pattern gives you a base stacked card that upgrades to a side-by-side card when there is room. Because the query is tied to the section width, the same component can be used in narrow and wide layouts without rewriting the CSS.

10. Key Points

11. Practice Exercise

Create a responsive profile card that changes from stacked to split layout when its container becomes wider than 32rem.

Expected output: A narrow card stacks the avatar above the text, while a wider card places them side by side.

Hint: Put container-type: inline-size on the wrapper, then use a min-width query on that container.

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

.profile-card {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1rem;
  padding: 1rem;
  border: 1px solid #ddd;
}

.avatar {
  width: 6rem;
  height: 6rem;
  border-radius: 50%;
  background: #cfd8e3;
}

@container (min-width: 32rem) {
  .profile-card {
    grid-template-columns: 6rem 1fr;
    align-items: center;
  }
}

12. Final Summary

CSS @container queries let you build components that respond to the space and style of their own container, which makes responsive design more modular and reusable. Size queries are the most common use case, while style queries add another layer of CSS-driven variation for advanced component systems.

The most important thing to remember is that container queries need the right setup: declare a container, query the correct ancestor, and provide a solid base style first. Once you do that, you can create components that fit wherever they are placed, rather than relying on page-wide breakpoints.

If you want to go further, next learn container-type, container query units, and how container queries interact with grid and flex layouts.