Resinen
Foundations

Typography

One grotesque and one mono, swapped as a pair. Four pairings ship, and the switcher in the top bar changes the live page — the faces are a token like any other.

The four pairings

A pairing sets --font-sans and --font-mono together, so there is no way to mismatch them. Each carries a full fallback stack, which means an app that loads no webfonts lands on System rather than on Times.

Systemdata-font="system"

SF Pro on Apple hardware, Helvetica Neue elsewhere. Zero bytes over the wire, and already the Swiss face.

Interdata-font="inter"

Built for screens, enormous weight range, tight default tracking. The closest thing to a neutral grotesque that was designed for pixels.

IBM Plexdata-font="plex"

A corporate grotesque with actual warmth — slightly humanist, less anonymous than Inter.

Archivodata-font="archivo"

Grotesque with a wider lowercase and firmer terminals. Holds up best at display sizes.

The scale

A modular scale at roughly 1.2, on a 15px body. The product default is 14px — a dashboard set at 16px reads as a marketing page, and the two sizes below the body are where most of a real interface actually lives.

Display — 600 / −0.022em / 1.05Dead reckoning
Heading 1 — 600 / −0.014emCharts and soundings
Heading 2 — 600 / 22pxRunning lights
Heading 3 — 600 / 17pxBerth allocation
UI — 400 / 14px — the product defaultPins untouched for sixty days stop counting toward Beat.
UI small — 400 / 13px — hints, table cells, metadataBerth H-14 is occupied until 18:00.
Body — 400 / 1.6 / 68ch measure

Set running text to roughly 65 characters and leave it alone. Flush left, ragged right, never justified — justification without hyphenation opens rivers, and Swiss setting is ragged by conviction rather than by compromise.

Weight stops at 600

Swiss hierarchy is built from size and space, not from weight. Three weights exist — regular, medium, semibold — and headings never go past the last of them. An 800-weight interface reads as a startup landing page, which is the opposite of what a tool should sound like.

--weight-regular · 400 · Body copy, table cells, hintsStormy clouds over the north channel
--weight-medium · 500 · Labels, buttons, the current nav itemStormy clouds over the north channel
--weight-semibold · 600 · Headings, card titles, stat valuesStormy clouds over the north channel

Where mono belongs

Mono plus caps plus tracking is a real device, and the fastest way to make a system look like a template is to use it on every label. It is restricted to data chrome — table headers, menu group labels, token names, code — and form labels are plain sentence-case sans at 13px instead.

ItemPinsChange
Jeanne Dielman1,284+112
Hats977−8
Pond412+1,001

Anything numeric that sits in a column gets font-variant-numeric: tabular-nums, so digits line up. Tables and [data-numeric] pick that up automatically from base.css.

Supplying the faces

A pairing references var(--font-inter) and friends; the consuming app supplies them. Here that is next/font, which also means no layout shift and no external request at runtime.

// apps/docs/app/fonts.ts
export const inter = Inter({
  subsets: ["latin"],
  variable: "--font-inter",   // <- the contract with tokens.json
  display: "swap",
});

// then put the variable classes on <html>
<html className={fontVariables}>