Tailwind CSS Container Queries: Component-Responsive Layouts
AI generated
</>
tw
Tailwind CSS · Container Queries · Responsive Design · CSS
Tailwind CSS Container Queries:
Responsive components without viewport dependency

Viewport breakpoints are the wrong tool for reusable components. A product card that is "narrow" at a 1200px viewport can be even narrower on that same viewport when placed in a sidebar. Container queries solve exactly this problem, and Tailwind CSS v4 ships with first-class support for them.

15 min read @container · cqw · containment · named container · Hyva Tailwind CSS v4 · CSS Container Queries Level 1 + 3

1. The problem with viewport breakpoints

The classic problem with viewport breakpoints and Tailwind CSS becomes obvious as soon as a component is used in different layout contexts. A product card with sm:flex-row becomes vertical at smartphone width and horizontal at tablet width, which is correct for a full-width list. That same card placed in a 300px-wide sidebar on a 1200px-wide desktop monitor looks like a desktop card even though its container is exactly as narrow as a smartphone. The card reacts to the viewport, not to the space it actually has available.

In practice this leads developers to either create special variants of the component for sidebar contexts (ProductCardSidebar, ProductCardMain) or set the viewport breakpoint low enough that it fits both contexts, which in turn breaks the full-width variant on large screens. Tailwind CSS container queries solve this dilemma fundamentally: the component reacts to its own container, not to the viewport. That same product card, dropped into a 300px sidebar, behaves as if it were on a smartphone, without a single extra CSS class.

2. What are container queries?

Container queries are a CSS feature that lets the browser apply styles to an element based on the size of its parent container, not the viewport. The syntax: an element is marked as a container (container-type: inline-size), then child elements can react to its width with @container. In Tailwind: container or @container as a class on the container element, then @sm:, @md:, @lg: as container query prefixes on the child elements.

The crucial difference from media queries: container queries are local, not global. Three different containers on the same page can react to different width breakpoints at the same time, one at 400px width, one at 600px, one at 200px, entirely independent of one another. That makes components genuinely portable: the same HTML structure with the same Tailwind classes works correctly at any width, in any layout context. In Hyva projects, that means one product-card phtml file for every context instead of separate templates for sidebar, slider and main listing.


/* Container Queries in pure CSS, for understanding the concept */

/* Step 1: Mark the parent as a container */
.product-grid {
  container-type: inline-size;
  container-name: product-list; /* optional named container */
}

/* Step 2: Apply styles to children based on container width */
@container product-list (min-width: 28rem) {
  .product-card {
    flex-direction: row;
  }

  .product-card__image {
    width: 40%;
    flex-shrink: 0;
  }
}

/* Container units, relative to the container, not the viewport */
.product-card__title {
  font-size: clamp(0.875rem, 4cqw, 1.25rem); /* 4% of container width */
}

3. Container queries in Tailwind CSS v4

Tailwind CSS v4 has first-class native support for container queries. The container element gets the class @container (not container, which is a different class used for max-width layouts). Child elements can then react to the container width with the prefixes @sm:, @md:, @lg:, @xl: and @2xl:. These breakpoints do not refer to the viewport but to the nearest ancestor container with the @container class.

The default container breakpoints in Tailwind CSS v4: @sm = 20rem (320px), @md = 28rem (448px), @lg = 32rem (512px), @xl = 36rem (576px), @2xl = 42rem (672px). That means a component with @md:flex-row switches to a horizontal layout as soon as its container is wider than 448px, regardless of the viewport. In a 1200px-wide layout with a 300px sidebar, the card stays vertical in the sidebar (container under 448px) and switches to horizontal in the main area (container over 448px), using the exact same classes.