Layout

One grid carries every screen: four columns on a phone, eight on a tablet, twelve from a laptop up. Everything else — gutters, page padding, the rhythm between sections — is a token, never a number typed into a component.

The grid

BreakpointFromColumnsInner gutterOuter gutterContainer
xs0412px1.175remauto
sm640px412px1.5remauto
md768px812px2.5remauto
lg1024px1216px4remauto
xl1280px1216px4remmin(1440px, 100%)
2xl1536px1218px4rem1440px

The plugin emits these as --grid-columns, --inner-gutter, --outer-gutter and --container-width. Read the variables; never re-derive the numbers. --container-width in particular is auto below xl, so arithmetic on it silently invalidates the whole declaration.

Columns in place

grid-layout is the grid; grid-col-span-N places a child on it. The darker bars are the four that survive on a phone. Resize the window and the count follows the table above — the grid adapts, the markup does not.

Spacing

space-1

xs 16px · lg 20px

space-2

xs 22px · md 24px · xl 28px

space-3

xs 32px · lg 40px

pad-1

xs 13px · md 16px · lg 17px

pad-2

xs 20px · lg 24px

pad-3

xs 32px · lg 36px

Each token already shifts per breakpoint. Writing gap-space-1 lg:gap-space-2 bypasses that curve and skips a step — pick one token and let it adapt.