container query patterns

[check: …]
📖 Pair this live demo with the companion guide (.md) — this page is the rendered ground truth. ↗ Builds on tailwind: container queries basics (mechanics of @container) and frontend/foundations: flexbox (the flex-row / flex-col the card flips between).

component-driven vs page-driven responsive design

Media queries are page-driven: one component looks identical everywhere at the same viewport size. Container queries are component-driven: the same markup reflows based on the space its parent gives it. Drop one card into a narrow sidebar, a wide grid cell, or a modal — it adapts to each placement automatically, with zero JavaScript.

dimensionmedia query (page-driven)container query (component-driven)
signal viewport width (md:) container width (@md:)
same component, different slots ❌ identical everywhere at a given viewport ✅ reflows per slot — sidebar card ≠ hero card
trigger source browser window resize only sidebar toggle, grid reflow, modal open — any layout change
reusability coupled to page layout self-contained, portable across pages & apps
JS needed? no — but can't react to placement no — pure CSS, reacts to its own box

1 · pattern 1 — the adaptive card (resize the container)

Drag the slider to resize the wrapper, not the browser. The card uses flex-col by default, @md:flex-row at ≥448px, and adds a larger image + 2-column meta at @2xl (≥672px). The viewport never changes — only the container does.

560px
image

design system

Adaptive Card Component

This card reflows on its container, not the viewport. Resize the wrapper to watch vertical → horizontal → expanded.

views

12.4k

updated

2 days ago

container-type: — · flex-direction: — · state: —

2 · same card, three placements (no viewport change)

The identical card markup below lives in three fixed-width containers — a sidebar (260px), a grid cell (520px), and a hero slot (720px). All three render at the same viewport size; only their containers differ. This is the design-system payoff: one component, every context.

sidebar · 260px

card

Sidebar Slot

Stacks vertically in a narrow column.

grid cell · 520px

card

Grid Cell Slot

Image goes left, text right — horizontal layout.

hero slot · 720px

card

Hero Slot

Larger image, room for richer content.

3 · pattern catalog

patterntriggercontainer-query mechanic
adaptive card card placed in slots of varying width flex-col @md:flex-row + image/text size at @2xl
sidebar toggle JS collapses a sidebar → main column widens main is @container; children reflow with no media query
component-library card same card dropped in grid / sidebar / modal card owns an @container wrapper; adapts to each host
data table → cards narrow container can't fit columns @max-lg:hidden table, card list shown below @lg
nav: inline → overflow toolbar items exceed container width @max-md:hidden extras + a "more" menu under @md

Sidebar toggle & component-library card are not interactive here — they're illustrated with code in the guide (.md). The key point: a layout change driven by JS state (collapsing a sidebar) changes the container size, which fires the container query — no media query or resize listener is involved.

4 · container breakpoints & cheat sheet

Verified values (Tailwind v4, 2026-06). The demo switches at @md = 28rem (448px) and expands at @2xl = 42rem (672px).

variantmin-widthadaptive-card role
@sm:24rem (384px)
@md:28rem (448px)flip flex-col → flex-row
@lg:32rem (512px)
@xl:36rem (576px)
@2xl:42rem (672px)larger image + 2-col meta
@3xl:48rem (768px)
@4xl:56rem (896px)
<!-- THE ADAPTIVE CARD PATTERN --> <div class="@container"> <!-- 1. mark the slot queryable --> <article class="flex flex-col @md:flex-row ..."> <!-- 2. reflow on container width --> <img class="w-full @md:w-36 @2xl:w-52 ..."/> <!-- 3. size per state --> <div class="grid grid-cols-1 @2xl:grid-cols-2"> <!-- 4. inner density at @2xl --> </article> </div>
intentpattern
make a slot queryable<div class="@container">
flip layout by containerflex-col @md:flex-row
size per statew-full @md:w-36 @2xl:w-52
range (below a width)@max-md:flex-col
target a specific container@container/main + @md/main:
arbitrary container size@min-[475px]:flex-row