|
| 1 | +import TypeSpecimen from '@site/src/components/TypeSpecimen' |
| 2 | + |
1 | 3 | # Typography |
2 | 4 |
|
3 | | -Typography presents hierarchy and organises information as clearly and efficiently as possible. |
| 5 | +Typography presents hierarchy and organises information as clearly and efficiently as possible. In EDS 2.0 it does more than that — type is also the **minimum spacing unit** the rest of the system is built on. Padding, gap, line-height and component height all derive from the active text size. |
| 6 | + |
| 7 | +## Principles |
| 8 | + |
| 9 | +EDS 2.0 typography rests on four principles. They sit underneath every decision about size, weight, line-height and alignment. |
| 10 | + |
| 11 | +### To ensure safety – legibility always comes first |
| 12 | + |
| 13 | +When it comes to typography, ensuring safety is our main goal, and we make sure of it through legibility. Anything that compromises a user's ability to read accurately and quickly is a regression, no matter how much it improves on other axes. |
| 14 | + |
| 15 | +### We build from the inside to the outside |
| 16 | + |
| 17 | +We build from our minimal unit — the text — and from there we define spatial proportions for paddings, line-heights and gaps that ensure harmony. Spacing isn't decided in isolation; it follows from the text it surrounds. |
| 18 | + |
| 19 | +### Visual alignment over metric alignment |
| 20 | + |
| 21 | +All our metrics are ruled by the need to create harmony. By creating harmonic visual rhythms in interfaces we make them easier to use. When the numbers and the eye disagree, the eye wins. |
| 22 | + |
| 23 | +### A balance between flexibility and rigidity |
| 24 | + |
| 25 | +We search for a balance of options that avoids decision paralysis but still gives teams enough freedom to express their specific needs. The system is opinionated where consistency matters, and open where context varies. |
| 26 | + |
| 27 | +## Typefaces |
4 | 28 |
|
5 | | -## Font families |
| 29 | +EDS 2.0 uses two typefaces, each with a clear job: |
6 | 30 |
|
7 | | -EDS uses two font families: |
| 31 | +| Context | Typeface | |
| 32 | +| ---------------- | ---------------------------- | |
| 33 | +| UI labels & body | **Inter** (variable) | |
| 34 | +| Headings | **Equinor** (brand typeface) | |
8 | 35 |
|
9 | | -- **Equinor** -- Primary typeface for headings (`data-font-family="header"`) |
10 | | -- **Inter** -- UI/body typeface for interface text such as buttons, inputs, and labels (`data-font-family="ui"`) |
| 36 | +Inter covers all UI text — button labels, form fields, captions, prose. Equinor is reserved for display headings. Equinor is rendered slightly larger at every step than Inter — its shorter x-height needs a small upward bump so headings and surrounding UI text feel optically equivalent. |
| 37 | + |
| 38 | +### Specimens |
| 39 | + |
| 40 | +The same modular scale drives both typefaces. Spacious density (`lg` = 16 px) shown. |
| 41 | + |
| 42 | +<TypeSpecimen /> |
11 | 43 |
|
12 | 44 | ### Loading fonts |
13 | 45 |
|
14 | 46 | To use EDS components correctly, you must load both font families. The recommended approach is the EDS variable font stylesheet, which includes both Equinor and Inter: |
15 | 47 |
|
16 | 48 | ```html |
17 | | -<link rel="stylesheet" href="https://cdn.eds.equinor.com/font/eds-uprights-vf.css" /> |
| 49 | +<link |
| 50 | + rel="stylesheet" |
| 51 | + href="https://cdn.eds.equinor.com/font/eds-uprights-vf.css" |
| 52 | +/> |
18 | 53 | ``` |
19 | 54 |
|
20 | 55 | :::warning eds-uprights-vf.css required for EDS 2.0 components |
21 | 56 | The older `equinor-font.css` stylesheet only includes the Equinor font. If you use EDS 2.0 (`next`) components with only `equinor-font.css`, UI components like Button and TextField will fall back to a generic sans-serif because the Inter font is missing. |
22 | 57 | ::: |
23 | 58 |
|
24 | | -## Guidelines |
| 59 | +## The modular type scale |
| 60 | + |
| 61 | +Too many type sizes cause confusion. EDS 2.0 uses a single modular scale with ten steps from `xs` to `6xl`, anchored at `lg`. Steps follow a geometric progression — five steps per octave (`pow(2, n/5)`) — so consecutive sizes always feel like the same kind of jump. |
| 62 | + |
| 63 | +| Step | Multiplier | Spacious (lg = 16px) | |
| 64 | +| ----- | ---------------- | -------------------- | |
| 65 | +| `xs` | ×2^(−3/5) ≈ 0.66 | 0.65625rem (10.5px) | |
| 66 | +| `sm` | ×2^(−2/5) ≈ 0.76 | 0.75rem (12px) | |
| 67 | +| `md` | ×2^(−1/5) ≈ 0.87 | 0.875rem (14px) | |
| 68 | +| `lg` | ×1 | 1rem (16px) | |
| 69 | +| `xl` | ×2^(1/5) ≈ 1.15 | 1.15625rem (18.5px) | |
| 70 | +| `2xl` | ×2^(2/5) ≈ 1.32 | 1.3125rem (21px) | |
| 71 | +| `3xl` | ×2^(3/5) ≈ 1.52 | 1.53125rem (24.5px) | |
| 72 | +| `4xl` | ×2^(4/5) ≈ 1.74 | 1.75rem (28px) | |
| 73 | +| `5xl` | ×2^(5/5) = 2 | 2rem (32px) | |
| 74 | +| `6xl` | ×2^(6/5) ≈ 2.30 | 2.3125rem (37px) | |
| 75 | + |
| 76 | +:::warning Reserve `xs` for decoration |
| 77 | +At spacious density `xs` is 10.5 px and at comfortable it falls to ~9 px. Use it for decorative or supplementary labels (badges, captions on dense visualisations), never for body copy or primary content. |
| 78 | +::: |
| 79 | + |
| 80 | +<iframe |
| 81 | + class="sb-iframe" |
| 82 | + src="https://storybook.eds.equinor.com/iframe.html?globals=&args=&id=eds-2-0-beta-foundation-typography--ui-text" |
| 83 | + width="100%" |
| 84 | + height="520" |
| 85 | +></iframe> |
| 86 | +
|
| 87 | +[View in Storybook](https://storybook.eds.equinor.com/?path=/story/eds-2-0-beta-foundation-typography--ui-text) |
25 | 88 |
|
26 | | -The Equinor typeface is the primary typeface and is available in four weights: `light`, `regular`, `medium` and `bold` with accompanying italics. Please do not use `light` in digital interfaces except in special cases where the font size is over 48px. |
| 89 | +_Demo shows `xs`–`3xl`; the full scale extends to `6xl`._ |
27 | 90 |
|
28 | | -:::info Note |
29 | | -Typography is called text styles in Figma. |
| 91 | +## Density |
| 92 | + |
| 93 | +The whole scale shifts when density changes — only the `--_base` value moves; every derived size and line-height re-derives automatically. |
| 94 | + |
| 95 | +| Density | Base | |
| 96 | +| ------------------ | ----- | |
| 97 | +| Spacious (default) | 16 px | |
| 98 | +| Comfortable | 14 px | |
| 99 | + |
| 100 | +Density is applied by setting `data-density` on an ancestor element. Components within that ancestor then read the cascaded base. The cascade reaches every typography axis (`font-size`, `line-height`, `tracking`, `font-weight`), so a single `data-density` is enough — you don't need to set `data-font-size` or other axes on individual components. |
| 101 | + |
| 102 | +```html |
| 103 | +<div data-density="comfortable"> |
| 104 | + <!-- everything here uses the comfortable scale --> |
| 105 | +</div> |
| 106 | +``` |
| 107 | + |
| 108 | +:::info Accessibility |
| 109 | +When you opt into `comfortable`, give users a way to switch back to `spacious`. Smaller default text shouldn't be a one-way door. |
30 | 110 | ::: |
31 | 111 |
|
| 112 | +## Line-height: default vs. squished |
| 113 | + |
| 114 | +Every size step has two line-height variants. Pick the one that matches what the text is doing, not the size it happens to be. |
| 115 | + |
| 116 | +- **`squished`** — tight leading for UI controls. Use this wherever text sits inside a component (buttons, inputs, badges, tabs). The reduced line-height keeps controls compact without sacrificing single-line legibility. |
| 117 | +- **`default`** — comfortable leading for reading. Use this for body copy, descriptions, tooltips, and any text that may wrap to multiple lines. |
| 118 | + |
| 119 | +<iframe |
| 120 | + class="sb-iframe" |
| 121 | + src="https://storybook.eds.equinor.com/iframe.html?globals=&args=&id=eds-2-0-beta-foundation-typography--long-form-text" |
| 122 | + width="100%" |
| 123 | + height="640" |
| 124 | +></iframe> |
| 125 | +
|
| 126 | +[View in Storybook](https://storybook.eds.equinor.com/?path=/story/eds-2-0-beta-foundation-typography--long-form-text) |
| 127 | + |
| 128 | +_Demo shows `xs`–`3xl`; the full scale extends to `6xl`._ |
| 129 | + |
| 130 | +<details> |
| 131 | +<summary>Line-height multipliers per step</summary> |
| 132 | + |
| 133 | +The multiplier decreases as font size increases, following an ease-out cubic curve — `1.39 − pow(n/9, 3) × 0.29` for default and `1.13 − pow(n/9, 3) × 0.13` for squished, where `n` is the step index (0 = `xs`, 9 = `6xl`). Reduction is barely perceptible at small sizes and accelerates toward the top of the scale, keeping large display text optically tight without compressing body copy. |
| 134 | + |
| 135 | +| Step | Default ≈ | Squished ≈ | |
| 136 | +| ----- | --------- | ---------- | |
| 137 | +| `xs` | 1.39 | 1.13 | |
| 138 | +| `sm` | 1.39 | 1.13 | |
| 139 | +| `md` | 1.39 | 1.13 | |
| 140 | +| `lg` | 1.38 | 1.13 | |
| 141 | +| `xl` | 1.36 | 1.12 | |
| 142 | +| `2xl` | 1.34 | 1.11 | |
| 143 | +| `3xl` | 1.30 | 1.09 | |
| 144 | +| `4xl` | 1.25 | 1.07 | |
| 145 | +| `5xl` | 1.19 | 1.04 | |
| 146 | +| `6xl` | 1.10 | 1.00 | |
| 147 | + |
| 148 | +</details> |
| 149 | + |
32 | 150 | ### Paragraph length |
33 | 151 |
|
34 | | -Paragraph length is the number of characters in a line of text---this includes spaces. To ensure readability, line length should be 55-80 characters. Lines less than 55 characters can cause strain on the eye requiring the eye to jump to the next line too quickly, breaking the reading rhythm. Lines greater than 80 characters can make it difficult for users to continue on the correct line in a large body of text. |
| 152 | +For prose, line length should sit between **55–80 characters** including spaces. Shorter lines force the eye to jump too often, breaking the reading rhythm; longer lines make it hard to find the next line in a large block of text. |
35 | 153 |
|
36 | | -## Styles |
| 154 | +## Baseline-grid alignment |
37 | 155 |
|
38 | | -Too many type sizes can cause confusion. The EDS has a limited set of type sizes that work well together. |
| 156 | +EDS 2.0 treats single-line and multi-line text differently. |
39 | 157 |
|
40 | | -### Headings |
| 158 | +**Single-line UI text is _not_ snapped to the baseline grid.** The component derives its height from cap-height (`1cap`) rounded to the nearest 4 px, then distributes the remaining line-height symmetrically as padding. This keeps element height predictable and optically centred regardless of font. |
41 | 159 |
|
42 | | -There are seven headings to choose between: `H1bold`, `H1`, `H2`, `H3`, `H4`, `H5` and `H6`. Sentence case (The quick brown fox…) should be used instead of title case (The Quick Brown Fox…) since it contributes to better readability. |
| 160 | +**Multi-line text uses baseline trimming** (`text-box: trim-both ex alphabetic`). The top of the text block aligns to the baseline grid, so wrapping content sits consistently alongside other elements. |
43 | 161 |
|
44 | | -### Paragraph |
| 162 | +:::danger Anti-pattern: multi-line button labels |
| 163 | +Multi-line button labels are harder to scan and weaker as call-to-action elements. Shorten the label instead. The `multiline` prop on Button is a safety valve, not a design choice. |
| 164 | +::: |
45 | 165 |
|
46 | | -There are many paragraph styles to choose between: `Overline`, `Ingress`, `Body long`, `Body long link`, `Body long italic`, `Body long bold`, `Body long bold italic`, `Body short`, `Body short link`, `Body short italic`, `Body short bold`, `Body short bold italic` and `Caption`. |
| 166 | +## Categorisation: where text lives |
47 | 167 |
|
48 | | -The style `Body short` is used for short sentences containing around four words. It is most commonly used within components. |
| 168 | +The system categorises every piece of UI by what surrounds the text. The category controls how the text is aligned vertically. |
49 | 169 |
|
50 | | -### Additional styles |
| 170 | +- **Elements** — Headings and body copy. Aligned to the baseline grid. |
| 171 | +- **Selectables** — Anything clickable that contains a single line of text: chips, buttons, autocomplete, search, text fields, menu items, list items, labels, breadcrumbs, pagination. **Vertically centred** to the UI control. |
| 172 | +- **Containers** — Popovers, tables, cards, side sheets, accordions, banners, dialogs and similar surfaces. Aligned to the baseline grid. |
| 173 | +- **Pages** — Top-level groups of containers (the main content area excluding top bar and sidebar). Aligned to the baseline grid. |
51 | 174 |
|
52 | | -There are other typography styles that are used internally within components. |
| 175 | +Knowing the category up-front tells you which line-height variant to pick and which alignment behaviour to expect. |
| 176 | + |
| 177 | +## Tokens |
| 178 | + |
| 179 | +| Concept | Token | |
| 180 | +| ----------- | ------------------------------------------------------- | |
| 181 | +| Font family | `--eds-typography-header-font-family` | |
| 182 | +| | `--eds-typography-ui-body-font-family` | |
| 183 | +| Font size | `--eds-typography-ui-body-{size}-font-size` | |
| 184 | +| | `--eds-typography-header-{size}-font-size` | |
| 185 | +| Font weight | `--eds-typography-{family}-{size}-font-weight-lighter` | |
| 186 | +| | `--eds-typography-{family}-{size}-font-weight-normal` | |
| 187 | +| | `--eds-typography-{family}-{size}-font-weight-bolder` | |
| 188 | +| Line height | `--eds-typography-{family}-{size}-line-height-default` | |
| 189 | +| | `--eds-typography-{family}-{size}-line-height-squished` | |
| 190 | +| Tracking | `--eds-typography-{family}-{size}-tracking-tight` | |
| 191 | +| | `--eds-typography-{family}-{size}-tracking-normal` | |
| 192 | +| | `--eds-typography-{family}-{size}-tracking-wide` | |
| 193 | + |
| 194 | +`{family}` is one of `ui-body`, `header`. `{size}` is one of `xs`, `sm`, `md`, `lg`, `xl`, `2xl`, `3xl`, `4xl`, `5xl`, `6xl`. |
53 | 195 |
|
54 | 196 | ## Implementation in Figma |
55 | 197 |
|
56 | | -### How to add |
| 198 | +EDS 2.0 doesn't ship a per-layer text-style library. Instead, all text is rendered through a single **Typography component** whose appearance is driven by variable modes set on the instance itself. The same `--eds-typography-*` variables that resolve in code (`font-family`, `font-size`, `font-weight`, `line-height`, `tracking`) resolve in Figma — switching a mode swaps the values they point to. |
| 199 | + |
| 200 | +To set typography in Figma: |
| 201 | + |
| 202 | +1. From the **Assets** panel, drag in an **EDS Typography** instance. |
| 203 | +2. Type the content into the `text` property. |
| 204 | +3. With the instance selected, open the **Variables** section in the **Design** panel and pick the mode for **size**, **weight**, **family**, **line-height** and **density**. The instance updates immediately. |
| 205 | +4. Nest the instance inside whatever container needs it — buttons, list items, page headings — without flattening or detaching. |
| 206 | + |
| 207 | +A freshly dragged-in instance leaves every mode at its default, so it inherits whatever modes are set on its parents. Set the density mode once on a page-level frame and every default Typography instance inside follows. The cascade stops at any instance where you've explicitly set a mode — that override sticks regardless of the parent. |
57 | 208 |
|
58 | | -1. Locate the _layer_ in the **Layers Panel** that needs typography style applied. |
| 209 | +## Do's and Don'ts |
59 | 210 |
|
60 | | -2. Locate the **Design** tab in the **Inspector Panel**. |
| 211 | +:::info[Do] |
61 | 212 |
|
62 | | -3. Under the **Text** section, open the **Style library** menu to view the text styles in order of relevance. |
| 213 | +- Pick the line-height variant by what the text is doing — `squished` for controls, `default` for prose |
| 214 | +- Use sentence case (capitalise only the first word) for headings and labels |
| 215 | +- Keep paragraph lines between 55–80 characters |
| 216 | +- Treat density as a global setting on an ancestor — don't switch it per component |
| 217 | +- Provide a way to switch back to spacious when you opt into comfortable density |
63 | 218 |
|
64 | | -4. Choose a style by clicking on the style needed. |
| 219 | +::: |
| 220 | + |
| 221 | +:::danger[Don't] |
| 222 | + |
| 223 | +- Wrap button or chip labels over multiple lines — shorten the label instead |
| 224 | +- Mix typefaces within a single piece of UI; let Inter do the UI work and Equinor do the headlines |
| 225 | +- Rely on font weight alone to communicate hierarchy without supporting size or position |
| 226 | +- Override `font-size` directly in components — set density on a parent and let the scale re-derive |
| 227 | + |
| 228 | +::: |
0 commit comments