Skip to content

Typography

Karthik P edited this page Oct 24, 2026 · 1 revision

This page sets the typefaces, sizes and text styles of the website, and how numbers are set. Use it when styling text; the values come from Design Tokens, and how to write times, dates and durations is on Writing Style.

Typefaces

One family for all text, one for code.

Instrument Sans JetBrains Mono
Used for Body, headings, navigation, buttons, the wordmark Code blocks, inline code, terminal commands
Licence SIL OFL 1.1, © 2022 The Instrument Sans Project Authors SIL OFL 1.1, © 2020 The JetBrains Mono Project Authors
Variable axes Weight 400–700, width 75–100 Weight 100–800
Weights used 400, 500, 600 400, 500
Width used 100 (normal); narrower only after a mockup is approved —
Specimen Google Fonts Google Fonts

Axes are from Next.js's Google font data (next 16.4.3). The Open Font License allows using, embedding and redistributing both fonts with the licence, commercial use included; they may not be sold on their own.

Loading

Fonts load through next/font/google, which downloads them at build time and serves them from the site: "No requests are sent to Google by the browser" (Next.js: Font).

// website/app/layout.tsx
import { Instrument_Sans, JetBrains_Mono } from 'next/font/google';
import type { ReactNode } from 'react';
import { Provider } from '@/components/provider';
import './global.css';

const sans = Instrument_Sans({
  subsets: ['latin'],
  display: 'swap',
  variable: '--font-instrument-sans',
});

const mono = JetBrains_Mono({
  subsets: ['latin'],
  display: 'swap',
  variable: '--font-jetbrains-mono',
});

export default function RootLayout({ children }: { children: ReactNode }) {
  return (
    <html lang="en" className={`${sans.variable} ${mono.variable}`} suppressHydrationWarning>
      <body className="flex min-h-screen flex-col font-sans">
        <Provider>{children}</Provider>
      </body>
    </html>
  );
}

Provider (the RootProvider with search, theme={{ hotKey: false }} and the icon stroke) is on Website Tech Stack.

Option Value Why
subsets ['latin'] Covers English, the en dash, the minus sign and ×. Without a subset, Next.js warns when preload is on.
display 'swap' Text shows at once in the fallback, then swaps. Also the default; written out so nobody changes it.
variable --font-instrument-sans, --font-jetbrains-mono Mapped to Tailwind's --font-sans and --font-mono in @theme inline (Design Tokens).
weight, axes not set Variable fonts: one file covers every weight. Add axes: ['wdth'] only if a narrower width is approved.
adjustFontFallback default true Sizes a local fallback to match, reducing layout shift.

Social cards drawn with next/og need a TTF instead; see Website Tech Stack.

Glyphs the fonts lack

Checked against the font files on 2026-10-24.

Character Where In Instrument Sans? Rule
µ (U+00B5) "8.5 µs" No; drawn from the fallback Accept it. JetBrains Mono has it.
½ (U+00BD) README "5½-hour span" No On the site, write "5.5-hour".
→ (U+2192) Arrows in UI text In the font, not in the latin subset Use a lucide-react icon, or write "to".
− (U+2212), – (U+2013), × (U+00D7), no-break space (U+00A0) Negatives, ranges, speed-ups, "8.5 µs" Yes Use freely. Not the narrow no-break space (U+202F), which the font lacks.

Type scale

Sizes in px, rem at 16 px. Each role is a Tailwind utility defined on Design Tokens that sets size, line height, and for headings weight and tracking.

Role Utility Size Line height Weight Tracking Used for
Caption text-caption 12 1.5 400 0 Captions, table notes, badges
Code text-code 13 1.6 400 0 Code blocks
UI text-ui 14 1.5 400; 500 when active 0 Sidebar, TOC, tabs, buttons, inputs, tables
Body text-body 16 1.7 400 0 Docs prose
Lead text-lead 18 1.6 400 0 Proposed: page description, landing intro
H3 text-h3 20 1.3 600 −0.01em Third-level headings
H2 text-h2 24 1.25 600 −0.02em Second-level headings
Section text-section 30 1.25 600 −0.02em Proposed: landing section headings; docs H1 below 640 px
H1 text-h1 38 1.1 600 −0.03em Page title
Display, phone text-display-sm 38 1.1 600 −0.035em Landing headline below 768 px
Display, tablet text-display-md 48 1.1 600 −0.035em Proposed: landing headline 768–1023 px
Display text-display 60 1.1 (Proposed) 600 −0.035em Landing headline from 1024 px

Proposed: H4, when needed, is Body size, weight 600, line height 1.5.

Text styles

Style Rules
Headings 600, text, sentence case, tracking as in the scale. Proposed: space above a heading twice the space below.
Body 16 px / 1.7, text, 400. Bold is 600. Links: Color. Proposed: 16 px between paragraphs.
Code blocks JetBrains Mono 13 px / 1.6 on surface, 16 px side padding. Keep sample lines to 77 characters or fewer: that fills the 640 px column (608 px after padding, 7.8 px per character at 13 px). Longer lines scroll inside the block.
Inline code Proposed: JetBrains Mono at 0.875 em, surface background, 4 px radius, 2 px side padding. Never in headings.
UI 14 px / 1.5. Navigation and inactive tabs text-2; active items text, 500. Button labels 500.
Captions 12 px / 1.5, text-2 (text-3 only on bg and raised).

Fumadocs sizes its title and prose headings itself (the title is text-[1.75em] in fumadocs-ui 16.17.8). These un-layered rules bring them onto our scale:

/* website/app/global.css — typography */
#nd-page h1 {
  font-size: var(--text-h1);
  line-height: 1.1;
  letter-spacing: -0.03em;
  font-weight: 600;
}
.prose h2 {
  font-size: var(--text-h2);
  line-height: 1.25;
  letter-spacing: -0.02em;
  font-weight: 600;
}
.prose h3 {
  font-size: var(--text-h3);
  line-height: 1.3;
  letter-spacing: -0.01em;
  font-weight: 600;
}
.prose {
  font-size: var(--text-body);
  line-height: 1.7;
}
@media (max-width: 639.98px) {
  #nd-page h1 {
    font-size: var(--text-section);
    line-height: 1.25;
  }
}

table,
time,
.tabular {
  font-variant-numeric: tabular-nums;
}

Tabular figures

Instrument Sans has proportional digits by default ("1" is 391 units wide, "0" is 666), so columns of times would not line up. Its tnum feature gives equal-width digits, and the latin file Google serves keeps it (checked 2026-10-24).

Use tabular-nums on every time, date and duration, every benchmark figure, every numeric table column and the whole demo. Numeric columns are right-aligned with one unit per column. JetBrains Mono digits are already equal width.

Line length

Text Width Characters per line
Docs prose 640 px at 16 px About 85 (Instrument Sans averages 0.47 em per character on README text)
Code 640 px at 13 px, minus 32 px padding 77
Landing paragraphs Proposed: 600 px About 80

85 is above the 80 that WCAG 1.4.8 sets at AAA; the target is AA, so this is not a failure.

Case

No all-caps: no uppercase, no text-transform, no small caps, including sidebar group titles, table headers and buttons. Sentence case everywhere. The product name is always lowercase "workclock" (Brand).

Open items

  • Proposed roles and styles: Lead, Section, Display tablet size, Display line height, H4, heading and paragraph spacing, inline code, 600 px landing paragraphs.
  • Proposed: load Instrument Sans italic (style: ['normal', 'italic']) if docs pages use emphasis, after comparing font file sizes.

Clone this wiki locally