Grid System

Columns, gutters and the 1320px container every page hangs from.

v1.0.0·Updated Jul 10, 2026 · 23:00
One system, three widths

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.

The three grids — live
The web grid — 12 columns
1
2
3
4
5
6
7
8
9
10
11
12
12 columns · 24px gutter · clamp(24,5vw,64) margin · 1320px max
Same layout, this grid
Header · span 12
Sidebar · 3
Content · 9
Card · 3
Card · 3
Card · 3
Card · 3
Specifications
SystemAppliesColumnsGutterMarginContainer
Mobile< 768px416px16pxfluid
Tablet768 – 1023px824px32pxfluid
Web≥ 1024px1224pxclamp(24,5vw,64)1320px max
Anatomy
1ContainerCentres content and caps its width at 1320px on web.
2MarginThe space between the viewport edge and the first column.
3ColumnOne of 4 / 8 / 12 equal tracks a region can span.
4GutterThe consistent gap between columns — never a margin.
Spanning across devices

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:

RegionMobile (of 4)Tablet (of 8)Web (of 12)
Sidebar4 (stacks)23
Content469
Card (×4)2 (2-up)4 (2-up)3 (4-up)
Full-bleed4812
How to use it in code
/* 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); } }
Rules
Design mobile-up

Lay out the 4-column case first, then add tracks as space appears. Widening a layout is far easier than cramming one.

Spans, not pixels

Describe a region as 2 / 4 / 6, never a fixed width. The grid handles the maths at every size.

One gutter, one container

Every page shares the same gutter and the 1320px cap, so unrelated screens line up without effort.

Reach for the component

The Grid component ships this as Container / Row / Col with responsive span props.