Skip to main content

Using typography

Which role and step to use for each case, how to build hierarchy, which header step each heading level takes, and common mistakes.

Version: EDS 2.0 (beta)

Overview explains how to choose a role, and which groups each role's steps fall into. This page gives the role and step to use in specific cases.

Decision table​

The rows are sorted by role, and within a role by the groups of steps on the overview. Use the page-layout groups on dashboards, overview pages and front pages. If your case is not here, ask whether the text wraps to find the role, and the nearest row in that role is usually right. Headers are always medium, and a label or body row without a weight uses normal.

You are settingUseWhy
Headers for the page title
the title of the page, its h1header.4xlit names the whole page, so it takes the largest header step
Headers for sections
a section heading, h2header.3xlit names what follows, one level below the page title
a heading within a section, h3header.2xlone step down for each level
Headers for subsections
a subsection heading, h4 to h6header.xl, header.lg, header.mdone step down for each level, in that order
Headers with no default element
a footer column heading, an "On this page" heading, or a section heading in a filter or side panelheader.sm or header.xspage chrome that has to look smaller than an h6
Labels inside components
the text in a buttonlabel.md, mediumone line, and it does a job
the text in a small buttonlabel.sm, mediuma smaller button takes the next step down
a field's label, placeholder and valuelabel.mdone line each, at the default step for component text
a checkbox, radio or switch label, a tab, a menu item or a chiplabel.mdone line of component text, at the default step
a link, a breadcrumb or a tooltiplabel.mdthe same default. A link inside a paragraph takes the paragraph's step
an Optional or Required indicator, or an input adornment such as a unitlabel.xsshort text that sits beside a larger label and adds to it
a menu group titlelabel.xsa short title for the menu items under it, smaller than they are
a badgelabel.sm, mediuma short tag beside other text, set apart by its weight
a card or accordion titlelabel.md, mediuma title inside a component is a label, set apart by its weight
a card's content and metadatalabel.smshort lines under the card title, one step below it
the name beside an avatarlabel.smone line beside a small element, one step below the default
an avatar's metadatalabel.xsone step below the name beside the avatar
a dialog titlelabel.lg, mediumthe largest step inside components, for the one title a dialog has
a table celllabel.mdand Compact density for a dense table, rather than a smaller step
a chart label, or a caption of one linelabel.smshort and functional, even when it sits under running text
metadata, such as a date or a countlabel.smwith text.secondary for colour, rather than a smaller step
Labels in the page layout
the top-level navigation, or the app or product name in the page headerlabel.xlone line that has to read as the top of the page
a prominent call to action on an overview pagelabel.xlone line, set larger so it stands out in the page layout
the main value in a row of stat tileslabel.3xla value on one line, so it is label at any size
the values beside the main valuelabel.2xlone step below the main value
the one figure a page is about, such as a large readout, a timer, or a value on a control-room or wall displaylabel.4xl or label.5xlthe page is about this value, so it may look larger than the h1
the label of a key figure, such as "Production rate"label.sm or label.mdit names the value, at a much smaller step
Body text inside components
helper or error text under a fieldbody.smit can run to a sentence, so it wraps
text that wraps inside a component: dialog content, a banner, accordion content or a snackbarbody.mdit can wrap, even when it is short today
a description in a side panelbody.mdthe same step as other text that wraps inside a component
a footnote, a source note under a chart or a table, or a disclaimerbody.smsupplementary text that runs to sentences
Body text on the page
running text on a pagebody.lgit wraps, and it is read rather than scanned
the ingress, the introductory paragraph under a page titlebody.xlone step above the running text it introduces
Body text in the page layout
intro text on a front page or an overview page, a pull quote, or a short statement on an onboarding or empty pagebody.2xl or body.3xltext that opens a page, set large in the page layout
running text on a display read from a distancebody.2xl or body.3xlit wraps, and it is read from further away than a desk screen

Key figures can use Inter's tabular figures, where every digit has the same width, so a value that updates does not shift sideways.

Setting font size and line height together​

Take the font size and the line height from the same role and step:

.caption {
font-size: var(--eds-typography-label-sm-font-size);
line-height: var(--eds-typography-label-sm-line-height);
}

Leave out the line height and the text inherits one from its parent. Inside a paragraph, that is the paragraph's line height, so a caption ends up with the leading of running text. Mixing steps, such as a label.sm size with a label.md line height, breaks the rhythm in the same way, just less visibly.

Building hierarchy​

