Skip to content

Typography (Heading, Text, Link, Code, Prose)

TypographySince 0.2.0
import { Heading, Link, Text, setLineClamp } from '@absoluteui/components/typography';
import '@absoluteui/components/typography/style.css';

Using Angular? See Typography (Heading, Text, Link, Code, Prose) for Angular.

Token-based typography. The CSS classes are the primary API and work without JavaScript: headings, body text, links, inline code, rich-text prose, truncation, and line clamping. The JS helpers create validated elements:

  • Heading.create: the semantic level (document outline) is independent of the visual size;
  • Text.create: tone, size, and weight modifiers;
  • Link.create: only http(s) and relative destinations; links that open a new tab say so in a localized, visually hidden hint;
  • setLineClamp: a validated line count.
  • Headings, body text, and supporting text that must follow the theme’s type scale.
  • Links inside content, including links that open in a new tab.
  • Rich text from a CMS or Markdown renderer (aui-prose), after sanitization.
  • Truncating long single-line labels or clamping previews to a number of lines.
  • Actions that do not navigate: use a Button.
  • Code blocks that need syntax highlighting: use a dedicated highlighter inside aui-prose pre.
  • Truncating essential information that users cannot otherwise reach.
import { Heading, Text } from '@absoluteui/components/typography';
const section = document.createElement('section');
section.append(
Heading.create(2, 'Billing', { size: 4 }),
Text.create('Invoices are issued on the first of each month.', { tone: 'muted', size: 'sm' }),
);
document.body.append(section);
import { Link } from '@absoluteui/components/typography';
const p = document.createElement('p');
p.append('Read the ', Link.create({ href: 'https://example.com/guide', text: 'setup guide', external: true }), '.');
document.body.append(p);
import { setLineClamp } from '@absoluteui/components/typography';
const preview = document.createElement('p');
preview.className = 'aui-text';
preview.textContent = 'A long description that should show at most two lines in the list view.';
document.body.append(preview);
const unclamp = setLineClamp(preview, 2);
preview.addEventListener('click', unclamp, { once: true });
import { configureAbsoluteUI, registerMessages } from '@absoluteui/core';
registerMessages('typography', 'de', { opensInNewTab: '(öffnet in einem neuen Tab)' });
configureAbsoluteUI({ components: { typography: { locale: 'de-DE' } } });
import { Heading, Link, Text, setLineClamp } from '@absoluteui/components/typography';
import '@absoluteui/components/typography/style.css';
const section = document.querySelector<HTMLElement>('#release');
if (section) {
const summary = Text.create('This release adds layout primitives, cards, and typography helpers across the library.');
section.append(Heading.create(2, 'Release 0.2', { size: 3 }), summary, Link.create({ href: '/changelog', text: 'Full changelog' }));
setLineClamp(summary, 2);
}
<h2 class="aui-heading aui-heading--4">Billing</h2>
<p class="aui-text aui-text--muted">Invoices are issued monthly. <a class="aui-link" href="/billing">Manage billing</a></p>
import { Heading, Link } from '@absoluteui/components/typography';
document.body.append(Heading.create(2, 'Billing', { size: 4 }), Link.create({ href: 'https://example.com', text: 'Status page', external: true }));
<h2 class="aui-heading aui-heading--4">Billing</h2> level 2, size 4
<p class="aui-text aui-text--muted aui-text--sm">Supporting text</p>
<a class="aui-link" href target="_blank" rel="noopener noreferrer">
Setup guide<span class="aui-link__hint"> (opens in a new tab)</span> (visually hidden)
</a>
<code class="aui-code">npm install</code>
<div class="aui-prose"> p, ul, ol, h2-h4, a, code, pre, blockquote </div>
<span class="aui-truncate">...</span>
<p class="aui-line-clamp" style="--aui-lines: 2">...</p> (custom property set via the CSSOM)
Heading.create(level: HeadingLevel, text: string, options?: HeadingOptions, doc?: Document): HTMLHeadingElement; Text.create(text: string, options?: TextOptions, doc?: Document): HTMLParagraphElement | HTMLSpanElement; Link.create(options: LinkOptions, doc?: Document): HTMLAnchorElement; setLineClamp(el: HTMLElement, lines: number): () => void

Global configuration key: typography (see Configuration).

