Utilities
A deliberately small, token-bound utility surface — a hand of cards, not a vocabulary; no arbitrary values, no responsive prefixes.
Display
.k-d-none·.k-d-block·.k-d-flex·.k-d-inline-flex·.k-d-grid- One display value each. That's the whole set — there is no utility for every display mode, only the ones a page actually switches between.
.k-mobile-only·.k-desktop-only- Show an element on only one side of keel's md
breakpoint (820px):
.k-mobile-onlyrenders below it and disappears above;.k-desktop-onlydoes the opposite. This is the only responsive behavior in the whole utilities layer. .k-visually-hidden- Hides an element visually while keeping it in the
accessibility tree — screen readers still announce it. Use it
for skip links, icon-button labels, and headings that structure
a page for assistive tech. Never use
display: none(or.k-d-none) for content you want announced; that removes it for everyone. .k-print-hide- Hides an element only in
@media print— for navigation, floating actions, and anything else that has no business on a printed page.
.k-mobile-only and
.k-desktop-only can't be demoed on the page itself —
resize this window past 820px, or see them in action in the
site's own header on a phone-width screen.
Markup
<a class="k-visually-hidden" href="#main">Skip to content</a>
<p class="k-mobile-only">Shown below 820px.</p>
<p class="k-desktop-only">Shown at 820px and up.</p>
<div class="k-d-flex k-gap-2">…</div>
<div class="k-d-grid k-gap-2" style="grid-template-columns: repeat(2, auto)">…</div>
Spacing
Properties on the token scale, nothing else. No negative values — if you need those, write CSS.
| Class pattern | Property | Scale |
|---|---|---|
.k-mt-* | margin-top | 0–8 |
.k-mb-* | margin-bottom | 0–8 |
.k-ml-* | margin-inline-start | 1–4 |
.k-mr-* | margin-inline-end | 1–4 |
.k-p-* | padding | 0–6 |
.k-px-* | padding-inline | 2–5 |
.k-py-* | padding-block | 2–5 |
.k-pt-* · .k-pb-* | padding-block-start / -end | 2–4 |
.k-pl-* · .k-pr-* | padding-inline-start / -end | 2–4 |
.k-gap-* | gap | 1–6 |
.k-mx-auto | margin-inline: auto | — |
Each step is the matching --k-space-* token
(.k-mt-4 is margin-top: var(--k-space-4);
step 0 is a plain zero), so everything still scales with
--k-density.
Markup
<div>baseline</div>
<div class="k-mt-2">.k-mt-2</div>
<div class="k-mt-5">.k-mt-5</div>
<div class="k-mt-8">.k-mt-8</div>
Text
.k-text-xs·.k-text-s·.k-text-l·.k-text-xl- Font size from the type scale. There is deliberately no
.k-text-m— that's the body default — and no utility for the heading sizes; headings are headings. .k-text-soft·.k-text-faint·.k-text-accent·.k-text-accent-2·.k-text-accent-3- Text color from the tokens: softened, faint, or one of the three brand colors.
.k-text-ok·.k-text-warn·.k-text-danger- The three semantic status colors — the same ones the alert and badge variants use.
.k-text-center·.k-text-start·.k-text-end- Text alignment. Logical properties, so they follow the writing direction.
.k-bold- Font weight 650 — the same weight keel uses for strong text and display type.
.k-mono- The mono font stack, for inline figures and identifiers.
.k-uppercase- Uppercase with letter-spacing and a drop to the
xs size — an opinionated label style, not a bare
text-transform. .k-nowrap- Prevents wrapping. For dates, figures, and short phrases that read wrong when broken.
.k-truncate- One line, cut with an ellipsis. Needs a constrained width to have anything to truncate against.
.k-balancetext-wrap: balance— evens out line lengths. Headings already get this from the base layer; use it on the odd standfirst or card blurb.
Markup
<p class="k-truncate" style="max-width: 16rem">
A line longer than its box gets cut with an ellipsis…
</p>
Flex & size
.k-items-center·.k-items-start·.k-items-end·.k-items-baseline- Cross-axis alignment for a flex or grid parent.
.k-justify-between·.k-justify-center·.k-justify-start·.k-justify-end- Main-axis distribution.
.k-flex-col·.k-flex-wrap- Stack a flex row into a column, or let it wrap.
.k-growflex-grow: 1— the child that takes the leftover room..k-flex-noneflex: none— the child that must not shrink or grow: icons, avatars, buttons in a row..k-w-full- Full inline size.
.k-maxw-narrow·.k-maxw- Max inline size capped at the container-width tokens
(
--k-width-narrow,--k-width) — for capping an element without introducing a new container. .k-aspect-square·.k-aspect-video·.k-aspect-portrait- Fixed
aspect-ratio(1/1, 16/9, 3/4) for any element — the same ratios.k-frame's modifiers use, available without the frame's other styling.
Markup
<div class="k-d-flex k-items-center k-justify-between k-gap-3">
<span class="k-grow k-truncate">A long filename that truncates</span>
<button class="k-btn k-btn--small k-flex-none">Open</button>
</div>
<div class="k-aspect-square" style="width: 4rem">…</div>
<div class="k-aspect-video" style="width: 6rem">…</div>
<div class="k-aspect-portrait" style="width: 3.5rem">…</div>
Surface
.k-rounded-s·.k-rounded·.k-rounded-l·.k-rounded-full- Corner radius from the shape tokens;
-fullis a pill or circle. .k-border- The standard one-pixel border in the border color. Combine with a side or color utility below.
.k-border-t·.k-border-b·.k-border-l·.k-border-r- One-pixel border on a single logical side, for dividers and table-row rules.
.k-border-accent·.k-border-accent-2·.k-border-accent-3·.k-border-ok·.k-border-warn·.k-border-danger- Recolors an existing border to a token — pair with
.k-borderor a side utility; on its own it has nothing to color. .k-shadow·.k-shadow-l- The token shadows — the same ones cards and dialogs use.
.k-bg-surface·.k-bg-surface-2·.k-bg-accent-soft·.k-bg-accent-2-soft·.k-bg-accent-3-soft- Background from the tokens: surface, raised surface, and a
soft wash of each brand color. Every color utility here points
at a token, never a raw color.
.k-bg-softis a shorter alias for.k-bg-accent-soft— same token, same value, just less to type when there's only one accent in play. .k-bg-ok-soft·.k-bg-warn-soft·.k-bg-danger-soft- The same soft-wash treatment for the three status colors —
for inline status chips that aren't a full
.k-alert. .k-bg-gradient·.k-bg-gradient-soft- The derived two-color gradient (
--k-gradient) and its soft, low-opacity version — a hero backdrop or a subtle section wash, without hand-mixing a gradient of your own. .k-text-gradient- Clips the same gradient to text instead of a background — for one accent word in a heading, not a whole paragraph (a gradient fill hurts legibility past a few words).
.k-bg-mesh·.k-bg-mesh--live- A soft, multi-point radial gradient built from both accent
seeds — hero material.
--livelets the points drift slowly and continuously; both are covered in full, with a live example, under Surface effects. .k-sticky-topposition: stickypinned to the viewport top — for a sticky header or table toolbar. Pair with.k-glassfor a frosted sticky bar.
Markup
<div class="k-bg-surface k-border k-rounded k-p-3">…</div>
<div class="k-bg-surface-2 k-rounded-l k-p-3">…</div>
<div class="k-bg-accent-soft k-rounded-full k-px-4 k-py-2">…</div>
<div class="k-bg-surface k-border k-border-accent k-rounded k-p-3">…</div>
<div class="k-bg-ok-soft k-text-ok k-rounded k-p-3">…</div>
<div class="k-bg-gradient k-rounded k-p-3">…</div>
<h2><span class="k-text-gradient">One accent word</span> in a heading</h2>
Motion
Entry animations and interaction utilities from keel's motion
layer — one-shot on mount, and off entirely under
prefers-reduced-motion: reduce. See
Motion for the full set,
including scroll-driven reveal and the progress bar.
.k-anim-fade·.k-anim-up·.k-anim-pop·.k-anim-slide- Fade, rise, pop, or slide in once on mount. Duration is the
--k-anim-durationtoken (450ms default). .k-stagger- Put on a parent and its children get increasing entry
delay automatically, no per-child class needed. Where
sibling-index()is supported the step is computed per child, so a list of any length staggers all the way through; elsewhere it falls back to six fixed steps, with everything past the sixth child sharing the last one. .k-anim-delay-1·.k-anim-delay-2·.k-anim-delay-3·.k-anim-delay-4- Manual 80ms delay steps for offsetting a single element's
entry animation outside a
.k-staggerlist. .k-lift- Generic hover lift — translate and shadow — for any element,
not just cards. Scoped to
@media (hover: hover) and (pointer: fine), so a tap on a touch screen can't leave it stuck lifted — touch has no hover-out event to reverse the transition. Reach for the same media query on any custom hover effect you write; it's the general fix for "sticky hover" on touch, not something a class alone can express. .k-marquee/.k-marquee__track- Continuous horizontal scroll for a logo strip or ticker.
Duplicate the track's children once so the loop is seamless;
pauses on hover. Speed is the
--k-marquee-durationtoken (24s default).
Markup
<div class="k-d-flex k-gap-3 k-stagger">
<div class="k-anim-up">1</div>
<div class="k-anim-up">2</div>
<div class="k-anim-up">3</div>
</div>
<div class="k-marquee">
<div class="k-marquee__track">
<span>Logo A</span> <span>Logo B</span> <span>Logo C</span>
<span aria-hidden="true">Logo A</span> <span aria-hidden="true">Logo B</span> <span aria-hidden="true">Logo C</span>
</div>
</div>
When not to use utilities
- If the same cluster of utilities keeps appearing across pages, that's a component asking to exist — name it once instead of repeating it.
- If a whole page feels too tight or too airy, don't sprinkle
spacing utilities everywhere — change
--k-densityonce and let every space scale with it. - If the brand changes, change the tokens. Never reach for color utilities to repaint pieces of a page — that's how a design system stops being one.