Typography

Deck styles the heading elements directly, so a page written in plain HTML is already typeset. The classes on this page are for the cases where the element and the appearance need to come apart — a <h3> that has to look like an <h1>, a paragraph that has to be small — plus the casing, alignment and writing-mode utilities.

Headings

.h1 through .h6 apply a heading's appearance to any element. Use them to keep the document outline honest: the heading level should come from where the section sits in the page, and the size from how it should look. A card title that is the third level of the document is an <h3> with .h5 on it, not an <h5>.

.display is a size above .h1, for the one line at the top of a landing page. .lede is the paragraph directly under a heading: larger, looser, muted, and capped at the measure.

The element carries the outline; the class carries the size

A third-level heading that looks like a fifth

The lede sets its own measure, so it stays readable however wide the column gets.

<h3 class="h5">A third-level heading that looks like a fifth</h3> <p class="lede">The lede sets its own measure, so it stays readable however wide the column gets.</p>
<h3 class="h5">A third-level heading that looks like a fifth</h3>
<p class="lede">The lede sets its own measure, so it stays readable however wide the column gets.</p>
Heading and prose classes
Class Declares Resolves to Source
.h1 font-family: var(--font-display); font-weight: 640; line-height: var(--leading-tight); letter-spacing: -.021em; color: var(--text) font-family: var(--font-sans); font-weight: 640; line-height: 1.12; letter-spacing: -.021em; color: light-dark(var(--ink-900), var(--ink-100)) 03-type.css:7
.h2 font-family: var(--font-display); font-weight: 640; line-height: var(--leading-tight); letter-spacing: -.021em; color: var(--text) font-family: var(--font-sans); font-weight: 640; line-height: 1.12; letter-spacing: -.021em; color: light-dark(var(--ink-900), var(--ink-100)) 03-type.css:7
.h3 font-family: var(--font-display); font-weight: 640; line-height: var(--leading-tight); letter-spacing: -.021em; color: var(--text) font-family: var(--font-sans); font-weight: 640; line-height: 1.12; letter-spacing: -.021em; color: light-dark(var(--ink-900), var(--ink-100)) 03-type.css:7
.h4 font-family: var(--font-display); font-weight: 640; line-height: var(--leading-tight); letter-spacing: -.021em; color: var(--text) font-family: var(--font-sans); font-weight: 640; line-height: 1.12; letter-spacing: -.021em; color: light-dark(var(--ink-900), var(--ink-100)) 03-type.css:7
.h5 font-family: var(--font-display); font-weight: 640; line-height: var(--leading-tight); letter-spacing: -.021em; color: var(--text) font-family: var(--font-sans); font-weight: 640; line-height: 1.12; letter-spacing: -.021em; color: light-dark(var(--ink-900), var(--ink-100)) 03-type.css:7
.h6 font-family: var(--font-display); font-weight: 640; line-height: var(--leading-tight); letter-spacing: -.021em; color: var(--text) font-family: var(--font-sans); font-weight: 640; line-height: 1.12; letter-spacing: -.021em; color: light-dark(var(--ink-900), var(--ink-100)) 03-type.css:7
.display font-size: var(--text-4xl); font-weight: 700; line-height: 1.04; letter-spacing: -.036em font-size: clamp(2.25rem, 1.5rem + 3.75vw, 3.75rem); font-weight: 700; line-height: 1.04; letter-spacing: -.036em 03-type.css:22
.lede font-size: var(--text-md); line-height: var(--leading-loose); color: var(--text-muted); max-inline-size: var(--measure) font-size: 1.0625rem; line-height: 1.72; color: light-dark(var(--ink-600), var(--ink-400)); max-inline-size: 68ch 03-type.css:29
.prose max-inline-size: var(--measure) max-inline-size: 68ch 03-type.css:105
.link-quiet color: var(--text); text-decoration: none color: light-dark(var(--ink-900), var(--ink-100)); text-decoration: none 03-type.css:50

.prose caps inline size at --measure (68ch), which is the line length research keeps landing on for continuous reading. It is the one class here that is about the block rather than the glyphs.

Size

Eight steps, each reading a --text-* token. The scale is a ratio rather than a series of round numbers, so the steps stay distinguishable at both ends. .text-cq and .display-cq size against the nearest .cq container instead of a fixed value, which is how a heading in a sidebar stays small on a wide monitor.

Font size utilities
Class Declares Resolves to Source
.text-2xs font-size: var(--text-2xs) font-size: .6875rem 09-utilities.css:87
.text-xs font-size: var(--text-xs) font-size: .75rem 09-utilities.css:88
.text-sm font-size: var(--text-sm) font-size: .8125rem 09-utilities.css:90
.text-base font-size: var(--text-base) font-size: .9375rem 09-utilities.css:91
.text-md font-size: var(--text-md) font-size: 1.0625rem 09-utilities.css:92
.text-lg font-size: var(--text-lg) font-size: clamp(1.125rem, 1.06rem + .32vw, 1.3125rem) 09-utilities.css:93
.text-xl font-size: var(--text-xl) font-size: clamp(1.3125rem, 1.19rem + .62vw, 1.625rem) 09-utilities.css:94
.text-2xl font-size: var(--text-2xl) font-size: clamp(1.5625rem, 1.33rem + 1.16vw, 2.125rem) 09-utilities.css:95
.text-cq .cq :is(.text-cq)
font-size: clamp(var(--text-sm), 3.4cqi, var(--text-lg))
font-size: clamp(.8125rem, 3.4cqi, clamp(1.125rem, 1.06rem + .32vw, 1.3125rem)) 18-container.css:32
.display-cq .cq :is(.display-cq)
font-size: clamp(1.5rem, 8cqi, 3.25rem); line-height: 1.05; letter-spacing: -.03em
— 18-container.css:33