Option Type Default Description
level 1 | 2 | 3 | 4 | 5 | 6 Heading.create: semantic heading level (document outline). Required.
size 1 | 2 | 3 | 4 | 5 | 6 (Heading) | 'xs' | 'sm' | 'md' | 'lg' (Text) Heading: visual size, default the level. Text: font size, default md.
tone 'default' | 'muted' "default" Text: muted uses color-text-muted.
strong boolean false Text: semibold weight.
as 'p' | 'span' "p" Text: element to create.
href string Link: http(s) URL or relative reference; other schemes throw TYPOGRAPHY_OPTIONS. Required.
text string Heading, Text, Link: text content (rendered with textContent). Required.
external boolean false Link: target=_blank, rel=“noopener noreferrer”, and a visually hidden localized “(opens in a new tab)”.
muted boolean false Link: muted color; the underline remains.
lines number setLineClamp: integer 1..20, written to –aui-lines. Required.
locale string "en-US" Locale of the external-link hint (Link.create option or global). Globally configurable. Inherited from common configuration.
Method Description
Heading.enhance(el: HTMLHeadingElement, options?: HeadingOptions): TypographyEnhancement<HeadingOptions> Since 0.9.0. Style an existing h1..h6 (level from the tag).
Text.enhance(el: HTMLElement, options?: TextEnhanceOptions): TypographyEnhancement<TextEnhanceOptions> Since 0.9.0. Add aui-text modifiers to an existing element.
Link.enhance(a: HTMLAnchorElement, options?: LinkEnhanceOptions): TypographyEnhancement<LinkEnhanceOptions> & { check(): void } Since 0.9.0. Style and validate an existing link; destroy() restores target and rel.
isSafeHref(value: unknown): value is string The link destination check used by Link and Card.

All rules live in the aui.components cascade layer, so application styles override them without specificity battles.

Class Purpose
aui-heading Heading base: semibold, line-height-tight, color-text.
aui-heading--1 ... aui-heading--6 Visual sizes font-size-4xl, 3xl, 2xl, xl, lg, md.
aui-text Body text: font-size-md, line-height-normal.
aui-text--muted | --lg | --sm | --xs | --strong Text modifiers.
aui-link Underlined color-link link with a focus ring; LinkText in forced colors.
aui-link--muted Muted link color, still underlined.
aui-link__hint Visually hidden new-tab hint.
aui-code Inline code: font-family-mono on color-surface-sunken.
aui-prose Rich text rhythm for p, lists, h2 to h4, links, code, pre, and blockquote (line-height-relaxed).
aui-truncate Single-line ellipsis.
aui-line-clamp Multi-line clamp; lines from –aui-lines (default 3).
--aui-lines Custom property: number of lines for aui-line-clamp.

Tokens this stylesheet reads (verified in CI). Override them in a theme or with the Theme Designer.

--aui-border-width | --aui-border-width-strong | --aui-color-border-strong | --aui-color-focus-ring | --aui-color-link | --aui-color-surface-sunken | --aui-color-text | --aui-color-text-muted | --aui-duration-fast | --aui-easing-standard | --aui-focus-ring-offset | --aui-focus-ring-width | --aui-font-family-mono | --aui-font-family-sans | --aui-font-size-2xl | --aui-font-size-3xl | --aui-font-size-4xl | --aui-font-size-lg | --aui-font-size-md | --aui-font-size-sm | --aui-font-size-xl | --aui-font-size-xs | --aui-font-weight-semibold | --aui-line-height-normal | --aui-line-height-relaxed | --aui-line-height-tight | --aui-radius-md | --aui-radius-sm | --aui-space-1 | --aui-space-2 | --aui-space-4 | --aui-space-6 | --aui-space-8

type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;
type TextTone = 'default' | 'muted';
type TextSize = 'xs' | 'sm' | 'md' | 'lg';
interface HeadingOptions { size?: HeadingLevel }
interface TextOptions { tone?: TextTone; size?: TextSize; strong?: boolean; as?: 'p' | 'span' }
interface LinkOptions { href: string; text: string; external?: boolean; muted?: boolean; locale?: string }
declare function setLineClamp(el: HTMLElement, lines: number): () => void;
declare function isSafeHref(value: unknown): value is string;

Pattern: Native HTML text semantics: <h1>-<h6>, <p>, <a href>, <code>. Visual size is decoupled from heading level.

Key Context Action
Tab / Shift+Tab Link Moves focus to and from the link (native).
Enter Link focused Follows the link (native).
Tab / Shift+Tab Headings, text, code Not applicable: not focusable.
Element Role
Heading.create heading (native), level = the level argument
Link.create link (native <a href>)
Text.create paragraph or generic (native <p> / <span>)
Attribute Element Value
(none) external link No ARIA: the new-tab hint is visually hidden text inside the link, so it is part of the accessible name

