Daybridge Design System
Foundations

Typography

Type scale, weights, line heights, and the safe zone

Font family

Daybridge uses Inter Variable as its primary typeface on the web. On iOS and Android, we use the platform system fonts (SF Pro and Roboto respectively).

"InterVariable", "SF Pro Display", -apple-system, BlinkMacSystemFont,
"Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans",
"Helvetica Neue", sans-serif

Text rendering

-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-rendering: optimizeLegibility;

Type scale

The standard UI size is 13px on desktop and 15px on touch surfaces. The scale uses ten steps; 3xs is the floor, for micro labels only, and is the one size that does not grow in the marketing tier:

Name
App
Marketing
3xs
TUE · GMT+1
10px · 500
TUE · GMT+1
10px · 450
2xs
Updated 3 min ago
11px · 500
Updated 3 min ago
12px · 450
xs
All day · London
12px · 500
All day · London
13px · 450
sm
Weekly team standup
13px · 500
Weekly team standup
15px · 450
md
Review the launch timeline
15px · 500
Review the launch timeline
17px · 450
lg
Upcoming events
18px · 500
Upcoming events
20px · 450
xl
Calendar settings
22px · 500
Calendar settings
24px · 500
2xl
Good morning
26px · 500
Good morning
28px · 500
3xl
Welcome to Daybridge
32px · 500
Welcome to Daybridge
36px · 500
4xl
Plan brilliantly
40px · 500
Plan brilliantly
44px · 500

Tiers

The same tokens resolve to different regimes: the app-scale and marketing-scale scope classes swap what every utility means inside their subtree, so shared components render correctly under either. The app tier is the default; scopes nest and the innermost wins. Never use rem-based type (text-base) — the root font size can't be scoped.

The touch tier

touch-scale layers over the app tier for phone and tablet surfaces. It is not a third scale: every step already resolves to a size a touch surface wants, so the only thing it moves is the resting size, from 13px to text-md's 15px.

AppTouch
base (unsized text)13px15px

That single change is what separates a scale read at arm's length on a desk from one read on a held device. Apply it alongside the app tier (class="app-scale touch-scale"), not instead of it.

Each tier's resting size is a different step, and in the marketing tier it is not a step at all:

TierResting sizeNamed step
App13pxtext-sm
Touch15pxtext-md
Marketing16pxnone

Where the resting size has a name, prefer that name to inheriting it. Text with no size class takes its ancestor's size, not the tier's resting size, so inside a heading or any other sized subtree there is no way back to body size — and because the padding tokens are line-height-relative, a control that inherits the wrong size changes shape as well as type. Omit the size only in components that must follow whichever tier they are rendered under.

On touch surfaces that gives:

RoleClass
Body, list rows, controlstext-md
Emphasised body, row headerstext-md font-emphasis
Captions, subtitles, section headerstext-sm
Micro labels, overlinestext-2xs
Large body, inputstext-lg
Page headerstext-xl
Displaytext-3xl

Emphasis is a weight, never a size bump: a list row and the header above it are the same size, separated by font-emphasis.

Size and weight are intrinsically linked — the weight shown in the scale table is what the size renders at, never something to set by hand. Body sizes rest at the tier's resting weight; the heading sizes (text-xl and above) carry 500 in both tiers via the size utility itself.

WeightAppMarketing
Resting (inherited)500450
text-xltext-4xl (intrinsic)500500
font-emphasis600550

Line height

Each font size has a set of line-height values depending on context. All are derived from the font size using CSS round() and clamp(), producing values snapped to a pixel grid. These tokens are the only sanctioned line heights — stock utilities (leading-tight, leading-snug, leading-relaxed, …) and arbitrary values should not be used.

Component line-height

For single-line UI elements — sidebar items, buttons, table cells, labels. Tight, just enough to contain the glyphs. This is the global default.

line-height: round(up, 1em + clamp(2px, 1em - 12px, 8px), 2px);

Reading line-height

For multi-line content — event descriptions, editor text, modal body copy. Generous, optimised for comfortable reading.

line-height: round(up, 1em + clamp(4px, 1em - 8px, 12px), 4px);

Airy line-height

For spacious multi-line content where reading still feels dense — long descriptions and editor bodies that benefit from extra breathing room.

line-height: round(up, 1em + clamp(6px, 1em - 4px, 16px), 4px);

