Layout primitives for composing responsive regions.
| Prop | Type | Notes |
|---|---|---|
| columns | number = 12 | Track count on the row; 12 is the base scale |
| gap | sm · default · lg | 12 · 24 · 32px between tracks |
| span | 1–12 | How many tracks a column occupies |
| offset | 0–11 | Empty tracks before the column starts |
| maxWidth | number = 1280 | Container cap; content centres inside it |
| reflow | md · sm | Fewer tracks at ≤767px, single column ≤479px |
The Grid holds no state and fires no events — it reflows through media queries alone, so the layout is correct on the server before a single line of JavaScript runs.
Two more properties aren't visible in a single frame: offset pushes a column inward by leaving leading tracks empty, and reflow drops the track count at the md and sm breakpoints. Both are in the contract above.
Compose regions from spans of a shared 12-track row. The gap stays consistent everywhere.
Don't hard-code pixel widths per column. They break the moment content or the viewport changes.
Use the gap tokens (12 / 24 / 32) for gutters, so rhythm matches the rest of the page.
Don't leave spans that don't total the columns unless the trailing space is intentional.
Four-across stat cards on a 3-span rhythm.
An 8+4 split for content beside a rail.
Paired fields on 6+6, full-width where needed.
| Token | Value | Role |
|---|---|---|
| --grid-columns | 12 | Tracks on the base row |
| --grid-gap | 24px | Default gutter between tracks |
| --grid-gap-sm | 12px | Tight gutter |
| --grid-gap-lg | 32px | Roomy gutter |
| --container-max | 1280px | Page container cap |
| --container-pad | 0 24px | Container horizontal padding |
| --bp-md | ≤ 767px | Row collapses toward 6 tracks |
| --bp-sm | ≤ 479px | Row becomes a single column |
<div class="o-container">
<div class="o-row">
<div class="o-col" style="--span:8">Main</div>
<div class="o-col" style="--span:4">Sidebar</div>
</div>
</div>