Breakpoints
Six canonical breakpoints. Mobile-first naming, min-width semantics — every breakpoint is the size above which the layout shifts. Below xs, treat the layout as "narrowest viable mobile."
| Token | Min width | Typical surface | Layout shift at this breakpoint |
|---|---|---|---|
xs | 360px | Narrow phones | Footer columns stack to one |
sm | 640px | Large phones, small tablets | Dashboard sidebar collapses to drawer |
md | 768px | Tablets, split-screen laptops | Two-column body grids appear |
lg | 1024px | Standard laptops | Three-column grids appear; rails activate |
xl | 1280px | Wide laptops | Content max-width caps; lateral padding grows |
2xl | 1536px | Monitors, large desktops | Hero typography max-scale; no further shifts |
Reading the tokens
import { breakpoints } from "@matter/tokens";
breakpoints.md; // "768px"In CSS:
@media (min-width: 768px) {
/* md and up */
}
/* or use the variable — same value, theme-agnostic */
@media (min-width: var(--breakpoint-md)) {
/* md and up */
}In Tailwind (the @matter/presets-tailwind preset maps each token to a screen):
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3">
…
</div>Container queries first
For component-level responsive behaviour, prefer container queries over breakpoints:
.card-grid {
container-type: inline-size;
}
@container (min-width: 640px) {
.card-grid__item {
/* card-grid is 640px wide or more — column up */
}
}Container queries scope to the component's parent, so a card grid inside a sidebar reflows independently from a card grid in the main column. Breakpoints scope to the viewport — reach for them only when the page-level shell itself shifts.
Why six, not five or seven
xsanchors the "narrowest viable" surface — below 360px, design fidelity drops and we don't optimise.sm/mdcover the phone-to-tablet transition where most dashboard surfaces change shape.lgis where rails appear — most product layouts assumelgand up for the canonical layout.xlis where content caps stop growing. Beyond this, we add lateral padding, not column count.2xlis the cap for hero typography. Beyond this, large monitors get whitespace, not larger text.
A seventh breakpoint (3xl at 1920px+) was considered and rejected — wide monitors get whitespace, not more columns, and 2xl is enough to cap hero typography.
Compact-density interaction
The site's density toggle is orthogonal to breakpoints. Density adjusts vertical rhythm and touch-target padding; breakpoints adjust column count and rail visibility. A user on lg with compact density gets the 3-column dashboard with tighter row padding — both axes are independent.
Touch-target floor
At every breakpoint, interactive touch targets stay ≥ 44×44 px (WIG touch rule). Density compaction never violates this floor. Compact density tightens spacing around touch targets, not the targets themselves.
What's in scope
| In scope (system owns) | Out of scope (consumer owns) |
|---|---|
| The six breakpoint values | Per-page custom breakpoints |
--breakpoint-{tier} CSS variables | App-specific media queries |
| Tailwind preset mapping | Component-level container queries |
What's deliberately not here
- Print breakpoints. Print styles live in
@matter/tokens/css/printas a separate stylesheet; the six tiers above don't apply to print. - Orientation-based queries.
@media (orientation: portrait)is consumer responsibility — no canonical token surface. - Hover-capable detection.
@media (hover: hover)is consumer responsibility for context-specific affordances.