skip to content
menu
styles · typography

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.

display
jetbrains mono 800
56px / 1.00
letter-spacing -0.030em
a splash or a sign-in screen. no component uses it
nalet
h1
jetbrains mono 700
32px / 1.15
letter-spacing -0.015em
the page title: heading level 1
storage settings
h2
jetbrains mono 600
22px / 1.20
letter-spacing 0
a section of the page: heading level 2
recent runs
body
inter 400
16px / 1.55
letter-spacing 0
sentences and paragraphs: text body
Backups run every night at 02:00 and are kept for 14 days. A run that fails is tried again an hour later.
ui
inter 500
13px / 1.40
letter-spacing 0
interface text and field labels: text ui
storage-01 · 412 GB used
code
jetbrains mono 500
13px / 1.50
letter-spacing 0
values read in mono: text code
npm i @nalet/design-system

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.

storage-01
healthy

Backups run every night at 02:00 and are kept for 14 days.

A full host name, such as storage-01.example.com

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

jetbrains mono 400
values: inputs, code, badges, status, tooltips, plain table cells
api-7f9c · 2,500 missing · 0O 1lI
jetbrains mono 500
tabs, filter tab labels, text code
api-7f9c · 2,500 missing · 0O 1lI
jetbrains mono 600
buttons, card, modal and tile titles, table headers, h2
api-7f9c · 2,500 missing · 0O 1lI
jetbrains mono 700
h1, page titles, key figures, the lockup
api-7f9c · 2,500 missing · 0O 1lI
jetbrains mono 800
the display role only
api-7f9c · 2,500 missing · 0O 1lI
inter 400
body, hints and errors, messages and toasts, muted and dim text
Every night at 02:00, kept 14 days
inter 500
field labels, text ui
Every night at 02:00, kept 14 days
  • 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

do

Backups run every night at 02:00 and are kept for 14 days. A run that fails is tried again an hour later.

Sentences in Inter: they are read, not scanned.
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.

A paragraph in monospace reads slower. Monospace is for names, labels and values.
do

recent runs

Written as it is read: in lowercase.
don't

Recent Runs

Headings aren't lowercased for you, so the capitals you write show on the screen.
do
2,500 missing
A count in monospace, with its thousands separator.
don't
2500 missing
Without the separator, people have to count the digits.
do

recent runs

A section title is a real heading, in the section title style.
don't
recent runs
A title styled by hand matches no role, and to assistive technology it isn't a heading.
  • 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 h3 role: a sub-section is Text with as="h3".
  • Sizes between the roles, in the components' own CSS (not tokens): 11px (badges, statuses, table headers), 12px (sm buttons, hints and errors, tooltips, dates, counts), 14px (lg buttons 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 ui text.
  • text-transform: lowercase on mono chrome only (the list under lowercase, above). Headings and tooltips are mono with no transform.

numbers

  • font-variant-numeric: tabular-nums on the counts listed under numbers, above. Add it to an Inter column or a value that updates in place.
  • formatCount(2500) returns 2,500; it is the formatter the components use.

fonts

  • tokens.css names the families in --ff-ui and --ff-mono but has no @font-face. Fallbacks: system-ui, then sans-serif; ui-monospace, then monospace. This site self-hosts the fonts with @fontsource/inter and @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-face under the family names Inter and JetBrains Mono, exactly.
  • The @import in the comment at the top of tokens.css starts JetBrains Mono at 500; load 400 as well.
  • display=swap shows 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.css defines .type-display, .type-h1, .type-h2, .type-body, .type-ui and .type-code: family, weight, size, line height and letter-spacing. No colour, no margin.
  • CSS variables. The families are var(--ff-mono) and var(--ff-ui). Sizes and weights are not variables.
  • TypeScript. typography.h1 and fontFamily.mono come from @nalet/design-system (or @nalet/design-system/tokens): the same metrics as objects, ready to spread into a style.
  • Tailwind. The preset maps font-mono and font-sans to 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) and var(--ff-mono), so overriding a family variable doesn't reach them.