CSS Container Queries: Components Instead of Viewport
AI generated
CSS · Container Queries · @container · Responsive Components
CSS Container Queries
Components Instead of Viewport

CSS Container Queries end the era of viewport-centric responsiveness. A card now knows how wide its container is, and adapts accordingly, whether it appears in a sidebar, a main content area, or a modal. This changes CSS architecture from the ground up.

17 min read container-type · @container · cqi · cqw · Style Queries Chrome 105+ · Firefox 110+ · Safari 16+

1. The Viewport Problem: Why Media Queries Fall Short

Media Queries are based on viewport width: the total width of the browser window. That made sense when web pages consisted of a single, linear flow area. Modern web applications, however, are component-based: the same card component appears in a main content area (700px wide), in a sidebar (280px wide), in a modal (400px wide), and in a four-column grid (200px wide each), all on the same 1440px-wide viewport. Media Queries cannot distinguish between these contexts.

The classic solution pattern was to write specific CSS classes for every usage context: .card, .sidebar .card, .modal .card. This leads to code duplication, context dependencies, and a stylesheet where component styles are scattered across the entire document. CSS Container Queries solve this problem structurally: a component defines its own responsive breakpoints relative to the container it sits in, not relative to the viewport. The same card adapts correctly everywhere, without context-specific overrides.

The conceptual leap with CSS Container Queries: responsiveness is a property of the component, not of the layout. A well-designed component built with CSS Container Queries can be placed into any container and will look correct there; it is truly self-contained. This is the principle React, Vue, and Web Components have long implemented at the logic level, but that was missing at the CSS level until now.

2. container-type: Declaring a Containment Context

To enable CSS Container Queries on an element, its parent element must be declared as a container. This happens with the container-type property. The value inline-size creates a containment context for the inline axis (in horizontal writing systems: the width). This is the most common use case: child elements query the width of their container. The value size creates a containment context for both axes (width and height), but is more restrictive and requires the container's height to be known.

An important technical detail: container-type: inline-size activates CSS containment for size calculations on the inline axis. This means the container can no longer be influenced by its child content in the inline direction; it must have a defined size itself, or receive one from its parent. This is the prerequisite that allows CSS Container Queries to reliably query the container size without running into a circular dependency.

Anyone who accidentally sets container-type on the wrong element gets no error message, but the CSS Container Queries simply will not work as expected. A common mistake: setting container-type on the component itself instead of on its parent element. The component cannot query its own container; CSS Container Queries always query the nearest ancestor with a containment context, an ancestor element, never the element itself.

/* Setting up container contexts for CSS Container Queries */

/* Parent element declares containment, NOT the component itself */
.card-grid {
  container-type: inline-size;
  container-name: card-grid; /* optional: named for precision */
}

/* Component styles adapt based on its container's width */
.card {
  display: flex;
  flex-direction: column;
  padding: 1rem;
  gap: 0.75rem;
}

/* Container Query: when card-grid is >= 480px wide */
@container card-grid (min-width: 480px) {
  .card {
    flex-direction: row;
    align-items: center;
  }
  .card__image { width: 160px; flex-shrink: 0; }
}

/* Container Query: even wider layout */
@container card-grid (min-width: 720px) {
  .card {
    padding: 1.5rem;
    gap: 1.25rem;
  }
}

/* Multiple containers in the same document */
.sidebar {
  container-type: inline-size;
  container-name: sidebar;
}

/* The same .card component adapts to sidebar context */
@container sidebar (max-width: 320px) {
  .card { flex-direction: column; }
  .card__image { width: 100%; }
}

3. container-name: Named Containers for Precise Queries

With container-name, a container can receive a name that can be used in CSS Container Queries to target that container specifically. Without a name, @container looks for the nearest ancestor with a containment context; with a name, it looks for the nearest ancestor with that specific name. This is essential when nested containers exist and a deeply nested component needs to react to a container further up the tree.

Named containers also enable clear, self-documenting CSS Container Queries rules. @container card-grid (min-width: 480px) immediately communicates that this rule relates to the card grid layout, not to some anonymous container. In large projects with many nested containers, this explicitness matters for maintainability. The naming convention should match the component architecture and be documented in a style guide.

