the @ variant scale — 13 container breakpoints
Every container variant maps to a --container-* theme token.
@md: fires when the container's inline-size is
≥ --container-md (28rem / 448px).
These are independent from viewport breakpoints (sm:,
md: use --breakpoint-*) — same names, different
namespace, different trigger (container vs window).
| variant | theme token | default | px (root 16) | fires when container… |
|---|---|---|---|---|
@3xs: | --container-3xs | 16rem | 256px | ≥ 256px |
@2xs: | --container-2xs | 18rem | 288px | ≥ 288px |
@xs: | --container-xs | 20rem | 320px | ≥ 320px |
@sm: | --container-sm | 24rem | 384px | ≥ 384px |
@md: | --container-md | 28rem | 448px | ≥ 448px |
@lg: | --container-lg | 32rem | 512px | ≥ 512px |
@xl: | --container-xl | 36rem | 576px | ≥ 576px |
@2xl: | --container-2xl | 42rem | 672px | ≥ 672px |
@3xl: | --container-3xl | 48rem | 768px | ≥ 768px |
@4xl: | --container-4xl | 56rem | 896px | ≥ 896px |
@5xl: | --container-5xl | 64rem | 1024px | ≥ 1024px |
@6xl: | --container-6xl | 72rem | 1152px | ≥ 1152px |
@7xl: | --container-7xl | 80rem | 1280px | ≥ 1280px |
Range variants: prefix max- → @max-sm: fires
when the container is < 24rem (384px). Arbitrary:
@min-[400px]: / @max-[400px]: for one-off thresholds.
Stacking: @sm:flex-row @lg:gap-8 — both apply, larger wins.
1 · resize the container — watch every @ variant activate
Drag the slider to change the container width. The grid below switches
column count at @3xs/@xs/@sm/@md/@lg; the chip bar reads out
all 13 min-width variants live (✅ = active, ❌ = inactive). The
values come straight from getComputedStyle() on invisible
probe elements — what you see is the browser's container-query engine,
not JS math.
2 · beyond min-width: ranges, arbitrary values, stacking
| pattern | syntax | fires when container… | generated CSS (sketch) |
|---|---|---|---|
| min-width (default) | @md:grid-cols-2 |
≥ 28rem | @container (width >= 28rem) |
| max-width range | @max-md:hidden |
< 28rem | @container (width < 28rem) |
| between two breakpoints | @sm:block @max-lg:hidden |
24rem ≤ w < 32rem | min & max queries ANDed |
| arbitrary min | @min-[400px]:flex |
≥ 400px | @container (min-width: 400px) |
| arbitrary max | @max-[400px]:hidden |
< 400px | @container (max-width: 400px) |
| stacked variants | @sm:flex-row @lg:gap-8 |
two independent rules | larger breakpoint wins on conflict (ascending source order) |
| override threshold | @theme { --container-md: 20rem } |
now @md: = 20rem |
redefines the token the variant reads |
Why @max-sm: = < 24rem and not
≤? Tailwind emits a strict width <
comparison so min- and max- variants never overlap at the exact
boundary — no double-application at 384.000px.
intent → variant
| intent | pattern | why |
|---|---|---|
| respond to container width | class="@container" → @md:grid-cols-2 |
parent needs @container; child reads --container-md |
| only-when-narrow | @max-sm:hidden |
range query: hides below 24rem (sidebar widget compact mode) |
| one-off threshold | @min-[400px]:flex-row |
arbitrary value — no theme edit needed |
| progressive grid | grid-cols-1 @sm:grid-cols-2 @lg:grid-cols-3 @2xl:grid-cols-4 |
each variant widens the grid as space grows |
| combine two breakpoints | @sm:flex-row @lg:gap-8 |
stack — both rules apply; ascending order resolves conflicts |
| customize a breakpoint | @theme { --container-md: 20rem; } |
shifts every @md: site app-wide |