Weight

Four weights, and two of them are not round numbers. Deck's default face is variable, so 540 and 620 are available and are better choices than 500 and 600: at text sizes 500 is nearly indistinguishable from 400, and 600 is heavy enough to read as bold when it is meant to read as emphasis.

Font weight utilities
Class Declares Resolves to Source
.fw-normal font-weight: 400 — 09-utilities.css:97
.fw-medium font-weight: 540 — 09-utilities.css:98
.fw-semi font-weight: 620 — 09-utilities.css:101
.fw-bold font-weight: 700 — 09-utilities.css:102

Alignment and casing

.text-start and .text-end are logical: they follow the text direction, so a right-to-left page mirrors without a second rule. There is no .text-left or .text-right, for the same reason there is no .ml-4.

.uppercase adds a small amount of letter-spacing along with the transform, because capitals set at the same tracking as lower case read as cramped. .nums switches on tabular figures, which is what you want in any column of numbers that has to line up.

Alignment, casing and numeral utilities
Class Declares Resolves to Source
.text-start text-align: start — 09-utilities.css:104
.text-center text-align: center — 09-utilities.css:105
.text-end text-align: end — 09-utilities.css:106
.uppercase text-transform: uppercase; letter-spacing: .04em — 09-utilities.css:122
.capitalize text-transform: capitalize — 09-utilities.css:123
.mono font-family: var(--font-mono) font-family: ui-monospace, "SF Mono", "Cascadia Mono", "Roboto Mono", Menlo, Consolas, monospace 09-utilities.css:121
.nums font-variant-numeric: tabular-nums — 09-utilities.css:120
.no-underline text-decoration: none — 09-utilities.css:124

Truncation

.truncate is one line with an ellipsis; .clamp-2 and .clamp-3 are two and three. All three hide text, so none of them should be the only place a value appears — put the full string in a title attribute or somewhere it can be read.

.truncate also sets min-inline-size: 0, without which it silently does nothing inside a flex row: a flex item will not shrink below its content by default, so the ellipsis never gets a chance to appear.

Truncation utilities
Class Declares Resolves to Source
.truncate overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-inline-size: 0 — 09-utilities.css:126
.clamp-2 display: -webkit-box; -webkit-box-orient: vertical; overflow: hidden — 09-utilities.css:132
.clamp-3 display: -webkit-box; -webkit-box-orient: vertical; overflow: hidden — 09-utilities.css:132

Writing modes

Six classes for vertical text — Japanese and Mongolian setting, and the rotated column header in a dense table, which is what .th-vertical is for. .writing-upright keeps Latin characters upright inside a vertical run instead of rotating them, and adds tracking, because upright Latin in a vertical column needs more air than horizontal Latin does.

Writing mode utilities
Class Declares Resolves to Source
.writing-vertical writing-mode: vertical-rl; text-orientation: mixed — 19-logical.css:89
.writing-vertical-lr writing-mode: vertical-lr — 19-logical.css:93
.writing-normal writing-mode: horizontal-tb — 19-logical.css:96
.writing-sideways writing-mode: sideways-lr — 19-logical.css:95
.writing-upright text-orientation: upright; letter-spacing: .1em — 19-logical.css:94
.th-vertical writing-mode: vertical-rl; rotate: 180deg; white-space: nowrap; block-size: 8rem; padding-block: var(--space-3); text-align: start writing-mode: vertical-rl; rotate: 180deg; white-space: nowrap; block-size: 8rem; padding-block: .75rem; text-align: start 19-logical.css:99

Direction and bidi

These are for text whose direction differs from the page around it: an English product name inside an Arabic sentence, a file path in a Hebrew paragraph. Without isolation the bidi algorithm reorders punctuation at the boundary and a path ending in a slash comes out with the slash on the wrong end.

.bidi-isolate is the one to reach for when you know the direction of the run. .bidi-plaintext is for user-supplied strings whose direction you do not know — it takes the direction from the first strong character in the string, which is what a chat message or a comment field needs. .code-ltr is the specific case of a code span or path, which is always left to right whatever the page is.

Direction and bidi utilities
Class Declares Resolves to Source
.dir-ltr direction: ltr; unicode-bidi: isolate — 19-logical.css:114
.dir-rtl direction: rtl; unicode-bidi: isolate — 19-logical.css:115
.bidi-isolate unicode-bidi: isolate — 19-logical.css:116
.bidi-plaintext unicode-bidi: plaintext — 19-logical.css:117
.code-ltr direction: ltr; unicode-bidi: isolate; text-align: start — 19-logical.css:123

When not to use these

  • Not instead of a heading element. .h2 on a <div> looks like a heading and is not one. Screen reader users navigate by heading, and a div is not in that list.
  • Not to shrink text below .text-xs for body copy. .text-2xs exists for a timestamp or a unit label beside something larger. A paragraph at that size is a paragraph nobody reads.
  • Not .truncate on something with no other home. A truncated string with no title and no detail view is a value the reader cannot get at.
  • Not .uppercase on a long run. Capitals lose the word shapes that make reading fast. It is for a label of one or two words.