An advanced pattern with named containers: a container can simultaneously serve as the containment context for multiple child components that define different breakpoints for the same container. A navigation component, a content component, and a footer component within the same container-type: inline-size parent element can all respond to its width, each with its own breakpoints. That is precise, component-based responsive design with CSS Container Queries.

4. Writing @container Rules

CSS Container Query rules are written with @container and support the same size comparisons as Media Queries: min-width, max-width, min-height, max-height, as well as the modern range queries (width >= 480px, 480px <= width <= 720px). Inside the @container block, normal CSS rules apply: all properties, selectors, and values can be used.

Range queries in CSS Container Queries are more elegant than the classic min/max pattern: @container (480px <= inline-size <= 720px) defines an exact range without the min-width/max-width redundancy. The keyword inline-size is more explicit than width and communicates that the logical inline axis is meant; in languages with vertical writing, that is not necessarily the horizontal axis.

Nested CSS Container Queries are possible: another @container rule can be placed inside an @container block. This allows complex responsive logic that reacts to both an outer and an inner container. In practice, nesting should be used sparingly; container hierarchies that are too deep make the CSS hard to debug and can slow down browser rendering.

/* CSS Container Queries: range syntax and named containers */

.product-list {
  container-type: inline-size;
  container-name: product-list;
}

/* Modern range syntax, more readable than min/max */
@container product-list (inline-size >= 600px) {
  .product-card {
    display: grid;
    grid-template-columns: 200px 1fr;
    grid-template-rows: auto;
  }
}

@container product-list (inline-size >= 900px) {
  .product-card {
    grid-template-columns: 280px 1fr auto;
  }
  .product-card__actions {
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 0.5rem;
  }
}

/* Range: apply only in a specific width window */
@container product-list (600px <= inline-size <= 900px) {
  .product-card__badge { display: none; } /* Too cramped for badge */
}

/* Nested containers for two-level responsive logic */
.page-layout {
  container-type: inline-size;
  container-name: page;
}

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

/* Widget responds to sidebar width, not page width */
@container sidebar (inline-size < 240px) {
  .widget-chart { display: none; }
  .widget-summary { font-size: 0.75rem; }
}

5. Container Query Units: cqi, cqw, cqb, cqh

CSS Container Query units are relative length units tied to the dimensions of the nearest containment context. cqw is 1% of the container width (container query width), cqh is 1% of the container height. cqi is 1% of the container's inline size (identical to cqw in horizontal writing systems), cqb is 1% of the block size. cqmin and cqmax are the minimum and maximum of the two axes, respectively.

CSS Container Query units enable fluid typography at the component level, analogous to vw for fluid typography at the viewport level. Instead of font-size: clamp(1rem, 3vw, 2rem), you can use font-size: clamp(1rem, 3cqi, 2rem). This means the font size scales with the width of the container instead of the viewport, ideal for components that appear in different contexts at different widths. A hero title automatically adapts whether it appears in a full-width section or in a 480px modal.

The difference between cqw and cqi: in horizontal writing systems (LTR, RTL), the inline axis is the horizontal one, so cqi and cqw are identical. In vertical writing systems (Japanese, traditional Chinese), the inline axis is the vertical one, and cqi refers to the height. For multilingual projects, cqi is the logically correct unit, since it scales with the writing direction.

6. Style Queries: Reacting to Custom Properties

CSS Style Queries are the second form of container queries and allow you to react to a container's custom property values. While size-based container queries react to dimensions, style queries react to declarative states. @container style(--card-variant: featured) applies when the container has the custom property --card-variant set to featured. This is a declarative mechanism for component variants, without JavaScript, without BEM classes, without data attributes.

CSS Style Queries fundamentally change the approach to component variants. Instead of defining .card--featured, .card--compact, and .card--hero as separate CSS classes, you set custom properties on the container and let child elements react to them via Style Queries. This keeps component styles cohesive and makes variants declaratively configurable. A parent element can control the layout of its child components through custom properties without knowing the children's classes.

