Skip to content

Typography ​

Ooko uses a small set of typography roles to keep text and control heights on a predictable vertical rhythm while respecting the user's root font size.

Roles ​

RoleFont sizeLine heightWeightSize at a 16px root
display3rem3.5rem30048px / 56px
h12.25rem2.75rem40036px / 44px
h21.75rem2.25rem40028px / 36px
h31.5rem2rem50024px / 32px
h41.25rem1.75rem50020px / 28px
h51.125rem1.5rem60018px / 24px
h61rem1.25rem60016px / 20px
lead1.125rem1.75rem40018px / 28px
body0.875rem1.540014px / 21px
small0.8125rem1.25rem40013px / 20px
label0.8125rem1.25rem60013px / 20px
hint0.75rem1rem40012px / 16px

Ooko targets admin interfaces and dashboards, so body text is 14px. Large headings and the display role stay light: numbers and titles carry the hierarchy through size, not weight.

The internal typo(...) Sass mixin applies these roles consistently across Ooko components. The display and lead roles are opt-in treatments. The body role is the default document typography, while form controls select the role that matches their size.

Font size scale ​

Small interface text uses a short scale of semantic tokens instead of one-off sizes:

TokenValueSize at a 16px rootSize at a 14px root
--o-font-size-xs0.75rem12px10.5px
--o-font-size-sm0.8125rem13px11.375px
--o-font-size-md0.875rem14px12.25px
--o-font-size-lg1rem16px14px

--o-font-size-min (11px) is the floor for small text that carries useful information. Chips and field hints and errors use max(var(--o-font-size-xs), var(--o-font-size-min)), so they stay at 12px by default and never drop under 11px when the consumer shrinks the root font size.

Each component exposes its own size token on top of the scale: --o-chip-font-size, --o-field-message-font-size, and --o-button-font-size (the xs, sm, and md button sizes). Override a scale token to move every component that uses it, or a component token to move one component:

css
:root {
  --o-font-size-md: 0.8125rem;
  --o-chip-font-size: var(--o-font-size-sm);
}

Font families ​

The base layer sets body to --o-font-family-sans (Onest) and h1 to h4 to --o-font-family-display (Bricolage Grotesque), with a slight negative letter spacing; h5 and h6, used as subtitles, keep the text family. ODialog titles use the display family through --o-dialog-title-font-family.

Ooko does not ship the font files. Load them in the application, either from @fontsource-variable/onest and @fontsource-variable/bricolage-grotesque or from Google Fonts; the token stacks match both names and fall back to the system font otherwise:

html
<link
  rel="stylesheet"
  href="https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,wght@12..96,400..700&family=Onest:wght@400..700&display=swap" />

Override either token in plain consumer CSS to use another family. Monospace treatments, such as the OChip overline, use the --o-font-family-mono semantic token, which defaults to the system monospace stack. Override it in plain consumer CSS to use a brand monospace font.

Semantic defaults ​

The base layer maps h1 through h6 to their matching roles. label and legend use label; small, figcaption, code, kbd, and samp use small. Paragraphs, lists, descriptions, and quotations inherit body from the document.

Heading elements must reflect the document hierarchy. Choose heading levels for structure, not for their visual size; use a typography role explicitly when a different visual treatment is needed.

Vertical rhythm ​

The reset removes browser-owned margins from headings and common text blocks, and removes list padding. The base layer then applies a 1rem end margin to headings, a 1.5rem end margin to common text blocks, and a 1.5rem inline-start indent to lists and descriptions. This preserves visible grouping and list markers while keeping spacing consistent across browsers.

Form control rhythm ​

The default form control minimum height is exposed as --o-control-form-height-md with a value of 2.25rem. At a 16px root, a one-line control using the small role combines a 20px line box with 8px of padding on each side for a 36px total height. Wrapped content increases the height in complete 20px line-box increments.

Use minimum heights instead of fixed heights so controls remain usable when text wraps or is enlarged.