Skip to content

Typography

Every font is a self-hosted variable font from Fontsource. Nothing is requested from Google Fonts, so there is no third-party request and no cookie banner question.

Family Used by Width axis
Archivo Signal, Mono (display and body) 62% to 125%
Instrument Sans Cobalt (display and body), Forest (body) 75% to 100%
Bricolage Grotesque Forest (display) 75% to 100%
JetBrains Mono All presets (code, meta labels) none

Each preset sets three families as CSS variables, which Tailwind exposes as classes:

Variable Tailwind Use
--font-display font-display Headings and display type
--font-body font-sans Body copy and UI
--font-mono font-mono Meta labels, code, numbers

Fonts a preset doesn’t use cost a few bytes of @font-face rules and are never downloaded.

Class Style Use
.type-display Display font, 800 weight, uppercase, wide Big numbers, labels in bento cells
.type-heading Display font, 700 weight, slightly wide Section headings
.type-meta Mono, small, uppercase, muted Eyebrow labels. At most one per three sections
.prose-swift Long-form rhythm: headings, lists, quotes, code Journal articles and legal pages

Combine them with the type scale: <h2 class="type-heading text-3xl">.

The theme’s signature is type that stretches. Archivo, Instrument Sans and Bricolage Grotesque all have a variable width axis, and the theme animates it:

  • Links and titles with .stretch widen when their .stretch-host parent is hovered or focused.
  • The footer wordmark opens from condensed to full width as it scrolls into view.
  • The 404 number follows the cursor.
<a href="/work" class="stretch-host">
<span class="stretch">View work</span>
</a>

The range comes from the preset: Archivo rests at 100% and opens to 125%; the narrower families rest condensed at 84% and open to 100%.

While a web font loads, the browser shows a local font. If the two have different metrics, text jumps when the web font arrives, which Lighthouse counts as layout shift (CLS).

src/styles/fallbacks.css defines a fallback for every family: Arial (or Courier New for mono) resized with size-adjust and ascent and descent overrides so it takes up the same space. Display type gets its own wider fallback, because headings are set on the width axis and no local font can imitate that. This is what keeps mobile CLS under 0.05 on every page.

  1. Install it from Fontsource, preferably the variable version:

    Terminal window
    npm install @fontsource-variable/inter
  2. Import it at the top of src/styles/global.css. Use wdth.css if the font has a width axis, otherwise index.css:

    src/styles/global.css
    @import '@fontsource-variable/inter/index.css';
  3. Add a stack in src/config/presets.ts and use it in a preset:

    src/config/presets.ts
    const inter =
    '"Inter Variable", "Inter Variable Fallback", ui-sans-serif, system-ui, sans-serif';
  4. Optional, but it keeps CLS at zero: generate a fallback with Capsize and paste it into fallbacks.css, named after your stack (Inter Variable Fallback).

    // npm install -D @capsizecss/core @capsizecss/metrics, then run with node
    import { createFontStack } from '@capsizecss/core';
    import inter from '@capsizecss/metrics/inter';
    import arial from '@capsizecss/metrics/arial';
    console.log(createFontStack([inter, arial]).fontFaces);

If the new font has no width axis, the stretch effect simply does nothing. Set the preset’s stretch values all to '100%' to make that explicit.

SwiftAgency is made byLessCodexLicence