Layout primitives¶
Nine classes compose almost every page. They are deliberately unopinionated about what they contain, and they are meant to be put on a component rather than around it:
Why layout.css loads last
Both selectors above are one class, so the cascade is decided by file order. With layout
first, the card's own display: flex would win and the cluster would silently do nothing. A
layout primitive that loses to the thing it is applied to is not a primitive.
The nine¶
ads-stack- Vertical rhythm. Children are spaced by one gap; no per-element margins to collapse or double. The default container for a form, a page, a card body.
ads-cluster- A horizontal group that wraps. Buttons in a toolbar, filters above a table, tags in a row.
ads-spread- A row with the first child pushed left and the last pushed right. A card head with a title and an action.
ads-grid- Auto-fitting columns. The track is
minmax(min(var(--ads-col, 16rem), 100%), 1fr)— themin(…, 100%)is what keeps it from overflowing a 320px screen. Set--ads-colto change the target column width, or useads-col-sm/ads-col-lg. ads-with-aside- Content plus a sidebar that drops below when there is no room, with no media query — the
main pane has
flex-grow: 999so it takes everything until the aside's basis no longer fits. ads-split- Two panes that scroll independently and fill the viewport height. A mail client, a master-detail view. Collapses to one column below 62em.
ads-fill- Take the remaining room in a flex row, and allow truncation (
min-width: 0). ads-push- Push this item and everything after it to the far end.
ads-center/ads-centered- Centre a block horizontally;
ads-centeredis the full-page version used by sign-in and error pages.
Modifiers¶
<div class="ads-stack ads-gap-sm">…</div> <!-- tighter -->
<div class="ads-grid ads-col-lg">…</div> <!-- wider columns -->
Gaps come from the space scale, so they follow --ads-space-scale like everything else.
Utilities¶
ads-muted- Secondary text colour.
ads-truncate- One line, ellipsis. Needs a parent that allows shrinking — pair it with
ads-fill. ads-toned- Take the colour of the current tone (see below).
ads-skip- The skip-to-content link. Put it first in
<body>; it is visually hidden until focused.
Tones instead of variants¶
Components that can carry an intent read --ads-tone rather than growing a modifier per
intent:
<span class="ads-status ads-tone-warning">Degraded</span>
<div class="ads-alert ads-tone-danger">…</div>
<div class="ads-progress ads-tone-success">…</div>
Six tones — brand, success, warning, danger, info, neutral — each setting five
variables the component reads: --ads-tone (readable ink), --ads-tone-solid (the fill),
--ads-tone-tint, --ads-tone-fill and --ads-tone-line. Adding a seventh intent is one line
in derive.css, not one line per component.
Not everything is a card¶
Border, fill, radius and shadow each say separate object. Spending all four on every block
flattens the hierarchy until nothing stands out. Lead with a card when the content really is a
discrete object; otherwise a heading and an ads-stack are enough.
Reflow¶
Everything here is reflow-safe at 320 CSS pixels, which is a 1280px window at 400% zoom — the
setting someone with low vision actually uses. If you add a layout of your own, the rule that
catches most mistakes is: no absolute length may act as a floor. min-width: 22rem and
minmax(22rem, 1fr) both overflow a 20rem viewport; min(22rem, 100%) does not.