Text
The friendly layer on top of the typography primitives. Every piece of page text — a title, the eyebrow above it, the lede below it, a caption under a demo — grabs a variant instead of hand-mixing size, weight, line-height, tracking, and transform. The primitive tokens do their job behind the scenes — you never have to think about them.
Why a role layer
Typography primitives (--text-h1, --weight-strong, --lh-body, --tracking-wide) are the raw ingredients — mix them however you like, but on their own they don't say much. A role turns them into something you can name out loud: eyebrow, lede, caption. Use the same bundle everywhere and the site reads like one friendly voice instead of a dozen. All-caps is a treatment, not a primitive — it lives inside a role (eyebrow, overline) so your copy keeps its natural casing.
Variants
Hero / splash headline.
Halo
Page title — one per page.
Page title
Section heading.
Section heading
Subsection heading.
Subsection heading
Small heading.
Small heading
Intro paragraph below an h1. Larger and looser than body.
A lede is the first paragraph of a page — it sets the stage for everything that follows. A little bigger than body, with more room to breathe.
Default paragraph.
Body is the everyday register — the size and rhythm everything else lines up with. Most of what you read on any page is body.
Smaller body for compact spaces (popovers, dense forms).
Body-sm steps down one notch from body — handy for dense surfaces, popovers, and side panels. A touch greyer, so it reads as the supporting cast.
Small grey description under a demo tile.
Gold uppercase category label above a heading. One per page.
Components
Grey uppercase origin / annotation label. Stands alone.
Tiny labels — skel rows, sub-meta, dense metric annotations.
Inline code styling — for code references in prose.
Run pnpm dev to start the server.
Token-name chip — for CSS custom property references.
Use --space-3 for gap between related items.
Semantic HTML elements
Each variant has ONE canonical semantic element. Variant drives both the visual treatment and the element so accessibility tooling and SEO stay correct by construction. Override via the as prop only when a heading-level shift is genuinely needed (a layout-only repetition of a heading elsewhere, for example).
- display
h1Hero on landing pages; still semantically the page title.
- h1
h1One per page. Pair with eyebrow above and lede below.
- h2
h2Section heading.
- h3
h3Subsection heading.
- h4
h4Small heading; rarely needed.
- lede
pParagraph; sits below the h1.
- body
pParagraph; the default.
- body-sm
pParagraph; compact spaces.
- caption
spanInline; usually inside a demo card.
- eyebrow
pBlock-level; category above the h1.
- overline
spanInline; small origin / annotation label.
- micro
spanInline; tiny meta.
- code
codeInline code reference.
- token
spanInline; token-name chip in prose.
When to add a new variant
Ask yourself two questions before adding one — you need a yes on both:
- Is there a recurring composition that doesn't map to an existing role?
- Does it have a distinct semantic meaning a reader can name?
If only the look differs and you can't say what role it plays, you probably want an existing variant with a small tweak, not a new one. Piling on variants is how a type system loses its way.
What composes from here
Roles are the building blocks for text-group patterns — recurring combos of multiple Text instances that read as one unit. A page header (eyebrow + h1 + lede), a card head (h3 + caption), a stat block (metric + label). Those live one layer up, in @halo-compliance/ui/patterns. Building them on top of <Text> is where the role layer really pays off — the pieces just snap together.
Usage
import { Text } from '@halo-compliance/ui'
<Text variant="h1">Page title</Text> {/* renders <h1> */}
<Text variant="lede">Opening paragraph</Text> {/* renders <p>, larger leading */}
<Text variant="body">Body prose</Text> {/* renders <p> */}
<Text variant="eyebrow">Category</Text> {/* renders <p>, uppercase */}
<Text variant="token">--space-3</Text> {/* renders <span>, mono */}<Text variant="h3" as="h2">Looks h3, structurally h2</Text>
<Text variant="body" as="span">Inline run of body-styled text</Text>Related
- /text-groups — text-group patterns composed of multiple Text instances.
- /card — Card.Title and Card.Eyebrow compose Text variants.
- /type — the underlying typography primitives Text composes over.