Utilities
A deliberately small helper layer — the handful of one-liners you would otherwise write a hundred times. This is all of it; the kit is not a utility framework, and anything not listed here does not exist.
Text
Color and size helpers, all reading kit tokens so they follow the theme.
<div class="site-demo-block">
<div>Default body text</div>
<div class="_78-text-dim">_78-text-dim — secondary text</div>
<div class="_78-text-sm">_78-text-sm — 12px</div>
<div class="_78-text-xs">_78-text-xs — 11px</div>
<div class="_78-text-accent">_78-text-accent</div>
<div class="_78-text-success">_78-text-success</div>
<div class="_78-text-warn">_78-text-warn</div>
<div class="_78-text-danger">_78-text-danger</div>
<div class="_78-text-info">_78-text-info</div>
<div class="_78-fw-600">_78-fw-600 — semibold</div>
<div class="_78-mono">_78-mono — the monospace stack</div>
<div class="_78-nums">_78-nums — 1,234,567 tabular figures</div>
</div>
| Class | Declaration |
|---|---|
_78-text-{tone} | color: var(--{tone}) — any of the six tones: accent success warn danger info dim |
_78-text-sm | font-size: var(--fs-sm) |
_78-text-xs | font-size: var(--fs-xs) |
_78-fw-600 | font-weight: 600 |
_78-mono | font-family: var(--font-mono) |
_78-nums | font-variant-numeric: tabular-nums |
_78-nums matters more than it looks: without tabular figures a column of numbers
jitters as the digits change. _78-right inside a
table already includes it.
Two spellings, one color
Every text tone has a short form too: _78-danger is the same rule as
_78-text-danger, for all six tones. The short forms started life on
_78-stat-card__num and work anywhere the long spelling does — so neither form is a
typo, and you will see both in real code. The tone aliases (green, red,
amber, yellow) work in both spellings as well; see
Tones.
<div class="site-demo-block">
<div class="_78-accent">_78-accent — the same as _78-text-accent</div>
<div class="_78-success">_78-success — the same as _78-text-success</div>
<div class="_78-warn">_78-warn — the same as _78-text-warn</div>
<div class="_78-danger">_78-danger — the same as _78-text-danger</div>
<div class="_78-info">_78-info — the same as _78-text-info</div>
<div class="_78-dim">_78-dim — the same as _78-text-dim</div>
</div>
Prefer the _78-text-* spelling in new code — it says that the class colors text,
which the short form does not. The short forms exist so that markup written against the stat card keeps
working, and so that a habit picked up there is not punished elsewhere.
accent, green, warn,
red and dim, so _78-text-danger, _78-text-success
and _78-text-info silently did nothing. All six canonical names work now, and the old
names still do.
Aurora text
_78-text-aurora runs a slow multi-color gradient through the letters — for a hero
word, a role badge, a wordmark. It is one class on any element that holds text, and its default colors
are the accent and three category hues, so it re-themes with everything else.
Theme everything, live.
A role badge: Founder · a wordmark: Meridian
<div class="site-demo-block">
<p style="font-size: 34px; font-weight: 700; line-height: 1.15">
Theme <span class="_78-text-aurora">everything</span>, live.
</p>
<p>
A role badge: <span class="_78-badge"><span class="_78-text-aurora">Founder</span></span>
· a wordmark: <strong class="_78-text-aurora" style="font-size: var(--fs-xl)">Meridian</strong>
</p>
</div>
Your own colors and tempo
--_78-aurora is the color list and --_78-aurora-speed the time for one pass.
End the list on the color it starts with and the loop has no seam.
<strong class="_78-text-aurora" style="font-size: var(--fs-xl); --_78-aurora: var(--success), var(--info), var(--success); --_78-aurora-speed: 3s">
Faster, and greener
</strong>
| Property | Default | What it does |
|---|---|---|
--_78-aurora | --accent-hover, --cat-1, --cat-3, --cat-5, --accent-hover | The gradient's colors. |
--_78-aurora-speed | 6s | One full pass. |
Every point of the default gradient clears 4.5:1 against --bg, --bg2 and
--bg3 in both themes — it uses --accent-hover rather than
--accent because that is the higher-contrast end of the accent in both. Check your own
colors the same way.
With reduce motion turned on, the gradient stands still. In Windows high-contrast mode, or a
browser without background-clip: text, the text is drawn solid — never invisible.
Keep it to a word or a short phrase: moving color across a paragraph is hard to read.
Truncate
_78-truncate is the three-property incantation for a single-line ellipsis. It needs a
constrained width to do anything — in a flex row, that usually also means
min-width: 0 on the item.
<div class="_78-truncate">A workspace name far too long to fit…</div>
Layout
Just enough flex to stop you writing the same four lines everywhere. Note that
_78-flex also sets align-items: center, which is what you want nine times
out of ten.
| Class | Declaration |
|---|---|
_78-flex | display: flex; align-items: center |
_78-flex-between | …plus justify-content: space-between |
_78-flex-wrap | flex-wrap: wrap |
_78-flex-col | display: flex; flex-direction: column |
_78-grow | flex: 1 1 auto — on a child, to take the slack |
_78-gap-8 | gap: var(--space-2) |
_78-gap-12 | gap: var(--space-3) |
_78-gap-16 | gap: var(--space-4) |
<div class="site-demo-block">
<div class="_78-flex _78-gap-8">
<span class="_78-badge _78-badge-accent">Team</span>
<span class="_78-fw-600">Northwind</span>
<span class="_78-text-dim _78-text-sm _78-grow">48 seats</span>
<button class="_78-btn _78-btn-sm" type="button">Open</button>
</div>
<div class="_78-flex-between">
<span class="_78-text-dim">_78-flex-between</span>
<span class="_78-badge _78-badge-success">pushed right</span>
</div>
</div>
Spacing
Single-axis margins only, on the space scale. There is no padding utility and no left/right margin utility — those are layout decisions that belong in your own CSS.
| Class | Value | Token |
|---|---|---|
_78-mt-8 | 8px top | --space-2 |
_78-mt-12 | 12px top | --space-3 |
_78-mt-16 | 16px top | --space-4 |
_78-mb-4 | 4px bottom | --space-1 |
_78-mb-8 | 8px bottom | --space-2 |
_78-mb-12 | 12px bottom | --space-3 |
_78-mb-16 | 16px bottom | --space-4 |
The names are the pixel values at default density, but the declarations use the tokens — so a generated theme with a tighter density scales them with everything else.
The escape hatch
The scale stops at 16px, and there are no padding utilities at all. That is deliberate rather than an omission: past 16px, spacing is layout, and a utility layer that keeps going ends up being the framework the kit is not. Above the scale, use the space tokens directly — that is the intended way out.
<!-- above the utility scale, or any padding at all -->
<div style="padding: var(--space-5)">…</div>
<section style="margin-top: var(--space-6)">…</section>
<!-- or in your own stylesheet, which is where anything repeated belongs -->
<style>.report-head { padding: var(--space-5) var(--space-4) }</style>
The tokens run --space-1 through --space-6 (4 · 8 · 12
· 16 · 24 · 32px) and scale with the theme's density, which a hard-coded
24px would not. They are listed with every other token on the
Theming page.
Lists
The reset strips list padding everywhere, on purpose. Inside the components that hold prose
(_78-alert-body, _78-card, _78-modal-body and
_78-details-body) a <ul> or <ol> with no class of
its own gets an indent and its markers back. Everywhere else, lists stay bare. The rule is written with
:where(), so any rule of yours out-ranks it.
_78-list-plain takes the markers and the indent off, anywhere.
- Indented, markers inside the padding
- A little space between items
- No markers
- No indent
- Still a list to a screen reader
<div class="_78-card-grid">
<div class="_78-card">
<div class="_78-card-title">A plain list in a card</div>
<ul>
<li>Indented, markers inside the padding</li>
<li>A little space between items</li>
</ul>
</div>
<div class="_78-card">
<div class="_78-card-title">_78-list-plain</div>
<ul class="_78-list-plain">
<li>No markers</li>
<li>No indent</li>
<li>Still a list to a screen reader</li>
</ul>
</div>
</div>
JS helpers
Two small functions on _78.util, with no dependencies and no DOM. They are the two
pieces of ad-hoc code every dashboard ends up writing. New in 0.3.0
sortAlpha(a, b)
A comparator for human sorting: case-insensitive, locale-aware, and numbers compared as numbers, so
“Item 2” comes before “Item 10”. Blanks (null,
undefined, "") sort last. Pass it straight to array.sort(), or
use it as a Tabulator column's sorter.
sort(): sortAlpha: ['Item 10', 'item 2', 'Item 1'].sort(_78.util.sortAlpha);
// → ['Item 1', 'item 2', 'Item 10']
new Tabulator('#accounts', {
columns: [{ title: 'Customer', field: 'customer', sorter: _78.util.sortAlpha }]
});
duration(ms, options)
Milliseconds as plain English. The last part shown is rounded, with carry, and a part that rounds to zero is left out — so it never prints “0 min”. Days are the largest unit; anything that isn't a number comes back as an empty string.
| Option | Default | What it does |
|---|---|---|
long | false | true spells the units out: “2 hours 5 minutes” instead of “2 hrs 5 min”. |
parts | 2 | The most units to show. 1 gives “3 days”, 3 gives “3 days 4 hrs 50 min”. |
Rendered live by the kit this page loads:
| ms | duration(ms) | { long: true } | { parts: 1 } |
|---|
_78.util.duration(45000); // '45 sec'
_78.util.duration(7500000); // '2 hrs 5 min'
_78.util.duration(7500000, { long: true }); // '2 hours 5 minutes'
_78.util.duration(276600000, { parts: 3 }); // '3 days 4 hrs 50 min'
_78.util.duration(-180000); // '-3 min'
Screen-reader only
_78-sr-only hides content visually while leaving it available to assistive technology.
It is the correct tool for a table <caption> you do not want to show, for extra
context inside a button, and for giving an icon-only control a name where
aria-label will not do.
<button class="_78-btn" type="button">
Export
<span class="_78-sr-only">— downloads a CSV of the current view</span>
</button>
hiddendisplay: none and the hidden attribute remove content from
everyone, screen readers included. Use _78-sr-only when the information
should still be announced.