Chapter 03

Typography

Five typefaces, and not one of them is decoration. Each has a single job, and the discipline is in never letting one do another's work.

Built with

  • next/font
  • Fraunces
  • Synonym
  • JetBrains Mono

The cast

Five voices, each with exactly one job.

  • The headline

    Fraunces

    var(--font-display)

    Considered work, quietly shipped

    Section headings, project and company titles, every display moment.

  • The voice

    Synonym

    var(--font-sans)

    Body copy sits in Synonym: a quiet humanist sans that stays out of the way of the words.

    Body copy, navigation and buttons. The default for everything that is not a heading.

  • The lead

    Amulya

    var(--font-lead)

    A lead paragraph is a half-step warmer than body, and a little larger.

    The paragraph under a heading that says what the section is about.

  • The stamp

    Bespoke Stencil

    var(--font-hero)

    Sanjeev

    The hero name lockup, and nowhere else. Too distinctive for anything longer than a few words.

  • The label

    JetBrains Mono

    var(--font-mono)

    Software Engineer · 2026

    Eyebrows, dates, tech badges and any small piece of interface chrome.

Together

Anatomy of a section heading.

Three of the voices meet in almost every section: a mono eyebrow to name it, a serif heading to state it, and a lead to explain it. This chapter's own opening is built exactly that way.

What I do

Quiet work, done carefully.

A lead paragraph that says what the section is about in a sentence.

  1. EyebrowJetBrains Mono, uppercase, wide tracking
  2. HeadingFraunces, tight leading, a hair of negative tracking
  3. LeadAmulya at the lead size

Sizes

Every size has a name.

Each size is a fluid clamp() kept in one config. If a size does not exist yet, it is added there, not written inline in a component.

  • heroTitle

    clamp(2.75rem, 8.5vw, 6.5rem)

    The hero name lockup.

    Sanjeev

  • heroTitleCompact

    clamp(2.25rem, 5.4vw, 4.25rem)

    The hero name lockup beside the portrait and the at-a-glance strip.

    Sanjeev

  • sectionTitle

    clamp(2.75rem, 6.5vw, 4.5rem)

    Section headings.

    Seven chapters

  • subTitle

    clamp(1.5rem, 3vw, 2.25rem)

    Sub-headings inside a section.

    A project title

  • entryTitle

    clamp(1.2rem, 2.5vw, 1.5rem)

    Small entry headings in lists.

    A company name

  • contactTitle

    clamp(2rem, 4vw, 3rem)

    The conversational contact headline.

    Let us talk about it

  • lead

    clamp(1.125rem, 2.4vw, 1.375rem)

    Lead paragraphs.

    The line under a heading

  • heroLead

    clamp(1.125rem, 2.4vw, 1.5rem)

    The hero lead, one step above a standard lead.

    The passion statement

  • body

    1rem

    Everything else.

    Standard paragraph text

Under the hood

How the fonts load.

Two faces come from Google through next/font; three are self-hosted as woff2. Each exposes a CSS variable, display: swap keeps text visible while they arrive, and the stylesheet maps every variable to a role, with a system fallback behind it.

layout.tsx and globals.css
// src/app/layout.tsx — self-hosted faces use localFont, Google faces use next/fontconst synonym = localFont({  src: [    { path: "../fonts/synonym/Synonym-Regular.woff2", weight: "400", style: "normal" },    { path: "../fonts/synonym/Synonym-Medium.woff2", weight: "500", style: "normal" },  ],  variable: "--font-synonym",  display: "swap",}); const fraunces = Fraunces({  subsets: ["latin"],  variable: "--font-playfair-display",  display: "swap",}); // globals.css maps each variable to a role, inside @theme inline--font-sans: var(--font-synonym), ui-sans-serif, system-ui, sans-serif;--font-display: var(--font-playfair-display), ui-serif, Georgia, serif;--font-lead: var(--font-amulya), ui-sans-serif, system-ui, sans-serif;--font-hero: var(--font-bespoke-stencil), ui-sans-serif, system-ui, sans-serif;--font-mono: var(--font-jetbrains-mono), ui-monospace, monospace;