Every dev blog I read before building this one used the same font. Inter, or system-ui, or some geometric sans that looks great on a component library and says nothing about the person writing. Mine doesn't. Open any page here and you're reading Literata, a serif, on the headings and the body text both. That was not an accident and it took longer to land on than I expected.

the brief I gave myself
When I sat down to redo the look of this site (the whole thing went through more redesign rounds than I want to admit, tracked as V1 through V7.3 in my own notes if you want the embarrassing detail) the first pass was "field notes, moss and paper." Generic nature blog energy. My own reviewer, which is to say me a week later, rejected it for reading exactly like every other blog template out there. The idea that survived was "the journal, loud": something that reads like an actual notebook someone keeps, not a content-marketing landing page with a blog bolted on.
A sans-serif UI face doesn't get you there on its own. Sans is what you reach for by default because every design system ships with one, and because it renders clean at small UI sizes. But a paragraph of body text set in a geometric sans reads like software, not writing. Serifs carry that old newspaper-and-book association whether you want them to or not, and since this site is mostly me writing about what I built and what went wrong, I wanted it to read like something written, not like a dashboard.
why Literata specifically
I didn't want a display serif that only works at headline size and falls apart at 17px. Literata is built for exactly this, it started life as a Google project for on-screen reading at book length, and it comes as a variable font, weights 400 through 900, roman and italic both, in one file per style. That matters because a heavy weight for h1 and a light one for body copy come from the same face instead of two different fonts pretending to match.
--font-display: 'Literata', ui-serif, Georgia, serif;
--font-body: 'Literata', ui-serif, Georgia, serif;
--font-mono: 'IBM Plex Mono', ui-monospace, monospace;Both display and body point at the same family. Headings and paragraphs are the same typeface at different weights, which is part of what makes the whole page read as one voice instead of a headline font doing a performance over generic body text underneath it.
the CDN mistake
First pass at wiring this up, I did what every tutorial tells you to do: link tag straight to Google Fonts, done in five minutes.
<!-- first pass -->
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Literata:wght@400..900&display=swap"
/>It worked, in the sense that the font showed up. What I didn't think about until later is that this means every visitor's browser makes a second network round trip to Google before the real text can render, and if that request is slow or blocked, the fallback stack kicks in and then swaps out from under you, which is the flash of unstyled text everyone complains about but nobody names right (it's not "unstyled," it's "styled in the wrong font for a second").
The fix was vendoring it. I pulled the actual woff2 files from the Google Fonts CSS2 API once, by hand, and committed them under static/fonts/, subset down to latin plus latin-ext so I wasn't shipping Cyrillic and Greek glyph tables nobody on this site needs:
@font-face {
font-family: 'Literata';
font-style: normal;
font-weight: 400 900;
font-display: swap;
src: url('/fonts/literata-normal-latin.woff2') format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304,
U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}Four of these blocks total, normal and italic, latin and latin-ext, each with its own unicode-range so a browser only downloads the subset it actually needs to render the page in front of it. No npm package, no font CDN at runtime, nothing pointed at a Google server anymore. IBM Plex Mono, the code-block font, still comes off the Google Fonts CDN because it's a much smaller ask (one weight pair, used for code, not the whole page) and I haven't gotten around to vendoring it too. Consistency would say I should. I haven't yet.
reading measure, the thing nobody notices until it's wrong
There's a CSS class in the layout called .measure:
.measure {
max-width: 68ch;
}ch is a unit that means "roughly the width of the character 0 in the current font." Cap body text at 68 of those and you're capping line length at somewhere around 65-75 characters depending on the letterforms, which is the range typography people have been citing for decades as the sweet spot before your eye starts losing its place jumping back to the start of the next line. Full-width text on a wide monitor with no measure control is genuinely uncomfortable to read for more than a paragraph, and almost no blog template bothers enforcing it, they just let the content column stretch to whatever the grid gives it.
I didn't know this term before I started paying attention to why some blogs feel easy to read and others feel like work even when the writing itself is fine. It's not the words, it's the line length.
the actual reason, underneath all of that
All the technical justification is true but it's also a little bit after the fact. The real reason is I wanted this thing to feel like mine and not like a theme I installed. A guy on Lovund writing about Django scripts and half marathons in the same typeface Random House uses for actual books felt like the right amount of mismatch. Most dev blogs are sans because "clean" and "technical" got equated somewhere along the way and nobody questioned it since. Mine doesn't have to look like a SaaS landing page. It's not one.
the scale isn't flat either
Once the typeface was settled I still had to decide how loud each size got to be relative to the others. The type scale here runs at roughly a 1.25 ratio between steps, an h3 at 1.3125rem, h2 at 1.6875rem, up through h1 at 2.125rem, with body text sitting at 1.0625rem and a line-height of 1.65 (that number matters almost as much as the font choice, too tight and a serif at body size gets visually noisy, too loose and paragraphs start feeling disconnected from each other). Post titles specifically don't follow that fixed step ladder at all, they scale with clamp() between 2.25rem and 3.5rem depending on viewport width, since a post title is the one place on a page where I wanted a genuinely loud display moment rather than another rung on a predictable scale. h1 carries the heaviest weight on the page, 800, while h2 through h4 all sit at 700, so there's exactly one "shout" in the whole hierarchy and everything else stays a step quieter than it, on purpose.