Appearance
Utilities
A small set of unprefixed, Tailwind-like utility classes for composing layouts: display, flex-*, alignment, gap-*, position, inset-*, logical margin / padding, and text-* typography styles.
They are generated deterministically from the design tokens — the spacing scale comes straight from alias.space in tokens.json, so spacing utilities can never drift from the tokens. Add a space token, run npm run gen:utilities:css, and the matching classes appear.
html
<div class="flex flex-col gap-md items-start">
<div class="flex justify-between items-center gap-sm">…</div>
</div>Cascade
All utilities live in @layer utilities. Component styles (qs-button, qs-icon, …) are unlayered, and unlayered rules always beat layered ones — so a utility can never accidentally override a component's internals. Utilities are for the space between and around components, not for reaching inside them.
Display
flex · inline-flex · grid · inline-grid · block · inline-block · inline · hidden
Flexbox
Direction: flex-row · flex-row-reverse · flex-col · flex-col-reverse
Wrap: flex-wrap · flex-nowrap · flex-wrap-reverse
Grow / shrink: flex-1 · flex-auto · flex-initial · flex-none · grow · grow-0 · shrink · shrink-0
Alignment
Items: items-start · items-end · items-center · items-baseline · items-stretch
Justify: justify-start · justify-end · justify-center · justify-between · justify-around · justify-evenly
Content: content-start · content-end · content-center · content-between · content-around · content-evenly · content-stretch
Self: self-auto · self-start · self-end · self-center · self-stretch
Gap
gap-*, gap-x-* (column), gap-y-* (row). Each step maps to a spacing token:
Values are rem-based (relative), so they scale with the root font size.
| Class | Token | Value |
|---|---|---|
gap-0 | — | 0 |
gap-xs | --space-xs | 0.25rem |
gap-sm | --space-sm | 0.5rem |
gap-md | --space-md | 1rem |
gap-lg | --space-lg | 1.5rem |
gap-xl | --space-xl | 2rem |
gap-2xl | --space-2xl | 3rem |
gap-3xl | --space-3xl | 4rem |
Position
static · relative · absolute · fixed · sticky
Pinning: inset-0 · inset-x-0 (inline) · inset-y-0 (block)
Spacing (logical)
Margin and padding use logical properties, so they follow the writing direction:
| Prefix | Property |
|---|---|
m-* / p-* | margin / padding (all sides) |
mx-* / px-* | margin-inline / padding-inline |
my-* / py-* | margin-block / padding-block |
ms-* / ps-* | *-inline-start |
me-* / pe-* | *-inline-end |
mt-* / pt-* | *-block-start |
mb-* / pb-* | *-block-end |
Each accepts the same scale as gap (0, xs … 3xl), e.g. px-lg, my-sm, me-md.
Typography
One class per text style in component.text. Each property reads a --text-<style>-* token:
text-hero · text-display · text-large-title · text-headline · text-title · text-body · text-footnote · text-label · text-print · text-code
html
<h1 class="text-hero">Print.</h1>
<p class="text-body">Generous body copy at 17px.</p>Accessibility
sr-only hides text visually but keeps it for screen readers. See visually hidden text.
html
<span class="sr-only">(opens in a new tab)</span>Background patterns
Discreet surface patterns built on one grid: a module of --space-lg with a major line every 4 modules. Minor marks use --color-pattern (barely there), major lines --color-border, so every pattern lines up with the others and with the layout spacing. Each theme draws its own through its tokens: a 24px module in Editorial, a denser 16px module in Industrial. Put them on large, calm surfaces (page sections, empty states, hero panels), never behind body text you need to read closely.
| Class | Pattern |
|---|---|
bg-grid | minor grid every module, major grid every 4 modules |
bg-dots | a dot at every module intersection, a stronger dot every 4 |
bg-lines | baseline rule every module, a stronger rule every 4 (scanlines in Industrial) |
html
<section class="bg-grid">…</section>Discreet movement
Add a motion modifier to a pattern. The pattern moves on a ::before layer with transform only (composited, off the main thread), and the loop moves exactly one major cell per cycle, so it never jumps. The settle variant plays when the element first appears; the showcase replays it each time you change the preview.
| Modifier | Motion | Rule |
|---|---|---|
bg-drift | eases one module over 5s (--duration-ambient-settle), then stays still | Use freely: it stops within 5 seconds (WCAG 2.2.2). |
bg-drift-loop | drifts forever, one major cell (4 modules) per 24s (--duration-ambient-loop, linear) | The page must offer a pause control that sets <html data-motion="paused">. |
Both stop entirely under prefers-reduced-motion: reduce. A motion modifier makes the element position: relative, isolation: isolate and overflow: clip, so don't use it on an element that has popovers or menus spilling out of it.
html
<section class="bg-dots bg-drift-loop">…</section>
<button type="button" onclick="(d => d.dataset.motion ? delete d.dataset.motion : d.dataset.motion = 'paused')(document.documentElement)">
Pause or play background motion
</button>WebGPU showcase (docs only)
A CRT noise field for Industrial, drawn with Three.js TSL on a <canvas>: faint animated grain plus scanlines, in the current theme's --color-canvas and --color-text. It is a demo of what the token colours can drive, not part of the library: the CSS patterns above are what components and pages should use. It loops, so it has its own pause button and starts paused under reduced motion. Without WebGPU it falls back to WebGL 2, then to bg-lines.
Regenerating
bash
npm run gen:utilities:cssThe extra spacing roles --space-2xs, --space-4xl, --space-5xl, --space-gutter and --space-margin are intentionally left out of the scale — use them directly via var(--space-gutter) when a layout calls for them.