Breakpoints and layout
Six breakpoints — Tailwind’s five and one below them — and two layout tokens that step with them. The navigation is outside the layout; the content beside it is not.
The scale
| Name | rem | px | What turns there |
|---|---|---|---|
| xs | 30rem | 480px | Below it, a phone: the Toast full width, the CommandPalette’s description under the label, the site’s specimens edge to edge |
| sm | 40rem | 640px | No reader yet — Tailwind’s step, kept so sm: means the same everywhere |
| md | 48rem | 768px | Below it, the SideNav is a sheet and the site’s bar is narrow; the Tailwind default, 768 |
| lg | 64rem | 1024px | Margin 24 and gap 20 from here; the screen’s navigation leaves its sheet |
| xl | 80rem | 1280px | Margin 40 from here; two panes side by side; the site’s evidence column |
| 2xl | 96rem | 1536px | No component reader; the site’s wide page, with the section list beside the prose |
320px is the floor, not a breakpoint: the narrowest width the system is built and tested at, WCAG 1.4.10’s reflow. Every step is in rem, so a reader who sets a larger default font size gets the narrower layout sooner.
Using it
A media query cannot read a custom property, so the tokens reach each place in its own form. In CSS, write the query in rem and range syntax; in React, take it from media; in Tailwind, xs: exists and the other five are Tailwind’s own.
@media (width < 48rem) { … } /* below md */
@media (width >= 80rem) { … } /* xl and up */import { media } from 'alpenglow';
const narrow = useMediaQuery(media.down.md); // the app's own hook, or any--ap-breakpoint-* in tokens.css are for JavaScript and for reading. A test fails on any width in a media query outside the scale.
A layout of your own
Measure where the content breaks, then round to the safe step. This site’s wide page needs 1108 of page between the margins to hold a Table specimen beside the section list — 1500 of viewport, once the rail, the drawer and the margins are out — so it starts at 2xl, 1536; its evidence column needs the prose beside it to keep 704, which it does from xl. The tests hold the inequality, not the number.
The viewport and the container
The viewport decides the chrome and the page’s composition: where the SideNav becomes a sheet, when two panes sit side by side. A component that answers its own space answers its container — the Alert stacks its actions under 400px of its own width in any screen — and a container query is not a breakpoint.
Margin and gap
| Token | Narrow (below lg, 1024) | Medium (from lg, 1024) | Wide (from xl, 1280) | Where |
|---|---|---|---|---|
| layout/margin | 16px | 24px | 40px | Around the content region: from the navigation’s edge, the window’s edge and the TopBar |
| layout/gap | 16px | 20px | 20px | Between panes, and between the columns of a composition |
The margin is the space around the content region, from the navigation’s edge, the window’s and the TopBar alike; the TopBar pads its sides by it, so its start and end line up with the content. The gap is between panes and between the columns of a composition. Below xl the content is one pane, two behind Tabs; from xl two sit side by side. There is no 12-column grid in code: the Scheduler and the Table size themselves. In Figma the Alpenglow Layout collection has the three modes, and a 12-column grid style is bound to it as a guide. The package’s default keeps the SideNav a rail down to md; beside a Scheduler, pass it narrow={media.down.lg} instead, so a tablet gets the whole 736 rather than sharing it with the rail.
Layering
Alpenglow ships no z-index tokens. What floats goes to the top layer; what stacks inside a component stays in its own isolation: isolate, at 3 or under. The page’s stack is the product’s: a sticky bar at z-index: 1 sits over everything the package draws.
Not checked
Safari, Firefox, a real touch device, and text zoom beyond the browser’s default font size.