Reach for size first, then weight, then colour, and change as little as you need to:

  1. Size, one step at a time. Neighbouring steps are close together, so one step is often enough to set two pieces of text apart.
  2. Weight, with medium in a label. bold is for strong emphasis, so keep it rare.
  3. Colour, with text.secondary for supporting text, such as the inspection date in the example below.

Here is a row in a list of wells, with three levels of text:

31/2-A-14

Producing. Pressure has been within range since the valve on the north manifold was replaced.

Inspected two weeks ago

.well-name {
font-size: var(--eds-typography-label-lg-font-size);
line-height: var(--eds-typography-label-lg-line-height);
font-weight: var(--eds-font-weight-medium);
}
.well-status {
font-size: var(--eds-typography-body-md-font-size);
line-height: var(--eds-typography-body-md-line-height);
}
.well-inspected {
font-size: var(--eds-typography-label-sm-font-size);
line-height: var(--eds-typography-label-sm-line-height);
color: var(--eds-text-secondary);
}

The name and the date stay on one line, so they are label. The status can wrap, so it is body, even though it sits between them in the hierarchy.

Heading levels​

Each heading level has a header step that goes with it:

ElementStep
h1header.4xl
h2header.3xl
h3header.2xl
h4header.xl
h5header.lg
h6header.md

header.sm and header.xs have no level, and are for page-chrome headings that must look smaller than an h6.

Treat the mapping as a starting point. Designers pick a text style by its size, so when a layout needs large text, the h1 style is often the one that fits, and the page ends up with a second h1. Choose the element by the structure of the page, and the header step by how it should look. Here a section heading on an overview page is set at the size of a page title, and it stays an h2:

.overview-page h2 {
font-family: var(--eds-font-family-header);
font-size: var(--eds-typography-header-4xl-font-size);
line-height: var(--eds-typography-header-4xl-line-height);
font-weight: var(--eds-font-weight-medium);
}

Line length​

Keep each line of running text between 55 and 80 characters, including spaces. Shorter lines make the eye jump back too often, and longer lines make it hard to find the start of the next one.

Common mistakes​

These look right on screen, which is why they get through.

  • Binding the family one layer too low. In Figma, bind font-family.ui or font-family.header, not family/ui from the hidden font collection. The value is the same today, but the hidden layer is internal and can change without notice.
  • Binding line height to a spacing token. Line heights resolve to the same values as spacing, so a spacing token in the line-height field looks correct, but it gives the wrong line height as soon as either scale changes. Use the line height of the step you set the size from.
  • Skipping a library update after a rename. When a style or variable is renamed, Figma brings in the new one and keeps the old one bound where it was used. Text on an old name still renders, but it no longer follows the system. Accept the update and rebind.

Accessibility​

  • The smallest step supports a larger label. label.xs is the smallest step, and Compact makes it smaller still. Use it for short text that sits beside a larger label and adds to it, such as an Optional indicator or the unit after a value, and never for primary content or for text that has to be read on its own.
  • Check small headings in Compact. Compact sets header.xs one step smaller, so check that a heading in it is still easy to read.
  • Keep heading levels in order. Screen reader users move through a page by its headings, so the heading elements must follow the structure of the page, whatever header step each one is set at.
  • Text must survive the user's own spacing. WCAG 2.1 success criterion 1.4.12 asks that content still works when someone increases line height and the spacing between letters, words and paragraphs. Components must not clip or overlap text when that happens, whichever role the text uses.
  • Offer a way back from Compact. If you use Compact for a whole product, give users a way to switch to Comfortable, so that someone who needs larger text can get it back.
  • Do not rely on weight alone. Pair a change of weight with size, position or colour, so the hierarchy is clear to someone who cannot see the difference in weight.

Do's and Don'ts​

Do
  • Ask whether the text wraps, and pick label or body from the answer
  • Pick the heading element by the structure of the page, and its header step by how it should look
  • Take the font size and line height from the same step
  • Use sentence case for headers and labels, capitalising only the first word
  • Let density resize text, with one attribute on a wrapper
  • Use text.secondary to make text matter less, before making it smaller
Don't
  • Wrap the text of a button or a chip over several lines. Shorten it instead
  • Use header for text that does not name what follows it, such as a large key figure or an eyebrow (the small category text above a title). There is no overline style
  • Pick a smaller step to fit a dense view. That reimplements Compact and drifts from every other dense view in the product
  • Mix typefaces within one piece of UI. Equinor is for headers, Inter for everything else
  • Set font weights by number. Use the three weight tokens

Where to go next​

  • Token reference: every token and every value, in all three densities.
  • The type scale: why the steps are these sizes, and how density maps onto them.