Skip to main content
CURRENTLY BEING DESIGNED

Typography

Two type roles — Heading and Body — each with its own typeface, size steps, and weight. Consistent composite styles for every typographic decision in product UI.

Typeface decisions

Screen-optimised alternatives, not the brand identity fonts

Lora (Heading) and Hanken Grotesk (Body) are not Open Point's brand typefaces — they're the deliberate screen-crafted alternatives to them, chosen specifically for product UI. Brand identity type lives in marketing and print contexts; it isn't optimised for rendering legibly at small sizes across every browser, OS, and screen density a government stakeholder might be using, and isn't licensed for that kind of broad, ongoing web delivery. Lora and Hanken Grotesk are the pair actually shipped to the browser — every size step and weight documented on this page describes what renders in the product, not the brand system.

Weight: Regular, Semibold, and a heavier Bold than the shared scale

Body text has three real weights — Regular (400) for default copy, Semibold (500) for inline emphasis within a paragraph, and Bold (700) for stronger emphasis. Bold intentionally renders heavier than the shared Font_Weight.Bold scale value (600) — 600 read as too subtle at body text sizes to function as real emphasis, so Body's Bold style uses 700 directly rather than the shared token.

Heading styles render at 500 (Semibold weight) with slightly tightened tracking (-0.8% letter-spacing, i.e. -0.008em) — a deliberate, confirmed decision for a lighter, more refined heading feel than a full Bold would give at heading sizes. The shared Font_Weight.Heading token and the hand-set --wa-font-weight-heading CSS variable both now resolve to 500 too, aliasing Font_Weight.Semibold rather than Font_Weight.Bold.

Font stacks: why Noto Sans / Noto Serif sit in the fallback chain

Both stacks carry a deliberate multi-lingual fallback before dropping to generic system fonts:

--orbit-font-family-body: 'Hanken Grotesk', 'Noto Sans', ui-sans-serif, system-ui, sans-serif;
--orbit-font-family-heading: 'Lora', 'Noto Serif', ui-serif, serif;

Content in a language Hanken Grotesk or Lora doesn't cover well needs to render legibly before falling all the way back to whatever generic font a user's OS happens to substitute. Noto Sans and Noto Serif were chosen specifically for their broad Unicode/script coverage, and each is matched to its stack's own style (serif fallback for the serif Heading stack, sans for the sans Body stack) so a missing glyph doesn't also mean a jarring style switch.

--orbit-font-family-body / --orbit-font-family-heading are the source of truth — --wa-font-family-body / --wa-font-family-heading just alias them, so every Web Awesome component picks up the same typeface automatically. Override the --orbit-* variable, not the --wa-* one, if you ever need to change the typeface for a consuming app — same pattern as --orbit-color-* and --orbit-elevation-* elsewhere in the theme.

Adding these fonts to a project

Both typefaces are Google Fonts. Consumers of @open-point-design/orbit-theme get them for free — its published theme.css loads them via @import, so no separate step is needed alongside --wa-font-family-body / --wa-font-family-heading.

If you're wiring up these fonts without the package (e.g. an app not yet on orbit-theme), add the following to the page <head> instead — <link> tags preload sooner than a CSS @import and are the better choice when you control the HTML directly. This includes Noto Sans/Noto Serif alongside the primary typefaces — load the fallback fonts too, not just Hanken Grotesk/Lora, or the multi-lingual fallback described above is decorative: Noto isn't preinstalled on most systems, so an unloaded font is just skipped straight past to ui-sans-serif/ui-serif.

<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
  href="https://fonts.googleapis.com/css2?family=Hanken+Grotesk:wght@300;400;500;600;700&family=Noto+Sans:wght@300;400;500;600;700&family=Lora:wght@400;500&family=Noto+Serif:wght@400;500&display=swap"
  rel="stylesheet"
/>

The weight lists match what Orbit actually uses — Hanken Grotesk 300–700 covers Body's Regular/Semibold/Bold styles, and Lora 400/500 covers Heading's Semibold weight plus a Regular fallback; Noto Sans/Noto Serif mirror the same weights as their primary counterpart. If your app genuinely doesn't need multi-lingual coverage, the Noto families can be dropped to shrink the font payload — but that's a deliberate trade-off against the reason they're in the stack, not a default.

What's actually wired into CSS today

Only a handful of hand-set globals in component-overrides.css are live in the published theme: --orbit-font-family-body / --orbit-font-family-heading (aliased onto --wa-font-family-body / --wa-font-family-heading), and --wa-font-weight-{normal, semibold, bold, body, heading, action}. The full composite type scale below — every Heading and Body style's exact size, weight, tracking, and line-height combination — is real, current, and drives this page directly from the token source, but isn't yet exposed as its own set of consumable --orbit-text-* CSS custom properties. Until it is, use the family/weight globals above directly rather than hand-assembling a composite style from the primitive tables in the Tokens tab.

Type styles

Each type role pairs a typeface with a fixed set of sizes, weights, and line heights. Heading uses Lora at Semibold (500) for page and section titles, and Body uses Hanken Grotesk for paragraph and UI copy — every Body size has Regular, Semibold, and Bold variants for emphasis within copy. See Typeface decisions above for why these are the screen typefaces rather than the brand fonts, and for the weight and font-stack reasoning. Specimens below render with the token's real font family — if the typeface isn't installed locally, the browser falls back through the same stack shipped in production.

Heading

