Columns, gutters and the 1320px container every page hangs from.
Layout hangs from a single responsive grid that changes column count with the viewport: 4 columns on mobile, 8 on tablet, 12 on web. Regions are described as spans of those columns, one value per device, so the same markup reflows instead of being rebuilt. Web content is capped by a 1320px container; below that, the grid is fluid and margins do the breathing.
| System | Applies | Columns | Gutter | Margin | Container |
|---|---|---|---|---|---|
| Mobile | < 768px | 4 | 16px | 16px | fluid |
| Tablet | 768 – 1023px | 8 | 24px | 32px | fluid |
| Web | ≥ 1024px | 12 | 24px | clamp(24,5vw,64) | 1320px max |
A region declares a span per device. Because the column counts are multiples (4 → 8 → 12), a half-width block stays a half-width block: 2 / 4 / 6. A common app shell reads like this:
| Region | Mobile (of 4) | Tablet (of 8) | Web (of 12) |
|---|---|---|---|
| Sidebar | 4 (stacks) | 2 | 3 |
| Content | 4 | 6 | 9 |
| Card (×4) | 2 (2-up) | 4 (2-up) | 3 (4-up) |
| Full-bleed | 4 | 8 | 12 |
/* mobile-first — 4 columns */
.o-container { padding-inline: 16px; }
.o-row { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; }
@media (min-width: 768px) { /* tablet — 8 columns */
.o-container { padding-inline: 32px; }
.o-row { grid-template-columns: repeat(8, 1fr); gap: 24px; }
}
@media (min-width: 1024px) { /* web — 12 columns */
.o-container { max-width: 1320px; margin-inline: auto; padding-inline: clamp(24px, 5vw, 64px); }
.o-row { grid-template-columns: repeat(12, 1fr); gap: 24px; }
}
/* a region spans different tracks per device */
.o-col { grid-column: span var(--m, 4); }
@media (min-width: 768px) { .o-col { grid-column: span var(--t); } }
@media (min-width: 1024px) { .o-col { grid-column: span var(--d); } }Lay out the 4-column case first, then add tracks as space appears. Widening a layout is far easier than cramming one.
Describe a region as 2 / 4 / 6, never a fixed width. The grid handles the maths at every size.
Every page shares the same gutter and the 1320px cap, so unrelated screens line up without effort.
The Grid component ships this as Container / Row / Col with responsive span props.