Notes on Reading and Type
Good typography is invisible. You don’t notice it — you just keep reading. That is the whole goal of this little theme: get out of the way of the words.
Measure matters#
The single biggest lever for readability is the measure — the width of a line of text. Too wide and your eye loses the start of the next line; too narrow and the rhythm breaks. Somewhere around 60–75 characters is the sweet spot.
Here is roughly how a paragraph’s text flows within a constrained measure:
- The eye scans left to right.
- It returns on a short, predictable hop.
- Repeat, comfortably, for as long as you like.
A scale, not a guess#
Font sizes shouldn’t be picked at random. A modular scale builds every size from a single base and a fixed ratio \(r\), so the \(n\)-th step is just \(s_n = s_0 \cdot r^{\,n}\). Pick the golden ratio, \(r = \tfrac{1 + \sqrt{5}}{2} \approx 1.618\), and the sizes relate to one another the way the eye already expects:
\[ s_n = s_0 \cdot \left(\frac{1 + \sqrt{5}}{2}\right)^{n}, \qquad n \in \mathbb{Z}. \]So from a 19px body \(s_0\), one step up lands near \(s_1 \approx 30.7\,\text{px}\) for a heading, and one step down gives \(s_{-1} \approx 11.7\,\text{px}\) for fine print — all in proportion, nothing arbitrary.
A little code#
Code should feel at home here too. Inline like const x = 42, and in blocks:
// Resolve the visitor's preferred theme without a flash of the wrong one.
function resolveTheme() {
const stored = localStorage.getItem("theme");
if (stored) return stored;
return matchMedia("(prefers-color-scheme: dark)").matches
? "dark"
: "light";
}
def measure(text: str) -> int:
"""Characters per line — keep it human."""
return max(len(line) for line in text.splitlines())
What this is not#
This blog is deliberately small. There’s no sidebar of widgets, no related-posts carousel, no newsletter popup chasing you down the page.
| Has | Skips |
|---|---|
| Posts | Profiles |
| Dark + light | Trackers |
| An RSS feed | Pop-ups |
That’s the idea. Now, back to writing.