typography
Two typefaces carry everything. JetBrains Mono names things: headings, titles, labels, values, numbers and code. Inter carries what people read: sentences, hints, messages and the text of the interface. Six text styles cover the rest.
the two families
| typeface | for |
|---|---|
| JetBrains Mono, a monospace face | Headings and titles; the labels of buttons, tabs, menus, filters and table headers; badges and statuses; tooltips; what people type into fields; numbers; code |
| Inter, the body face | Body text; field labels, hints and errors; checkbox and switch labels; banners and toasts; second lines |
Anything that doesn't choose a typeface of its own is set in Inter.
Backups run every night at 02:00 and are kept for 14 days.
In this panel, the card's title, the status, the value in the field and the button labels are in monospace. The sentence, the field's label and its hint are in Inter.
the roles
A type role is a named text style, with its typeface, size, weight and spacing decided once. There are six:
| role | looks | used by |
|---|---|---|
| Display | Monospace, very large and heavy | No component; a splash or a sign-in screen |
| Page title | Monospace, large and bold | The heading at the first level |
| Section title | Monospace, medium and semi-bold | The heading at the second level |
| Body | Inter, regular, at reading size | The text component's body text |
| Interface | Inter, medium, small | The text component's interface text, and the field label |
| Code | Monospace, medium, small | The text component's mono text |
- There is no role for a third heading level. A sub-section gets a real third-level heading through the text component, as the heading page explains.
- The components also use a few sizes between the roles, set in their own styles: for badges and table headers, small buttons and hints, tile titles, dialog titles, key figures and more. They aren't tokens, so use them through the components rather than in your own styles. The specs list them.
- The largest monospace text is drawn a little tighter, and the smallest monospace labels a little looser, so both stay easy to read. That spacing is the letter-spacing.
weights
- Most monospace values don't set a weight of their own. Inputs, code, badges, tooltips and the cells of the plain table take the weight of the text around them, which is regular on a plain page.
- Semi-bold marks a label that names something: a button, a title, a column. Bold is for the page title and for a key figure. Inter is regular for reading and for checkbox and switch labels, and medium for field labels and interface text.
lowercase
- The two typefaces are the system's two voices. Monospace is the system's voice, and it is lowercase; Inter is the reader's language, in sentence case. Content & voice explains them.
- The components lowercase their monospace chrome, and only that: buttons, tabs and filter tabs, menu items, badges and statuses, table headers, the labels of the filter bar and the multi select, divider labels, and the titles of cards, dialogs, tiles, toolbars, tile groups, worklists, the portal and empty states. Nothing in Inter is changed.
- Lowercasing changes the look, not the text: the page keeps what you wrote. Browsers differ in which of the two they pass on to assistive technology, so write monospace chrome the way it should be read, in lowercase, and both say the same.
- Headings and tooltips are monospace, but they aren't lowercased for you: write them in lowercase. The values in fields are content and keep their case. Body text, field labels, hints and errors, checkbox and switch labels, banners and toasts are in Inter and aren't changed: write them in sentence case, and any capitals you write will show.
- Names that are content keep their case: an identifier cell, the label of a data table group, a title quoted in a sentence. A card, tile or dialog title is lowercased even when it holds a name, so a name that must keep its capitals goes in the body.
numbers
- Numbers that people compare, count or watch change are set in monospace: counts, sizes, dates, durations and ids. Every monospace character has the same width, so a column of numbers lines up, and a count that changes doesn't push its neighbours.
- The counts in the components also use tabular figures: the filter tabs' counts, the filter bar's result count, the multi select's option counts, a tile's key figure, change and count badge, the show-more row of a data table, and the date cell.
- A number inside an Inter sentence stays in Inter. In a column of Inter text, or a value that updates in place, use tabular figures too.
- Counts carry a thousands separator, as in “2,500”. The components format their counts that way.
loading the fonts
The library doesn't include or load the fonts: it names them, and leaves the loading to the app. Until they load, or if they never do, text shows in a fallback font, the computer's own. React shows how to load them; this site hosts them itself.
- Linking the fonts from the head of the page starts the download sooner than importing them from a stylesheet.
- To host the files yourself, declare them under the family names Inter and JetBrains Mono, spelled exactly like that: those are the names the system looks for.
- Load JetBrains Mono in its regular weight, 400: most monospace values are drawn in it. The import suggested at the top of the tokens file starts at 500, and with it every regular value shows at 500.
- The components draw Inter at 400 and 500 only; 600 and 700 are there for bold text in your own content. JetBrains Mono 800 is only for the display role.
- Asking the browser to swap the font in shows the fallback first, and the real font as soon as it arrives.
accessibility
- The lowercase look of monospace chrome is only a style, and assistive technology may get either the written or the lowercased text. Written in lowercase, the two are the same.
- Don't let a typeface, a weight or a size carry a meaning on its own. A state is a status, with words and a symbol; a structure is a real heading.
- Today: every size is set in pixels. Text grows when people zoom the browser, but not when they choose a larger default text size in the browser's settings.
- Today: the role styles write out their font lists instead of using the shared font settings, so changing a font in one place doesn't reach them.
do and don't
Backups run every night at 02:00 and are kept for 14 days. A run that fails is tried again an hour later.
Backups run every night at 02:00 and are kept for 14 days. A run that fails is tried again an hour later.
recent runs
Recent Runs
recent runs
related
- Heading: the page and section title roles, as real headings.
- Text: the body, interface and code roles, on any element.
- Content & voice: how the words are written, in the two voices.
- Colour: the text colours and their contrast.
specs
families
| token | family | stack |
|---|---|---|
--ff-mono |
JetBrains Mono | 'JetBrains Mono', ui-monospace, monospace |
--ff-ui |
Inter | Inter, system-ui, sans-serif |
tokens.css sets font-family: var(--ff-ui) on body, so anything that sets no family of its own is Inter.
roles
From tokens/tokens.json (type.role); the specimen above is drawn from the same values.
| role | in code | family | weight | size / line height | letter-spacing | in components |
|---|---|---|---|---|---|---|
| Display | display |
Mono | 800 | 56px / 1.00 | −0.030em | None; through .type-display |
| Page title | h1 |
Mono | 700 | 32px / 1.15 | −0.015em | Heading level={1} |
| Section title | h2 |
Mono | 600 | 22px / 1.20 | 0 | Heading level={2} |
| Body | body |
Inter | 400 | 16px / 1.55 | 0 | Text variant="body" |
| Interface | ui |
Inter | 500 | 13px / 1.40 | 0 | Text variant="ui", the field label |
| Code | code |
Mono | 500 | 13px / 1.50 | 0 | Text variant="code" |
- No
h3role: a sub-section is Text withas="h3". - Sizes between the roles, in the components' own CSS (not tokens): 11px (badges, statuses, table headers), 12px (
smbuttons, hints and errors, tooltips, dates, counts), 14px (lgbuttons and fields, the modal body, tile titles), 15px (modal titles), 20px (filter tab counts), 22px at 700 (the worklist and portal titles) and 28px (a tile's key figure). - Letter-spacing: display −0.03em,
h1−0.015em, the portal title and the filter tab counts −0.01em; 0.04em on table headers, tile group legends and divider labels; 0.02em on badges and statuses.
weights and case
- Mono values with no weight of their own (inputs, code, badges, tooltips, plain table cells) inherit it: 400 on a plain page. 600 names a label; 700 is the page title and a key figure. Inter: 400 for reading and for checkbox and switch labels, 500 for field labels and
uitext. text-transform: lowercaseon mono chrome only (the list under lowercase, above). Headings and tooltips are mono with no transform.
numbers
font-variant-numeric: tabular-numson the counts listed under numbers, above. Add it to an Inter column or a value that updates in place.formatCount(2500)returns2,500; it is the formatter the components use.
fonts
tokens.cssnames the families in--ff-uiand--ff-monobut has no@font-face. Fallbacks:system-ui, thensans-serif;ui-monospace, thenmonospace. This site self-hosts the fonts with@fontsource/interand@fontsource/jetbrains-mono.- A
<link rel="stylesheet">in the<head>starts the download sooner than an@import, which must come before every other rule in its stylesheet. - Self-hosting by hand:
@font-faceunder the family namesInterandJetBrains Mono, exactly. - The
@importin the comment at the top oftokens.cssstarts JetBrains Mono at 500; load 400 as well. display=swapshows the fallback first and swaps the font in when it arrives.
how to apply it
- Components first. Heading, Text, Code and Kbd carry the roles; every other component sets its own type.
- Role classes.
tokens.cssdefines.type-display,.type-h1,.type-h2,.type-body,.type-uiand.type-code: family, weight, size, line height and letter-spacing. No colour, no margin. - CSS variables. The families are
var(--ff-mono)andvar(--ff-ui). Sizes and weights are not variables. - TypeScript.
typography.h1andfontFamily.monocome from@nalet/design-system(or@nalet/design-system/tokens): the same metrics as objects, ready to spread into astyle. - Tailwind. The preset maps
font-monoandfont-sansto the two stacks. It adds no font sizes. - Today: every size is in px, and the role classes write the font stacks out instead of using
var(--ff-ui)andvar(--ff-mono), so overriding a family variable doesn't reach them.