Display line-height

For hero and display type above the component scale, where even the component formula's +8px cap reads loose. A bare multiplier snapped to the grid.

line-height: round(up, 1.05em, 4px);

Exact line boxes

leading-none (a 1em line box) is the sanctioned exception for single-line labels that need the line box to match the glyphs exactly — time labels positioned against a grid, avatar initials, badge numerals. The formulas can't produce it (component's floor is +2px), and it should never appear on anything that can wrap.

sm (13px)
Component
Calendar events are displayed with their title, time, and location. When an event spans multiple lines, the line height determines readability.
Sidebar items, table cells, single-line UI
Reading
Calendar events are displayed with their title, time, and location. When an event spans multiple lines, the line height determines readability.
Descriptions, multi-line content
md (15px)
Component
Calendar events are displayed with their title, time, and location. When an event spans multiple lines, the line height determines readability.
Modal labels, form fields
Reading
Calendar events are displayed with their title, time, and location. When an event spans multiple lines, the line height determines readability.
Editor body, long-form text

Padding around text

When text appears inside a boxed element — a button, tag, badge, or input — the component line-height defines a safe zone around the glyphs. Padding extends outward from this using the lh unit, which resolves to the element's computed line-height. This means padding scales automatically with text size — no per-size values needed.

Six density tiers, named with t-shirt sizes to match the radius and shadow scales:

/* xs — tags, badges, inline labels */
padding: 0.075lh 0.375lh;

/* sm — small buttons, chips, dense controls */
padding: 0.125lh 0.5lh;

/* md — buttons, standard controls */
padding: 0.25lh 0.625lh;

/* lg — prominent actions, large buttons */
padding: 0.4lh 0.875lh;

/* xl — text inputs, search fields */
padding: 0.625lh 0.875lh;

/* 2xl — touch controls */
padding: 0.8lh 1lh;

Both axes use lh (line-height-relative) so all padding scales proportionally with text size.

text-smtext-mdtext-lg
xs
Label
Label
Label
sm
Label
Label
Label
md
Label
Label
Label
lg
Label
Label
Label
xl
Label
Label
Label
2xl
Label
Label
Label

Setting element heights directly should be avoided. Instead, height is determined by the safe zone:

element height = line-height + (2 × vertical padding) + border

Inline congruence

When a badge or tag appears inline with text, its total height must fit within the surrounding text's reading line-height. Otherwise it pushes adjacent lines apart.

Rule: use one step down from the surrounding text size.

One step down (correct)13px tag in 15px text20px ≤ 24px
Meeting tagged Work scheduled for next Tuesday
Same size (overflows)15px tag in 15px text25px > 24px
Meeting tagged Work scheduled for next Tuesday

Text colors

Three semantic color tokens control text contrast. Each carries a descending semantic chroma multiplier, so in hued contexts vividness descends with emphasis instead of peaking at low contrast; neutral contexts are unaffected. Each maps to a luminance shade, and the resolved color depends on the color mode applied to the element — neutral text is gray, brand text takes the brand hue, and so on. See the colors guide for how semantic colors work.

LightDark
ShadeAlpha×CShadeAlpha×C
Text
high-contrast1510.9
white11
medium-contrast1110.55
510.55
low-contrast910.25
810.25

Examples

background · light
Event detailshigh-contrast
Team standup with design and engineering — recurring weeklymedium-contrast
Tomorrow · 10:00 AM · 30 minlow-contrast
background · dark
Event detailshigh-contrast
Team standup with design and engineering — recurring weeklymedium-contrast
Tomorrow · 10:00 AM · 30 minlow-contrast
surface · light
Event detailshigh-contrast
Team standup with design and engineering — recurring weeklymedium-contrast
Tomorrow · 10:00 AM · 30 minlow-contrast
surface · dark
Event detailshigh-contrast
Team standup with design and engineering — recurring weeklymedium-contrast
Tomorrow · 10:00 AM · 30 minlow-contrast
elevated · light
Event detailshigh-contrast
Team standup with design and engineering — recurring weeklymedium-contrast
Tomorrow · 10:00 AM · 30 minlow-contrast
elevated · dark
Event detailshigh-contrast
Team standup with design and engineering — recurring weeklymedium-contrast
Tomorrow · 10:00 AM · 30 minlow-contrast