Browser support for CSS Style Queries: Chrome 111+, Safari 17.2+, Firefox 128+. Support is broader than for size-based container queries. Style queries are especially powerful in combination with design-token custom properties: a theme component sets --theme: dark, and all child components adapt automatically via style queries, without propagating classes through the DOM.

/* CSS Style Queries: component variants via custom properties */

/* Parent declares containment (required for style queries too) */
.card-wrapper {
  container-type: style; /* or inline-size for combined queries */
}

/* Variant 1: featured card */
.card-wrapper[data-variant="featured"] {
  --card-variant: featured;
}

/* Child components react to parent's custom property */
@container style(--card-variant: featured) {
  .card {
    grid-column: span 2;
    background: var(--color-brand-subtle);
  }
  .card__title { font-size: var(--text-2xl); }
  .card__badge { display: block; }
}

/* Variant 2: compact */
@container style(--card-variant: compact) {
  .card {
    padding: 0.5rem;
    gap: 0.25rem;
  }
  .card__image { display: none; }
  .card__description { display: none; }
}

/* Combined: size AND style query */
@container product-list (inline-size >= 600px) and style(--card-variant: featured) {
  .card {
    grid-template-columns: 320px 1fr;
    font-size: 1.125rem;
  }
}

7. Architecture: Container Queries and Component-Based CSS

CSS Container Queries change the way you organize CSS files. In a viewport-centric architecture, responsive logic often lives in global stylesheet sections or separate @media blocks at the end of the file. With CSS Container Queries, responsive logic belongs inside the component itself, right next to its default styles. A card component contains its own @container rules, and the file is fully self-contained.

The implication for design systems: components built with CSS Container Queries are true black boxes. Anyone dropping a card into a grid does not need to write context-specific CSS overrides. The card handles its own layout. This reduces the knowledge developers need about other components and makes design systems more robust against unexpected layouts in new contexts.

An architectural principle for CSS Container Queries: container declarations (container-type, container-name) belong on layout components, not on content components. A grid wrapper, a sidebar, a modal: these are layout contexts. A card, a button, a form field: these are content components that react to containers. This separation makes the architecture predictable: content components know they will always sit inside a declared container.

8. Container Queries vs. Media Queries Compared

Both techniques solve different problems. CSS Container Queries and Media Queries complement each other; they do not replace one another. A fully responsive project uses both techniques for the respective use cases where each excels.

Criterion Media Queries Container Queries Recommendation
Context Entire viewport Parent container CQ for components
Component reuse Context-dependent Fully portable CQ for design systems
Global layout switching Ideal (navigation, columns) Not possible (no viewport info) MQ for global layout
Fluid typography (component) Viewport-relative only (vw) Container-relative (cqi) CQ + cqi for components
Browser support All browsers for years Chrome 105+, Safari 16+, FF 110+ CQ for new projects

The practical recommendation: use CSS Container Queries for all component styles, cards, tables, forms, widgets. Keep Media Queries for global layout decisions: the number of columns in the main grid, mobile vs. desktop navigation, showing or hiding the sidebar. This split mirrors the natural separation between layout decisions (global, viewport-dependent) and component decisions (local, container-dependent).

9. Pitfalls and Common Mistakes

The most common mistake with CSS Container Queries: setting container-type on the component itself instead of on its parent element. A component cannot query its own container; the containment context is always read from an ancestor. If you set container-type: inline-size on a .card and then use @container (min-width: 400px) within the .card rules, the query never works correctly, either because the card has no children defined inside the @container block, or because the containment context originates from the wrong element.

A second pitfall: intrinsic sizing conflicts. When a container has container-type: inline-size, its children must not influence the container width through their content. A container combining width: max-content with container-type: inline-size creates a circular dependency that browsers resolve differently. The container needs a defined width, whether through an explicit value, block layout rules, or parent constraints.

A third common mistake is forgetting container-type in CSS reset layers. If a CSS reset or normalize resets all properties, container-type can be reset back to normal (the default value with no containment). This causes CSS Container Queries to stop working. container-type should be declared in the correct CSS Cascade Layer, so the reset does not override the declaration.

/* CSS Container Queries: common mistakes and corrections */

