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

variant="display"

Hero / splash headline.

Halo

variant="h1"

Page title — one per page.

Page title

variant="h2"

Section heading.

Section heading

variant="h3"

Subsection heading.

Subsection heading

variant="h4"

Small heading.

Small heading

variant="lede"

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.

variant="body"

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.

variant="body-sm"

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.

variant="caption"

Small grey description under a demo tile.

Caption — friendly little description text.
variant="eyebrow"

Gold uppercase category label above a heading. One per page.

Components

variant="overline"

Grey uppercase origin / annotation label. Stands alone.

Design system
variant="micro"

Tiny labels — skel rows, sub-meta, dense metric annotations.

v1.4.2 · 2026-05-28
variant="code"

Inline code styling — for code references in prose.

Run pnpm dev to start the server.

variant="token"

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
h1

Hero on landing pages; still semantically the page title.

h1
h1

One per page. Pair with eyebrow above and lede below.

h2
h2

Section heading.

h3
h3

Subsection heading.

h4
h4

Small heading; rarely needed.

lede
p

Paragraph; sits below the h1.

body
p

Paragraph; the default.

body-sm
p

Paragraph; compact spaces.

caption
span

Inline; usually inside a demo card.

eyebrow
p

Block-level; category above the h1.

overline
span

Inline; small origin / annotation label.

micro
span

Inline; tiny meta.

code
code

Inline code reference.

token
span

Inline; 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

Variants map to semantic HTMLtsx
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 */}
Override the semantic element when role and structure divergetsx
<Text variant="h3" as="h2">Looks h3, structurally h2</Text>
<Text variant="body" as="span">Inline run of body-styled text</Text>
  • /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.