Breakpoints
Five widths, and the only token category that cannot enforce its own values.
Every other token is consumed where it is used — var(--border-1) in a rule, and if the token changes the rule changes with it. A breakpoint cannot work that way, so these five are reference values: a shared name for a number, held in one place, that the rules then repeat by hand.
They earn their place on one argument, and it is not documentation: lanes={{ base: 1, md: "1|1" }} is a public API. Stack’s lanes and reverse and Grid’s maxColumns take sm to 2xl as keys, so anyone using the package needs to know what md is — machine-readable, in the same file as every other value, shipped with it. These five are the dictionary entries for words the components already speak. Delete them and the prop still exists, with its meaning living only in a comment.
Widths
T-shirt names, matching the font-size scale — and no preview, because a width has no appearance.
| Token | Value |
|---|---|
| --breakpoint-sm | … |
| --breakpoint-md | … |
| --breakpoint-lg | … |
| --breakpoint-xl | … |
| --breakpoint-2xl | … |
There is deliberately no xs: the system is mobile-first, so everything below sm is the unqualified base case, and a name for “narrower than the narrowest breakpoint” would be naming the absence of a query.
xl and 2xl are currently unreached — no primitive collapses at them and no rule in the shell queries them. They stay on the rule this system keeps arriving at: a token may be unused, but it may not be undeliverable. A query at 1280px works perfectly the day someone writes one.
Tokens are invalid in queries
A custom property is invalid inside a query condition. @media (min-width: var(--breakpoint-md)) does not fail loudly — it is simply not a valid condition, and the whole block is dropped. The same is true of @container. So layout.css hard-codes 768px in the rule and tokens.css declares --breakpoint-md beside it, and the two are kept in step by hand.
The invalid version looks entirely reasonable, so it is the thing anyone writes first — and nothing will tell you the two have drifted. When editing a query, the number beside it is the one that matters; the token says which number it was meant to be.
Viewport or container?
@media asks about the viewport — the browser window, which is what most people mean by “responsive”. @container asks about the nearest ancestor declaring container-type. Same syntax, same numbers, different subject.
Every responsive rule in the layout primitives is a @container query, and the Section is the system's only query container. So the instinct to read md as “a tablet” is wrong here: an md key means this Section is at least 768px wide, which can be false on a 2560px display inside a narrow band, and true on a phone inside a full-bleed one.
Mobile-first, like Tailwind. base is the narrow case and each key applies at that width and up — the same direction and the same five numbers as Tailwind’s md:. The one difference is the subject: Tailwind’s prefixes ask about the viewport, these ask about the Section.
One consequence is worth carrying into every page you write: a Stack’s maxWidth does not move its breakpoints. The cap is a width, not a container, so an lg key on a Stack capped at 40rem still fires when the Section reaches 1024px — the lanes change inside a box that never got wider.
The app shell is the deliberate exception, and it uses a real @media: whether there is room for a sidebar beside the content is a property of the window, not of any band inside it. That is the test for which to reach for — ask @media about the window, and @container about the space a component was actually given.