Skip to content
Monogem

Grids

Overview

Grids set how a page divides horizontally and how that division responds to viewport width: columns, gutters, and breakpoints. Breakpoints and the column count are genuinely new values — off the master scale, the same way Shadows were — so this foundation ships real tokens for those. Gutters are not new: they're a two-step recipe against the scale, the same move Spacing made.

Source of truth: tokens/semantic.css in the Monogem codebase.

Principles

  • One column count, everywhere. --grid-columns is the single knob any grid container reads — no page invents its own column count.
  • Breakpoints are a shared ladder. Five steps, matching Tailwind's, so this system's breakpoints and a future Tailwind config never disagree.
  • Gutters ride the scale. No parallel spacing system for "grid gaps" — the same master scale that handles padding and margin handles gutters too.
  • Restraint. Twelve columns, five breakpoints, two gutter steps — the ladder a real layout needs, not the finest one imaginable.

Tokens

TokenValueUse for
--breakpoint-sm40rem (640px)Phone landscape, small tablet
--breakpoint-md48rem (768px)Tablet portrait
--breakpoint-lg64rem (1024px)Small laptop
--breakpoint-xl80rem (1280px)Desktop
--breakpoint-2xl96rem (1536px)Wide desktop
--grid-columns12Column count for any grid container

No --breakpoint-xs. This is a mobile-first, min-width system — the unprefixed styles (no media query at all) already are the smallest-screen styles, covering everything from 0px up to --breakpoint-sm. That floor is tested down to a 320px viewport (iPhone SE and similar), no separate token needed.

.grid-container {
  display: grid;
  grid-template-columns: repeat(var(--grid-columns), 1fr);
  gap: var(--scale-4); /* mobile gutter — see recipe below */
}

Gutters add no new token. Reach for the scale directly, at two steps:

ViewportGutterToken
Below --breakpoint-md (768px)16px--scale-4
--breakpoint-md and up24px--scale-6
.grid-container {
  gap: var(--scale-4);
}
@media (min-width: 48rem) { /* mirrors --breakpoint-md */
  .grid-container { gap: var(--scale-6); }
}

Why the media query repeats 48rem instead of var(--breakpoint-md): a CSS custom property cannot be read inside a raw @media condition — that's invalid CSS. Hand-authored CSS repeats the value; a future Tailwind v4 build step is what turns --breakpoint-* into real generated media queries and variants. The token is still the source of truth a human reads and updates.

Rationale

Why 12 columns? It divides evenly by 2, 3, 4, and 6 — halves, thirds, quarters, and sixths all land on whole columns with nothing left over. It's also the count most designers already think in, so the mental model transfers instead of being invented from scratch.

Why CSS Grid instead of a float/flexbox row-and-column system? display: grid with grid-template-columns: repeat(12, 1fr) needs no wrapper "row" markup to make columns behave, and gives real two-dimensional placement for later when components need to span multiple rows. Float- or flexbox-based row systems are a workaround Grid makes unnecessary.

Why ship --grid-columns as a token for "just a number"? It's a genuinely new value (not a scale step, not a color) and — like --radius — a real interface a grid container reads directly. One knob changes the system's column count everywhere at once.

Why five breakpoints, in rem? Matches Tailwind v4's own values, so a future Tailwind config and these tokens are the same numbers, nothing to reconcile. rem (not px) means a reader's browser zoom crosses breakpoints at the same perceived width — consistent with how every other size in this system is already expressed.

Why does Gutters ship zero new tokens, same as Spacing? The scale already names the distance — a --gutter alias would be pure indirection for no gain, the same call Spacing made.

Why two gutter steps, not one per breakpoint? Tailwind's own container utility scales padding at every breakpoint (five distinct values). Restraint says two intent levels — compact (mobile) and comfortable (tablet and up) — cover what real layouts need today; a five-step ladder is precision to add later on real demand, not now.

Do / Don’t

Do

  • Read --grid-columns for the column count — never hard-code 12 in a component.
  • Use --breakpoint-* values as the source of truth when writing a media query, even though the query itself must repeat the pixel/rem value.
  • Reach for --scale-4 / --scale-6 for gutters — never invent a one-off gap value.

Don’t

  • Invent a --gutter or --grid-gap alias — reference the scale directly (Restraint).
  • Add a sixth breakpoint or a finer gutter ladder without a real, repeated need.
  • Confuse a grid's gutter with a container's outer margin — that's Layout's job, one foundation over.