/* WRONG: container-type on the component itself */
.card {
  container-type: inline-size; /* card cannot query itself */
}
@container (min-width: 400px) {
  .card__content { flex-direction: row; } /* Will not work as expected */
}

/* RIGHT: container-type on the parent wrapper */
.card-wrapper {
  container-type: inline-size;
  container-name: card-wrapper;
}
.card { /* No container-type here */ }
@container card-wrapper (min-width: 400px) {
  .card__content { flex-direction: row; } /* Works correctly */
}

/* Container Query Units in component fluid typography */
.hero-card {
  container-type: inline-size;
  container-name: hero-card;
}
@layer components {
  .hero-card__title {
    /* Fluid size: relative to CONTAINER, not viewport */
    font-size: clamp(1.25rem, 5cqi, 2.5rem);
    line-height: clamp(1.2, 1.1 + 0.5cqi, 1.4);
  }
}

/* Style Query: boolean-style check for a custom property */
@container style(--is-featured: true) {
  .card { outline: 3px solid oklch(55% 0.25 290); }
  .card__badge { display: block; }
}

Mironsoft

Component-based CSS architecture and Hyva theme development

Ready to bring CSS Container Queries into your design system?

We migrate viewport-centric components to container queries, define container architectures, and build style-query-based variant systems for Hyva themes and Magento frontend projects.

Container Architecture

Defining container hierarchies and naming conventions for your component system

Component Migration

Converting media-query-based components to container queries

Style Queries

Implementing component variants as style queries, with no JavaScript class switching

10. Summary

CSS Container Queries are the missing piece for truly component-based responsive design. With container-type: inline-size on the parent element and @container rules within the component, a card responds to the width of its container, not the viewport. The same component works in a sidebar, a main content area, a modal, and a grid, without context-specific overrides. Container Query units (cqi, cqw) complement fluid typography at the component level, and style queries enable declarative component variants through custom properties.

The key architecture decisions: set container-type on layout elements, not on content components. Use named containers for clear, maintainable CSS Container Queries rules. Combine Media Queries and CSS Container Queries: global layout via media query, component adjustments via container query. Use style queries for variant systems. And use cqi units for fluid typography at the component level to achieve the same fluid scaling as vw does at the viewport level.

CSS Container Queries: The Essentials at a Glance

Setup

container-type: inline-size on the parent element. container-name for precision. Never on the component itself: it cannot query itself.

@container Rules

Like @media, but relative to the container. Modern range syntax: @container (inline-size >= 480px). Named: @container card-grid (min-width: 480px).

CQ Units

cqi = 1% of the container's inline size. For fluid typography in components: clamp(1rem, 5cqi, 2.5rem) scales with the container, not the viewport.

Style Queries

@container style(--variant: featured): reacts to custom properties. Declarative component variants with no JavaScript or BEM modifier classes.

11. FAQ: CSS Container Queries

1What are CSS Container Queries?
Components react to the size of their container, not the viewport. A card adapts whether it appears in a sidebar, a grid, or a modal.
2How do you set up container-type correctly?
On the PARENT element of the component, not the component itself. A component cannot query its own container.
3inline-size vs. size?
inline-size means containment for width only. size means width and height. size requires a known container height; inline-size is enough for almost all use cases.
4What are cqi and cqw?
1% of the container's inline size or width, respectively. Analogous to vw, but relative to the container. Ideal for fluid typography at the component level.
5What are Style Queries?
React to custom property values: @container style(--variant: featured). Declarative component variants with no JavaScript or BEM modifier classes.
6Do Container Queries replace Media Queries?
No. Container Queries for components, Media Queries for global layout. Both together equal complete responsive design.
7Browser support?
Size queries: Chrome 105+, Firefox 110+, Safari 16+. Over 90% global support, ready to use in new projects today.
8Combining with Cascade Layers?
Yes. @container inside @layer blocks works. Layer priority applies to container query rules just like normal rules.
9Most common mistake?
Setting container-type on the component itself instead of on its parent element. Always declare the wrapper as the container.
10Container Queries for fluid typography?
font-size: clamp(1rem, 5cqi, 2.5rem) scales with container width instead of viewport, ideal for components appearing in contexts of varying width.