Heading-2xl
--orbit-typography-heading-2xl
Lora 56px weight 500 line-height 67.2px tracking -0.8%

Hero page title

Heading-xl
--orbit-typography-heading-xl
Lora 40px weight 500 line-height 48px tracking -0.8%

Section headers

Heading-lg
--orbit-typography-heading-lg
Lora 32px weight 500 line-height 38.4px tracking -0.8%
Heading-md
--orbit-typography-heading-md
Lora 24px weight 500 line-height 28.8px tracking -0.8%
Heading-sm
--orbit-typography-heading-sm
Lora 20px weight 500 line-height 24px tracking -0.8%
Heading-xs
--orbit-typography-heading-xs
Lora 16px weight 500 line-height 19.2px tracking -0.8%

Body

Body Regular xl
--orbit-typography-body-regular-xl
Hanken Grotesk 20px weight 400 line-height normal tracking 0%
Body Regular lg
--orbit-typography-body-regular-lg
Hanken Grotesk 16px weight 400 line-height normal tracking 0%
Body Regular md
--orbit-typography-body-regular-md
Hanken Grotesk 14px weight 400 line-height normal tracking 0%
Body Regular sm
--orbit-typography-body-regular-sm
Hanken Grotesk 12px weight 400 line-height normal tracking 0%
Body Semibold xl
--orbit-typography-body-semibold-xl
Hanken Grotesk 20px weight 500 line-height normal tracking 0%
Body Semibold lg
--orbit-typography-body-semibold-lg
Hanken Grotesk 16px weight 500 line-height normal tracking 0%
Body Semibold md
--orbit-typography-body-semibold-md
Hanken Grotesk 14px weight 500 line-height normal tracking 0%
Body Semibold sm
--orbit-typography-body-semibold-sm
Hanken Grotesk 12px weight 500 line-height normal tracking 0%
Body Bold xl
--orbit-typography-body-bold-xl
Hanken Grotesk 20px weight 700 line-height normal tracking 0%
Body Bold lg
--orbit-typography-body-bold-lg
Hanken Grotesk 16px weight 700 line-height normal tracking 0%
Body Bold md
--orbit-typography-body-bold-md
Hanken Grotesk 14px weight 700 line-height normal tracking 0%
Body Bold sm
--orbit-typography-body-bold-sm
Hanken Grotesk 12px weight 700 line-height normal tracking 0%

Usage guidelines

Don't go below text-sm for interactive elements

--op-text-xs (12px) is appropriate for timestamps, badges, and captions — passive text the user doesn't need to read carefully. Never use it for interactive labels, form fields, error messages, or any text the user needs to act on. WCAG recommends 16px minimum for body text; 14px (--op-text-sm) is acceptable for secondary metadata.

Set line height to match the text's purpose

Use --op-leading-tight on headings, --op-leading-relaxed on body paragraphs, and --op-leading-normal on UI labels and list items. The default body style already uses --op-leading-relaxed — only override it when you have a specific layout reason.

Not yet available outside this site. These --op-leading-* tokens are real and work exactly as described — this page and the rest of Orbit's own site use them directly — but they're only defined in Orbit's internal site CSS, not published in @open-point-design/orbit-theme. A product team building in Open Point or Social Point can't var(--op-leading-relaxed) today; treat this guidance as the intended behaviour to replicate with your own line-height values until this scale is published for consumption.

Never set text in all-caps at body size

All-caps text reduces readability by removing the ascender/descender cues that distinguish letters. Reserve it for short labels, section markers, and status badges at small sizes — never for sentences or paragraphs.

Do

Use --op-leading-relaxed (1.625) for all paragraph and body text. This is already the global default — preserve it rather than overriding it to a tighter value.

Don't

Set line-height: 1 or line-height: 1.1 on text elements for visual effect. This makes multi-line text unreadable and fails WCAG 1.4.12 (Text Spacing).

Do

Keep body text line length between 60–80 characters (roughly 60ch). Long lines force the eye to travel too far; short lines create an exhausting rhythm of line breaks.

Don't

Let body text span the full page width. Content wider than 80ch is measurably harder to read — use max-width on text containers, not on the page grid.

Accessibility

Minimum sizes

WCAG does not mandate a specific minimum font size, but best practice (and browser defaults) establish 16px as the baseline for body text. In Open Point product UI, --op-text-base (16px) is the minimum for body copy. Helper text, labels, and metadata may use --op-text-sm (14px). --op-text-xs (12px) should be used only for non-essential passive text (timestamps, version numbers, decorative labels).

Relative units

All size tokens use rem. This means they scale with the user's browser base font size setting. A user who has set their browser to 20px base will see all text 25% larger than the design baseline — this is intentional and required by WCAG 1.4.4 (Resize Text). Never use px for font sizes in component styles.

Line height and text spacing

WCAG 1.4.12 (Text Spacing) requires that content remains readable when users override line height to 1.5× the font size, letter spacing to 0.12em, and word spacing to 0.16em. Using the Orbit leading tokens (which start at 1.25 for tight and reach 1.625 for relaxed) gives enough headroom that overrides at the WCAG levels won't break layouts.

Colour contrast on text

Text contrast requirements differ by size: normal text (below 18pt / 24px or 14pt / 18.67px bold) requires 4.5:1 (AA) or 7:1 (AAA). Large text meets AA at 3:1. Always pair text tokens with their intended background — --op-color-text-primary on --op-color-bg-primary achieves AAA. See the Colour page for contrast ratios across the palette.

Was this page helpful?