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.
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>
| 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.
| 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.
| 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.
| 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.
| 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.
| 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.
| 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.
.h2on 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-xsfor body copy..text-2xsexists for a timestamp or a unit label beside something larger. A paragraph at that size is a paragraph nobody reads. -
Not
.truncateon something with no other home. A truncated string with notitleand no detail view is a value the reader cannot get at. -
Not
.uppercaseon a long run. Capitals lose the word shapes that make reading fast. It is for a label of one or two words.