Utility Classes
Azalea Kit UI ships an optional set of framework-agnostic utility classes for common layout, spacing, typography, and accessibility needs — so app teams don’t hand-roll flex/grid/spacing CSS that drifts from the design tokens.
Every utility on this page ships inside the theme. Load one and you have them all:
<link rel="stylesheet" href="/dist/themes/light.css" />
There is no second stylesheet to add. (utility.css used to be one; it was merged into the
themes and removed.)
All classes are prefixed ahi- and reference --ahi-* design tokens, so they track
the active theme. Everything lives in the ahi-utilities
cascade layer, which means
any unlayered CSS in your app overrides these utilities regardless of stylesheet load order
— treat them as low-priority defaults you can always override.
:::tip The type and accessibility utilities need no second stylesheet. They ship inside
every theme, so ahi-text-body, ahi-text-truncate,
ahi-visually-hidden and the rest are available as soon as light.css or
dark.css is on the page.
That is deliberate. Some apps embed AKU components and a theme and nothing else — for them,
<ahi-text> works
today only because its styles ride inside its own shadow root. Its replacement had to reach them the same
way. So does everything else on this page. :::
There is no
.ahi-stackutility.<div class="ahi-column">is deprecated — use.ahi-columnfor a vertical stack or.ahi-rowfor a horizontal one, with.ahi-gap-*and the alignment classes. Both ship inside the theme, so no extra stylesheet is needed.
Layout primitives
| Class | What it does |
|---|---|
ahi-cluster |
Inline row that wraps; vertically centered. Buttons, tags, chips, nav links. |
ahi-grid |
Responsive columns with no media queries. Tune the wrap threshold with
--ahi-min-column-size (default 20ch).
|
ahi-flank |
A fixed-size item beside a flexible one (media object). Set the fixed width with
--ahi-flank-size. Add ahi-flank-end to flank the last child instead.
|
ahi-split |
Pushes children to opposite ends (toolbars, headers with a trailing action). Add
ahi-split-column to stack vertically.
|
ahi-frame |
Fixed aspect-ratio box; children cover it. Add ahi-frame-landscape (16:9) or
ahi-frame-portrait (9:16); default is square.
|
<div class="ahi-cluster ahi-gap-2">
<ahi-button>Save</ahi-button>
<ahi-button variant="default">Cancel</ahi-button>
</div>
<div class="ahi-grid" style="--ahi-min-column-size: 16rem;">
<ahi-card>…</ahi-card>
<ahi-card>…</ahi-card>
<ahi-card>…</ahi-card>
</div>
<div class="ahi-flank ahi-gap-3" style="--ahi-flank-size: 3rem;">
<ahi-avatar></ahi-avatar>
<div>Name and supporting text that fills the remaining space.</div>
</div>
Use ahi-span-grid on a child to make it span the full width of an ahi-grid row.
Spacing
ahi-gap-* sets the gap on any flex or grid container, from the spacing scale
(--ahi-space-*): ahi-gap-0, -1, -2, -3,
-4, -5, -6, -8, -10, -12,
-16.
Alignment
-
Cross-axis:
ahi-align-items-start/-end/-center/-stretch/-baseline -
Main-axis:
ahi-justify-content-start/-end/-center/-space-between/-space-around/-space-evenly - Wrapping:
ahi-flex-wrap/ahi-flex-nowrap
Typography
Available from the theme alone — no second stylesheet. See type roles for what the roles mean and how a density tier moves them.
Roles
A role sets a size and its matching line height together. Pick what the text is, not how big it should be.
| Class | What it is | Example |
|---|---|---|
ahi-text-display |
Page-level titles. | Aa |
ahi-text-heading |
Section titles. | Aa |
ahi-text-subheading |
Card and group titles. | Aa |
ahi-text-body |
Running text, and most of the UI. | Aa |
ahi-text-body-emphasis |
Body, weight-shifted. Same metrics, heavier. | Aa |
ahi-text-label |
Form labels, table headers. | Aa |
ahi-text-caption |
Help text, timestamps, metadata. | Aa |
ahi-text-code |
Monospace, inline and block. | Aa |
Modifiers
Combine any of these with a role. Each is a single class, so order in the class attribute does
not matter.
| Group | Classes |
|---|---|
| Color |
ahi-text-subdued, -critical, -warning, -success,
-inherit
|
| Weight |
ahi-text-regular, -medium, -semibold, -bold
|
| Alignment | ahi-text-start, -center, -end, -justify |
| Transform | ahi-text-uppercase, -capitalize |
| Wrapping |
ahi-text-truncate (single-line ellipsis), -nowrap,
-break-word (breaks long URLs and identifiers), -clamp-2,
-clamp-3
|
| Other |
ahi-text-numeric (tabular figures, so columns of numbers line up),
ahi-text-line-through
|
<p class="ahi-text-body ahi-text-subdued">Last updated 12 minutes ago</p>
<p class="ahi-text-caption ahi-text-critical">This field is required</p>
<p class="ahi-text-body ahi-text-clamp-2">A long note that stops after two lines…</p>
ahi-text-truncate needs a definite width to clip against, and the clamps need the element to be
a block — neither forces display, so they keep working on a flex or grid child.
If your app styles h1–h6 globally, those rules win. The
utilities live in the ahi-utilities
cascade layer, and unlayered CSS beats a layer — which is deliberate, so your app can always override AKU. But it
means a rule like h1 { font-size: 2.5rem } overrides .ahi-text-heading on an
<h1 class="ahi-text-heading">.
The failure is worse than it sounds, because it is partial: your rule sets font-size but
probably not line-height, so the element takes your size and the role’s leading — a
mismatched pair, which is the one thing the roles exist to prevent. AKU’s own documentation site hit
exactly this.
Exclude elements that opt into a role:
h1:not([class*='ahi-text-']),
h2:not([class*='ahi-text-']) {
font-size: 2.5rem;
}
Migrating from <ahi-text>
<ahi-text> is being
retired. Every one of its properties is plain CSS, so each maps to a class:
<ahi-text> |
Utility class |
|---|---|
variant="heading-3xl" / "heading-2xl" |
ahi-text-display |
variant="heading-xl" / "heading-lg" |
ahi-text-heading |
variant="heading-md" / "heading-sm" |
ahi-text-subheading or ahi-text-body-emphasis |
variant="heading-xs" |
ahi-text-caption |
variant="body-lg" / "body-md" (default) |
ahi-text-body |
variant="body-sm" / "body-xs" |
ahi-text-caption |
color="subdued" | "critical" | "warning" |
"success"
|
ahi-text-subdued | -critical | -warning |
-success
|
font-weight="regular" | "medium" | "semibold" |
"bold"
|
ahi-text-regular | -medium | -semibold | -bold
|
alignment="start" | "center" | "end" | "justify"
|
ahi-text-start | -center | -end | -justify
|
truncate |
ahi-text-truncate |
break-word |
ahi-text-break-word |
numeric |
ahi-text-numeric |
text-decoration="line-through" |
ahi-text-line-through |
visually-hidden |
ahi-visually-hidden |
as="h2" |
Write the element you want: <h2 class="…"> |
The variant sizes and the roles are not the same scale, so a few mappings change the rendered size by a
pixel or two. body-md — the default when no variant is set — is 13px and becomes
14px as ahi-text-body. Check anywhere the exact size matters.
Links
Available from the theme alone — no second stylesheet. Put ahi-link on an
<a> for navigation, or on a <button type="button"> for an action that
reads as a link. It takes the surrounding font, so a link inside a sentence matches the sentence and wraps
with it.
| Class | What it does |
|---|---|
ahi-link |
Interactive color, underlined at rest. Inherits the font family, size, weight and line height, with no padding or minimum height. Hover and press follow the interactive tokens, and keyboard focus draws the focus ring. |
ahi-link-standalone |
Add to ahi-link for a link outside a sentence, such as “Back to results” or a list of
links. Underlines on hover and keyboard focus instead, and meets the target-size floor: 24px, or
44px on touch and in the comfortable and spacious density tiers.
|
ahi-link-subdued |
Add to ahi-link for a secondary link, such as a footer or a caption’s “Learn more”.
Text color instead of interactive, still underlined.
|
ahi-surface-selected |
Put on a panel, not a link, to paint the selected tint. Links and plain buttons on it move one state along the interactive colors, so they clear 4.5:1 in the light theme (see below). |
Your results are ready. Review the lab report from your last visit before your next appointment.
<p class="ahi-text-body" style="max-width: 36ch">
Your results are ready.
<a class="ahi-link" href="#">Review the lab report from your last visit</a>
before your next appointment.
</p>
The same class on a button, for an action in a sentence:
Showing 3 of 12 notes.
<p class="ahi-text-body">Showing 3 of 12 notes. <button type="button" class="ahi-link">Show all</button></p>
Standalone links, out of running text:
<div class="ahi-column ahi-align-items-start ahi-gap-1">
<a class="ahi-link ahi-link-standalone" href="#">Back to results</a>
<a class="ahi-link ahi-link-standalone" href="#">Privacy policy</a>
<a class="ahi-link ahi-link-standalone" href="#">Terms of use</a>
</div>
Subdued, for a link that should not compete with the text around it:
By continuing you agree to the terms of use.
<p class="ahi-text-caption ahi-text-subdued">
By continuing you agree to the <a class="ahi-link ahi-link-subdued" href="#">terms of use</a>.
</p>
A type role on the link itself sets its size, as it would on any element:
ahi-link ahi-text-caption is a caption-sized link.
Links or plain buttons? Use ahi-link when the control sits in or after a
sentence.
<ahi-button plain>
stays for actions that sit beside other buttons, where its sizes, icons and loading state matter. A
<button> is an atomic inline box, so a long label wraps inside the button rather than
across the paragraph’s lines. Keep button labels short, and use an <a> for anything that
navigates.
Links on the selected tint. The standard interactive blue falls just short of 4.5:1 on
--ahi-surface-selected in the light theme. When a panel takes that tint, such as a chosen card,
a notice or a picked row, paint it with the ahi-surface-selected class rather than setting the
background yourself. The class moves its content one state along the interactive colors, as an
info banner does: links
take the usual hover color at rest and the pressed color on hover. Put the class on the container, not on
the link, and put a theme class (ahi-theme-*, ahi-invert) on a wrapper around the
panel rather than on the panel itself. On the panel, its tokens undo the shift at rest, and links fall back
below 4.5:1. A background set by hand to var(--ahi-surface-selected) gets no shift. A panel
inside another shifts once, but a panel and an info banner nested in each other add their shifts together,
and hover then stops changing the color.
<div
class="ahi-surface-selected ahi-text-body"
style="padding: var(--ahi-space-4); border-radius: var(--ahi-border-radius-medium)"
>
Tuesday at 9:30 AM is selected. <a class="ahi-link" href="#">Choose a different time</a>
</div>
If your app styles a or button globally, those rules win over
ahi-link, for the same cascade-layer reason as the headings above. A global
button { padding: 8px 16px } puts the padding back on a button link. Exclude the class:
button:not(.ahi-link) {
padding: 8px 16px;
}
Accessibility
Available from the theme alone — no second stylesheet.
-
ahi-visually-hidden— hide content visually while keeping it available to assistive technology (revealed when it or a descendant receives focus, so it works for skip links) -
ahi-visually-hidden-label— applied to a form control, visually hides just its label part while keeping it announced
Preventing flash of undefined elements (FOUCE)
Add ahi-cloak to a container to keep it hidden until it and all of its custom-element
descendants are defined — no loader required:
<div class="ahi-cloak">
<ahi-card>…</ahi-card>
</div>