Skip to content
TYPO3Soul Design System

Type

The split between the type families is semantic rather than decorative.

Source Sans 3 carries everything a person wrote. Source Code Pro carries everything the machine reads, writes or names — tool names, arguments, paths, versions, CLI fragments — at every size, including headings. --font-size-body, packages/frontend/src/tokens/colors.css, make verify. Never title-cased, never prettified.

Both are vendored under packages/frontend/fonts/ rather than pulled from a font host, so a rendered design sets in the right type behind a strict content policy or with no network at all. They are generated from @fontsource by make fonts and committed because the frontend package publishes them. A fresh clone and a consuming project therefore have the same faces without a setup step or a network request at render time.

Each upright family uses a variable face across the weight axis; Source Sans also ships its variable italic face. The page preloads the upright latin faces, and font-display: optional keeps a late response from replacing a fallback after paint. The first visit may stay in the fallback on a slow connection; later visits use the cached face without a layout-changing swap.

Weight is bound by role as well. Running text, labels, headings and emphasis select named --weight-* tokens rather than literals. Italic is reserved for semantic emphasis and citation rather than used as another heading style. Variable faces make those changes without adding another font request.

One scale, bound by role#

tokens/typography.css holds every size, and tokens/controls.css binds component roles to it — a button is --font-size-ui, a table head is --font-size-label — without writing another value. Controls still set tighter than prose, but they use the same steps. A size a component needs is one the scale already names, or the scale is what gains it.

Size tokens live under --font-size-*. The --text-* namespace is reserved for text colour, so a token's name says whether it changes the register or the ink before its value is read.

The role is the durable decision. A literal on a button creates a second type scale whose relationship to prose exists only in that declaration; binding the button role to --font-size-ui lets the scale move without leaving a control behind.

Font-size tokens use whole pixels. A half-pixel step is neither a distinct role nor an optical correction tied to context, so it creates an unnameable step between the named ones and produces less predictable rasterisation. A relative em adjustment may still be used for optical alignment; that is a ratio of its context, not another font size in the scale.

Scale#

Type scale · 700x246

Titles and bodies move together#

A titled component chooses a register rather than separate sizes for its heading and text. Notes, surfaces, modals and accordions use the block register; cards and results use the entry register because their titles are destinations. Each register binds a title token and the shared body token, so the relationship between them moves as one decision.

Sentence-bearing blocks use the reading register wherever they stand. A note should not become dense merely because its sentences sit in a box. Code, compact rows and captions choose dense roles explicitly because they are scanned as machine content. Density follows how the content is read, not whether it has a border or happens to sit inside .sds-prose.

This is why a component never chooses --font-size-* directly for one half of a titled block. Doing so creates an independent relationship between title and body which the register tokens can no longer keep together.

Display and headings#

Headings use sentence case. Writing carries the rest of the voice: how claims are bounded, why machine names stay verbatim and why promotional superlatives do not belong in a precise interface.

A heading sets itself: h1 to h3 carry the size their level usually has, so an outline written as plain HTML is already set, and below h3 everything is the UI register. sds-h1, sds-h2 and sds-h3 are for where the two disagree — a section that stands third in the outline and reads as the first thing on the page. The level is the document's and the size is the page's; the class is how the page says so without moving the heading in the outline. sds-display is not one of them: it is the opening line of a page rather than a step in the outline, and it says nothing about level at all.

Display & headings · 700x180

Body and lead#

Body & lead · 700x240

Lists#

A list is indented by the width of its own marker, so an item's text lines up with the text of the paragraph above it, and the marker is muted — it is punctuation for the item, not part of what the item says. The element carries all of that: a <ul> an editor emitted is already set, with no class on it.

Two classes say what the element cannot. sds-list puts air between items that are each a sentence or two; sds-list--plain takes the markers and the top-level indent off a list whose items are links, where every item is marked by being a link already.

Lists · 700x280

The one thing the system does not decide is which marker an ordered list counts with. A source that said a. or i. arrives as the type attribute HTML has for it, and that attribute carries no weight at all — so the rules here are written to leave it alone wherever it is set, rather than quietly turning every lettered list back into a numbered one.

Mono and labels#

A mono run inside a sentence comes down to --font-mono-optical. Every glyph in Source Code Pro carries the same advance, so a phrase set in it at the size around it reads a step larger than that size and pushes the line apart. The token is a ratio of its context rather than a step in the scale, which is what lets a 13px note and 16px body copy each get their own answer from one value. It is stated once, in tokens/fonts.css, and every mono run — code, kbd, samp, .sds-mono, an option name, a formula the renderer left as source — reads it from there.

Inline code carries no box and no colour of its own. A tinted, bordered, padded chip breaks the line's rhythm at every occurrence, and a reference sentence can hold four of them; a second ink would have to come from a palette where orange is already the link and green, amber and red already mean a status, so it would either collide with a link or claim a meaning the run does not have.

What sets the run apart is a step of weight instead. It is set in --weight-medium where the sentence around it is regular, which puts back the colour the smaller size took out and costs neither a box nor a hue — the face on its own reads as a lighter grey at that ratio, and the run goes under in running text. A block the machine wrote takes the step straight back off: inside it the block has already decided the size, the weight and the colour, and a whole block set in medium is one asking to be read as emphasis.

Mono & labels · 700x201