Links show a color-focus-ring outline on :focus-visible (Highlight in forced colors).

Criterion Level Note
1.3.1 Info and Relationships A
1.4.1 Use of Color A
1.4.3 Contrast (Minimum) AA
1.4.4 Resize Text AA
1.4.12 Text Spacing AA
2.4.4 Link Purpose (In Context) A
2.4.6 Headings and Labels AA
2.4.7 Focus Visible AA
3.2.5 Change on Request AAA

Automated test results: Accessibility and compliance.

  • Links are always underlined, so they are not identified by color alone (WCAG 1.4.1).
  • Links that open a new tab say so in their accessible name (WCAG 3.2.5 advisory technique G201).
  • Heading level and visual size are independent, so the outline can be correct without compromising the design.
  • Truncated or clamped text remains in the DOM, so screen readers read it in full; sighted users need another way to see it.
  • Manual screen-reader verification (NVDA, JAWS, VoiceOver) of the new-tab hint is pending.

Links: default, hover (thicker underline), focus-visible (focus ring). Text: truncated, clamped.

From Trigger To
unclamped setLineClamp(el, n) clamped to n lines
clamped disposer called previous classes and --aui-lines restored

Heading, Text, and Link elements are static once created.

  • Links are always underlined, so they are never identified by color alone (WCAG 1.4.1). This includes --muted.
  • Choose the heading level for the outline and size for appearance (WCAG 1.3.1, 2.4.6).
  • Links that open a new tab tell the user in advance (WCAG 3.2.5, technique G201).
  • Truncated and clamped text stays in the DOM, so screen readers read it in full. Give sighted users a way to see the full text too.
  • Manual screen-reader verification is pending.

All sizes are rem tokens and scale with user font settings (WCAG 1.4.4). Long words wrap (overflow-wrap). aui-truncate needs a constrained inline size, for example a flex child.

Logical properties throughout: list indentation and the blockquote rule sit at the inline start, and the ellipsis appears at the inline end.

Colors come from role tokens with verified contrast pairs:

  • color-link and color-text-muted on color-bg and color-surface;
  • color-text on color-surface-sunken (code).

In forced-colors mode, links use LinkText and a Highlight focus outline, and inline code keeps a visible border.

registerMessages('typography', 'de', { opensInNewTab: '(öffnet in einem neuen Tab)' });
configureAbsoluteUI({ components: { typography: { locale: 'de-DE' } } });

Link.create({ locale }) overrides the global locale for that link. The hint is resolved when the link is created.

None. External links get a text hint for assistive technology, not an icon.

  • All helpers set text with textContent.
  • Link.create checks href with the WHATWG URL parser (the one the browser uses to follow the link). Only http:, https:, and relative references are accepted. javascript:, data:, vbscript:, file:, blob:, and obfuscated forms (mixed case, leading control characters, embedded tabs or newlines) are rejected.
  • External links get rel="noopener noreferrer".
  • setLineClamp writes one custom property through the CSSOM.
  • aui-prose styles markup but does not sanitize it: pass untrusted HTML through the core sanitizer (setSanitizedHtml) first.
  • CSP-safe.

No persistence, network access, or telemetry. External links send no referrer (noreferrer).

Each helper creates one element (two for external links) and attaches no listeners or subscriptions. There is no dedicated performance budget yet.

Not data-driven. aui-prose handles documents of any length; layout cost is the browser’s.

  • Heading.create(2, 'x', { size: 1 }) is an <h2> at the largest size.
  • Link.create({ href: 'java\tscript:alert(1)' }) throws: the URL parser strips the tab, so the scheme is javascript:.
  • Protocol-relative URLs (//cdn.example.com) are accepted; they resolve to the page’s http(s) scheme.
  • mailto: and tel: are rejected by Link.create; use a plain <a class="aui-link"> for them.
Code Category Cause
TYPOGRAPHY_OPTIONS configuration Invalid level, size, tone, as, empty text, unsafe href, or line count outside 1..20.
I18N_MISSING_MESSAGE configuration No opensInNewTab message for the locale chain (English is always registered).

Invalid calls create nothing and leave elements unchanged.

  • The external-link hint does not update when the locale changes after creation.
  • aui-line-clamp relies on -webkit-line-clamp; the standard line-clamp is declared for forward compatibility.
  • aui-prose covers p, ul, ol, li, h2-h4, a, code, pre, and blockquote only.
  • Manual screen-reader verification is pending.
  • HTML strings as heading, text, or link text.
  • Non-http(s) schemes in Link.create.
  • Fluid (viewport-based) type scales.