Layout
The kit has no twelve-column grid, no .row, no .col-6 — on purpose. Not because layout does not matter, but because the browser already does it. This page shows what the old grid frameworks were solving, and the native CSS that replaced them.
Why there is no grid system
A grid framework is the one component almost every kit ships and the one this kit deliberately leaves
out. The reason is simple: CSS now has a real layout system, and it is standard.
Grid, Flexbox and the gap property do everything the old .row /
.col / gutter model did — with less markup, no wrapper divs, and nothing to learn
that is not the actual language.
So a kit that ships its own twelve-column grid today is handing you a heavier, non-standard wrapper around something your browser already does — and, worse, it quietly stops you learning the real thing. That is the trap this page exists to keep you out of: reaching for a kit instead of the two or three lines of CSS that would have done it and stayed with you forever.
What a grid framework was actually solving
Bootstrap’s grid was a genuinely brilliant answer — to a 2013 problem. Back then
CSS had no layout tools at all: you positioned things by floating them and clearing
the float, and there was no way to put space between items without hacks. The grid papered
over all of that — a .row with negative margins, .col-* children with
matching padding to fake the gutters, and a pile of responsive classes because there was no other way
to reflow.
Every one of those problems has since been fixed in the platform. Grid landed in every
browser in 2017; gap works in Flexbox too as of 2021; container queries arrived in 2023.
The framework solved a real problem — the problem just does not exist anymore.
The cheat sheet
If you know the Bootstrap grid, you already know the shape of this. Here is the whole translation:
| Bootstrap grid | Native CSS today |
|---|---|
.container | max-width + margin-inline: auto |
.row | display: grid (or display: flex) |
| gutters (row negative margin + col padding) | gap — one property, no hacks |
.col-6 | grid-column: span 6 on a 12-track grid |
.col-md-4 (responsive) | repeat(auto-fit, minmax(16rem, 1fr)) — often no breakpoint at all |
.col (fluid) | flex: 1 |
.g-3 (gutter size) | gap: 1rem |
nesting a .row in a .col | a grid inside a grid — just works, no wrapper |
The one row worth staring at is the gutters one. gap is your gutters
— the negative-margin dance the old grids did existed only because there was no
gap. There is now, and it works in both Grid and Flexbox.
The pattern you will use most
Nine times out of ten you do not want fixed columns at all — you want “lay these cards out, as many across as fit, and reflow on small screens.” That is one rule, and it needs no breakpoints:
.cards {
display: grid;
/* as many columns as fit, each at least 16rem, sharing the space */
grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
gap: var(--space-4);
}
<div class="cards">
<div class="_78-card">…</div>
<div class="_78-card">…</div>
<div class="_78-card">…</div>
</div>
That single repeat(auto-fit, minmax(…)) replaces the whole stack of
col-sm / col-md / col-lg classes. The browser does the reflow; you wrote three lines and
no breakpoints.
When you really do want columns
Sometimes you genuinely need set proportions — a wide main area beside a narrow rail. Ask for a twelve-track grid and span across it. This is the Bootstrap grid, in native CSS, in five lines:
.layout {
display: grid;
grid-template-columns: repeat(12, 1fr);
gap: var(--space-4);
}
.layout__main { grid-column: span 8; }
.layout__rail { grid-column: span 4; }
For a plain horizontal row — a toolbar, a set of buttons — Flexbox is even less to write,
and gap spaces it for you:
.toolbar {
display: flex;
flex-wrap: wrap;
gap: var(--space-3);
}
What the kit gives you instead
Not a grid framework — a handful of small conveniences on top of the native layout above:
- Spacing and flex utilities for the everyday one-liners — stacking, gaps, letting an element grow. See Utilities for the full list. They are shorthands for the same CSS above, not a layer over it.
-
._78-grid— the auto-fit pattern above as one class, with the one part that is genuinely hard solved for you: a child that spans two columns when there is room and the whole row when there is not. Below. -
._78-kpi-gridand._78-card-grid— presets of._78-gridfor dashboard stat rows and general cards, so you do not re-type the minimum on every page. -
Design tokens for spacing — the
--space-*scale used in every snippet here, so your gaps stay consistent with the rest of the UI instead of being one-off pixel values.
The rule the kit holds to: it adds a component only when the browser makes something genuinely hard and the kit can solve it once, well. Layout is not that — the platform already solved it. The kit gives you the tokens and a couple of presets, and gets out of the way of the CSS you should own.
._78-grid
Auto-fit columns no narrower than --min (200px by default), a --space-4 gap,
and no breakpoints. Set --min on the grid itself to change the track size.
Spanning is the part plain auto-fit gets wrong. Give a child grid-column: span 2 and the
moment the grid is down to one column it invents a second, implicit one, and the row runs off the side
of the screen. ._78-col-span-2 is two columns when the grid has two, and the whole row
when it has one; ._78-col-full is always the whole row.
Two columns when there's room, the whole row when there isn't.
A plain cell.
A plain cell.
Always the whole row.
<div class="_78-grid">
<div class="_78-card _78-col-span-2">
<div class="_78-card-title">Span 2</div>
<p class="_78-text-dim">Two columns when there's room, the whole row when there isn't.</p>
</div>
<div class="_78-card">
<div class="_78-card-title">One</div>
<p class="_78-text-dim">A plain cell.</p>
</div>
<div class="_78-card">
<div class="_78-card-title">Two</div>
<p class="_78-text-dim">A plain cell.</p>
</div>
<div class="_78-card _78-col-full">
<div class="_78-card-title">Full</div>
<p class="_78-text-dim">Always the whole row.</p>
</div>
</div>
._78-kpi-grid and ._78-card-grid are presets of it, so a stat card can span on
a dashboard row too — the wide account card next to the narrow ones:
<div class="_78-kpi-grid">
<div class="_78-stat-card _78-col-span-2">
<div class="_78-stat-card__label">Account value</div>
<div class="_78-stat-card__num">$48,290</div>
<div class="_78-stat-card__sub">the wide card on the row</div>
</div>
<div class="_78-stat-card">
<div class="_78-stat-card__label">Open P/L</div>
<div class="_78-stat-card__num _78-success">+$1,204</div>
</div>
<div class="_78-stat-card">
<div class="_78-stat-card__label">Win rate</div>
<div class="_78-stat-card__num">61%</div>
</div>
</div>
| Class / property | What it does | Span 2 from |
|---|---|---|
_78-grid | Auto-fit grid, --min: 200px | 416px wide |
_78-card-grid | The same, for cards | 416px |
_78-kpi-grid | Preset, --min: 190px | 396px |
_78-kpi-grid-sm | With _78-kpi-grid: --min: 150px | 316px |
_78-kpi-grid-lg | With _78-kpi-grid: --min: 240px | 496px |
--min | The narrowest a column gets. Set it on the grid: style="--min: 260px" | — |
_78-col-span-2 | Two columns when the grid has two; the whole row when it has one | — |
_78-col-full | Always the whole row | — |
"Room for two" is a container query on the grid, and a container query can't read a custom
property — so each preset switches at its own fixed width, two minimums plus the gap. With a
--min of your own the span still switches at the preset's width; use
._78-col-full there, or the preset whose minimum matches.
Only a grid with a spanning child becomes a size container. That keeps every other grid exactly as
it was, but a grid that does have one needs a width from its surroundings: it is fine in normal flow
or a grid cell, and in a flex row give it flex: 1.