Skip to content

Typography

Seven fluid sizes, three font tokens, and a base layer that makes plain HTML read as designed — no classes required.

Scale

The scale is fluid — every step is a clamp() that breathes with the viewport; resize the window and every specimen below resizes with it.

TokenSpecimenDefault
--k-text-xs The quick brown shipwright clamp(0.78rem, 0.75rem + 0.15vw, 0.84rem)
--k-text-s The quick brown shipwright clamp(0.88rem, 0.85rem + 0.18vw, 0.95rem)
--k-text-m The quick brown shipwright clamp(1rem, 0.96rem + 0.22vw, 1.1rem)
--k-text-l The quick brown shipwright clamp(1.15rem, 1.06rem + 0.45vw, 1.35rem)
--k-text-xl The quick brown shipwright clamp(1.4rem, 1.22rem + 0.9vw, 1.8rem)
--k-text-2xl The quick brown shipwright clamp(1.8rem, 1.48rem + 1.6vw, 2.5rem)
--k-text-3xl The quick brown shipwright clamp(2.4rem, 1.9rem + 2.6vw, 3.6rem)

Headings

Headings take the display font, weight, and tracking from --k-font-display, --k-display-weight, and --k-tracking — retune those three tokens and every heading on the site follows. The design page pairs them for you. Rendered live, h1 through h4:

Weights & styles

The scale is one axis; weight and style are the other. keel uses exactly two weights — 400 for reading and 650 for emphasis and display — plus the italic, underline, highlight, and mono treatments the base layer ships. Every specimen below renders in the current pairing, so this table is also how you audit a font choice: swap the tokens on the design page and re-read this page.

SpecimenThe CSS that produces it
Regular body — the reading weight font-weight: 400 — the body default, no rule needed
Strong — the emphasis weight strong { font-weight: 650 } — dt, labels, and th share it
Display — headings and stat values font-weight: var(--k-display-weight) in var(--k-font-display) — one token, default 650
Italic — a voice change, not a weight change em — the browser's italic, untouched
The link style — accent, thin underline a { color: var(--k-accent); text-decoration-thickness: 1px; text-underline-offset: 0.2em }
Small — a size step down and a shade softer small { font-size: var(--k-text-s); color: var(--k-text-soft) }
A sentence with a marked phrase in it mark { background: var(--k-accent-soft) } — derived, so always on-brand
Inline code sits in running text code { font-family: var(--k-font-mono); background: var(--k-surface-2) }

Why 650 and not "bold"? The system stacks carry 400 and 650–700 reliably on every OS, and the six self-hosted faces ship exactly 400 + 700 — so keel never asks for a weight a font can't honestly render.

Markup
<p>Regular body, <strong>strong</strong>, <em>italic</em>,
<a href="…">a link</a>, <small>small print</small>,
a <mark>marked phrase</mark>, and inline <code>code</code>.</p>

<!-- the display treatment is what every heading gets for free -->
<span style="font-family: var(--k-font-display);
             font-weight: var(--k-display-weight);
             letter-spacing: var(--k-tracking)">…</span>

Prose

All of this is the base layer — plain HTML, zero classes. A paragraph with a link, strong text, emphasis, small print, a marked phrase, inline code, and a Ctrl key.

A blockquote gets the accent edge, softer text, and the large step — quote something worth the room.

  • An unordered list
  • with sensible indentation
  • and hairline gaps between items
  1. An ordered list
  2. gets the same treatment
  3. numbers included

Fonts

TokenWhat it doesDefault
--k-font-display Headings and stat values. system-ui, "Segoe UI", sans-serif
--k-font-body Everything else. system-ui, "Segoe UI", sans-serif
--k-font-mono Code, kbd, kickers, eyebrows, the brand mark. ui-monospace, Consolas, "Cascadia Mono", monospace

The design page ships twelve ready pairings for these tokens. Six stay on system stacks — Grotesk, Editorial, Humanist, Console, Rounded, Print — zero requests, rendered with whatever the OS has. Six are real typefaces self-hosted with this site as woff2 (SIL OFL licensed, ~300 KB total, lazy-loaded per face): Inter, Manrope, Space Grotesk, Playfair Display, Lora, and JetBrains Mono — identical on every OS. The framework itself ships no fonts; the export tells you which woff2 files to copy, or gives a Google Fonts link if you'd rather link than host.

Leading & rhythm

Two tokens set the vertical feel: --k-leading (1.65) is the body line-height, and --k-leading-tight (1.12) is the heading line-height. Vertical rhythm between blocks is automatic — a heading that follows content gets room to breathe with no classes; the base page documents that smart content spacing.

Type in context

Specimens prove the tokens; compositions prove the system. Three small pieces of real typographic work, each built from nothing but the scale, the color steps, and the mono token.

The lede

An article opener: the heading does the display work, then one paragraph steps up to k-text-l and softens to k-text-soft — bigger but quieter, so it reads as an invitation rather than a second heading.

The harbor rebuild, one year on

Twelve months after the storm, the shipwrights have a working slipway, a new saw shed, and a waiting list — this is how they did it.

Markup
<h3>The harbor rebuild, one year on</h3>
<p class="k-text-l k-text-soft">Twelve months after the storm, …</p>

The stat callout

A big number with a small caption: .k-stat__value takes the display font and weight, .k-stat__label drops to the xs step, faint and uppercase. There's no wrapper class — any element holds a pair, and a .k-cluster rows several up.

31hulls restored
4.2toak reclaimed
0days lost
Markup
<div class="k-cluster">
  <div><span class="k-stat__value">31</span><span class="k-stat__label">hulls restored</span></div>
  <div><span class="k-stat__value">4.2t</span><span class="k-stat__label">oak reclaimed</span></div>
</div>

The meta row

The dense caption under a title — byline, date, reading time. Everything drops to k-text-xs and fades to k-text-faint; the machine-readable parts (time) take k-mono so figures line up and read as data.

Steam-bending white oak, safely

By R. Daiven · · 6 min read · in Workshop notes

Markup
<h4>Steam-bending white oak, safely</h4>
<p class="k-text-xs k-text-faint">By R. Daiven
· <time class="k-mono" datetime="2026-07-12">2026-07-12</time>
· 6 min read · in <a href="…">Workshop notes</a></p>

Text utilities

For one-off adjustments, the utilities page documents the Text group — k-text-* for size, color, and alignment, plus k-bold, k-mono, k-uppercase, k-truncate, and k-balance — every value a token, nothing arbitrary.