Typography, from the ground up
Typography means arranging text on a page: size, weight, spacing and line length. Choosing the typeface is only one of those decisions and in practice the least consequential; a page with an ordinary typeface and a correct scale always reads better than a page with a beautiful typeface and a sloppy one.
- Lesson 4 of 12
- Beginner
- Free, no signup
The rungs of a scale, from small text to the page heading
-
1
Small text, 0.85rem
Breadcrumbs and figure notes. Anything that gets read but costs nothing when it is not.
-
2
Body text, 1.0625rem
The rung the others are derived from. Get this right first, then everything else.
-
3
Subheading, 1.15 to 1.4rem
The smallest difference still read as a heading. Any less and it does not look like one.
-
4
Section heading, 1.5 to 2.2rem
The rung that gives a long page its structure. Its space above must exceed its space below.
-
5
Page heading, 2 to 3.3rem
One per page. If you have two, the problem is not the scale but the structure of the page.
The numbers are from this site theme, not a standard; what transfers is the constant ratio, not the values themselves. The headings are written with clamp, so each is a range rather than one number.
Last checked: Facts and tool names in this lesson are re-checked against their sources on this date.
Typography is arranging text, not picking a font
The first thing to put aside is the idea that the job is finished once you have found a good typeface. Typography is the set of decisions taken after the typeface is chosen, and in practice there are four of them: size, weight, spacing and line length.
The easiest way to see this is an experiment. Take a page you like and swap its typeface for an ordinary system font. The page still works. Now do the reverse: keep the typeface, make every size identical and strip out the spacing. The page falls apart. What was holding it together was never the typeface.
Our position, after years of Persian site work, is blunter still: on most projects the list of usable typefaces is so short that the choice makes itself. For Persian on the web, the number of families that have a web licence, enough weights, and correct Persian numerals is not large enough to deserve a week of anyone time. That week belongs to the other four decisions.
One thing this lesson deliberately leaves out, because it has a lesson of its own: everything specific to the Persian script, from letter joining to the half-space and the numerals. That argument is opened in Persian typography, and without it this lesson is not complete for a Persian site.
What makes a typeface legible?
Legibility is not taste, it is a handful of measurable properties. The most important is x-height: the height of a lowercase letter without an extension, relative to the full letter height. Typefaces with a taller x-height read better at small sizes, which is why two faces set at the identical size can look nothing alike.
Three more worth knowing: ascenders and descenders, the parts of a letter that leave the baseline and that decide how open your line spacing must be; the counter, the enclosed space inside a letter, which fills in at small sizes if it is tight and makes the letter unreadable; and stroke contrast, the difference between thick and thin strokes, where more contrast means a better display face and a worse one for long text.
And a practical test that discards bad typefaces in twenty seconds: write the lookalike characters next to each other. In Latin, capital I, lowercase l and the digit one. If all three are a plain vertical stroke, that typeface is bad for an interface, because your user has to read a tracking code and a password in it.
In Persian the same test takes a different form and is stricter still: the dots. What separates beh, peh, teh and theh is nothing but dots, and in decorative typefaces those dots either shrink or fuse into the body of the letter. A page of headlines set that way is fine; a signup form set that way sells the client a support desk.
How do you build a size scale?
A scale is a short list of permitted sizes, and its rule is simple: every size in the design must be on that list. If a page contains eight different sizes, that page has no scale and each of the eight was decided separately.
The simplest way to build one is this: start from the body text size and derive the rest with a constant ratio. A smaller ratio gives a calmer scale suited to long text; a larger one makes headlines fuller and suits sales pages. The exact ratio does not matter much. That it stays constant does.
The numbers from the site you are reading, so you have seen a real one: the root is 16px, body text is 1.0625rem, about seventeen pixels, and small text such as breadcrumbs and figure notes is 0.85rem. The headings are all written with clamp(): h3 between 1.15 and 1.4rem, h2 between 1.5 and 2.2rem, and h1 between 2 and 3.3rem. The smallest number on a phone, the largest on a big display, with no breakpoint anywhere.
That clamp() is the most useful practical tool in this section and it replaces three or four media queries. It takes three values: a minimum, a fluid size usually based on viewport width, and a maximum. The common mistake is writing only the fluid part and leaving the minimum off; the result is a heading that shrinks to body size on a narrow phone.
And one rule that matters more than any ratio: reduce the number of sizes. Five sizes are enough for almost any site, and if you feel you need a sixth, the problem is usually not size but spacing.
Line length and line height: the two numbers with the most effect
If you keep only two numbers from this lesson, keep these. A very long line loses the eye on its way back to the start of the next one; a very short line turns reading into a series of jumps. And tight line spacing presses two lines together until the reader unconsciously reads one of them twice.
For line length there is a formal ceiling. WCAG criterion 1.4.8, which is level AAA rather than AA, says the width of a block of text should be no more than eighty characters. The same criterion says two other things that get quoted far less often: text should not be justified to both margins, and line spacing should be at least one and a half.
In CSS you set this with the ch unit rather than pixels, because the measure has to rise and fall with the font size. The numbers on this site: the intro paragraph of every page is 56ch and article bodies are 78ch, which is just under that eighty ceiling.
One point almost nobody writes down, and the one everyone gets wrong the first time: ch is the width of the digit zero in the current font, not the width of a real character. In the typeface this site uses, that zero is 570 units of a 1000-unit em, so 0.57em. So 78ch is a repeatable number but it is not "seventy eight letters", and in Persian, where letter widths vary a great deal, the gap widens further. Treat the number as an adjustable measure, not as a count.
For line spacing, WCAG criterion 1.4.12, which is level AA, says a user must be able to raise line height to at least 1.5 times the font size without losing content. That is a floor, not a recommendation. Body text on this site sits at 1.9, and that choice is for Persian: the script carries many dots and descenders below the baseline, and at spacing that suffices for Latin it looks crowded.
Do you need two typefaces or one?
The honest answer for most sites: one. A single family with several weights nearly always beats two different families, because a weight difference is a designed difference and a family difference is an accidental one.
If you genuinely want two, the rule is that they must differ by category and not by mood. A sans for headings and a serif for body is a difference the eye grasps at once. Two sans faces that differ only slightly are the worst possible case: the reader cannot tell whether the difference means something or is a mistake.
And the cost that usually goes unseen in the decision: every extra family is more files to download. For Persian that cost is heavier, because a Persian typeface carries more glyphs and its file is larger than an equivalent Latin one. A second typeface that appears only in headings can cost as much as several images on a mobile connection.
Weights follow the same logic. Three are usually enough: one for text, one for emphasis inside text, and one for headings. If a design has six weights, three of them probably never catch anyone eye and are only downloaded.
How do you know the hierarchy works?
Hierarchy means the reader can tell what matters most without reading. The test takes ten seconds: squint until the page blurs, or look at it from two metres away. Whatever is still visible in that blur is what lands first in the reader mind. If what you see is not what you intended, the hierarchy is not working, however clean the design looks.
You have four levers for building it: size, weight, spacing and colour. The first three nearly always work better than the fourth, and among those three, spacing is the cheapest and the least used. When something is not prominent enough, everyone first reaction is to make it bigger or darker; usually it is enough to empty the space around it.
Colour is the lever to reach for last. The reason is simple: some of your readers do not see colour the way you do, and if the only signal that an element matters is its colour, then for them that element is no different from the rest. Colour should be the third emphasis, never the only one.
One common mistake, visible in headings more than anywhere else: the space above a heading must be larger than the space below it. A heading equidistant from the paragraph above and the one below belongs to neither, and this one small adjustment raises the structural legibility of a long page more than any other change.
The four levers of hierarchy, in the order you should reach for them
The order is not arbitrary: the further up this list, the more readers the lever works for.
-
Spacing
The cheapest and the least used. Emptying the space around an element usually replaces enlarging it.
-
Size
The most obvious lever and the first one to be overused. Do not step outside the scale.
-
Weight
Best for emphasis inside a paragraph, because it does not change size and does not break the line.
-
Colour
The last lever. For a reader who sees colour differently, being the only cue means being no cue at all.
This order is for text. In a chart or a map colour rises higher, provided it is not the only cue.
The fast path, with AI
The fast path here is not the one you expect. The ordinary request to a model is "suggest a good font pairing", and the answer is almost always useless, because the model names typefaces that do not exist for Persian or lack the weights. The professional move is the reverse: you hand the model your own real text and container width and ask for a scale that <em>removes</em> sizes rather than adding them.
- Pull every font size actually in use out of the current page. In the browser developer tools, the Computed tab on any element tells you exactly that, with no need to read the code.
- Feed that list, with a real sample of your own heading and paragraph, into the recipe below. A fast, cheap model of the Gemini Flash class is enough; this is arithmetic and sorting, not judgement. Our current pick among models is kept in the <a class="text-link" href="/ai/">AI section</a>.
- From the output, read the "remove" column first, not the new scale. If the model proposed no size for removal, either your page was clean already or the recipe was not given properly.
- Apply the new scale to the longest page on the site, not the home page. The home page is usually short and any scale looks fine on it; a page of three thousand words shows the faults.
Copy-ready recipe
The font sizes currently in use on my page:
{list of sizes, e.g. 13px, 14px, 15px, 17px, 20px, 22px, 28px, 34px}
Text container width: {number} pixels
Language of the text: {Persian or another language}
A sample heading: {a real heading}
A sample paragraph: {two or three real sentences}
1. Give me a table with three columns: current size, the role you think it plays, and "keep" or "remove". The target is at most five sizes.
2. For the sizes that stay, propose a scale with a constant ratio and state the ratio explicitly. Write everything in rem, with a 16 pixel root.
3. For each heading write a clamp() with all three values: minimum, a fluid value in vw, and maximum.
4. Compute the measure in ch and tell me roughly how many ch each line becomes at the container width I gave. If it goes over eighty, say so plainly.
5. Write the line height you propose for body text and say why you chose that number for this language.
Do not suggest any font names. Do not add a size that is not in my list unless you say which two sizes it replaces.
Before you trust the output: Three boundaries. First, the model does not see your page and holds only a list of numbers; the role it guesses for each size may be wrong, and you must read the second column before the third. Second, the ch number it gives should be checked once by you on the real page, because only the real font knows how wide its digit zero is. And third, the thing this lesson opened with: no scale can fix a page whose structure is wrong. If one page carries two main headings, the problem is not typography.
AI in this kind of work
Our position on typography and AI: excellent for tidying what you already have, useless and sometimes dangerous for suggesting typefaces. Models are good at laying out a scale, writing clamp, finding duplicate sizes and translating a design into CSS. But when they name a typeface they are speaking from data that is almost entirely Latin, and for Persian it slips out of their hands at that exact moment.
Tools that actually help
- Claude The best fit for the recipe above, because its output comes out as usable CSS rather than commentary: the scale as variables, clamp with all three values, and duplicate sizes flagged. Iran is on neither of Anthropic two supported-countries lists, which we read on Anthropic own page rather than by measuring it.
- Gemini Better when you have a screenshot instead of a list of numbers: it reads the image and tells you which two elements are so close in size that the difference does not register. Google own page says the Gemini web app is available in more than two hundred and thirty countries and territories, and Iran is not on that list.
- Google Fonts Not AI, but the answer models should give and do not: the list of families that genuinely cover the Arabic script, filtered by subset, each with its licence on its own page. When a model names a typeface, check here first whether that typeface has Persian at all.
Where it backfires
The specific and repeatable risk here is that models suggest typefaces which do not exist for Persian. A model trained mostly on English data, asked for "a good font pairing for a modern site", returns familiar Latin names; put those on a Persian site and the browser falls back to a substitute for the Persian letters, rendering the page in two different typefaces without you knowing. Before accepting any name, check that typeface in the Arabic-subset list on Google Fonts or on the foundry own site.
The second risk is licensing. A model will not tell you whether the typeface it suggested carries a web licence, and on a client project that difference is financial and legal. Each typeface licence is written on its own page and reading it takes thirty seconds.
And a third risk that is not about typography but is always present when working with models: any image or design file you put in a chat has left your machine, and its fate depends on the plan and settings of that service, written on its own data-usage page. To see how each of these tools can be paid for from Iran, see the buying guide.
Sources: Google Fonts: families covering the Arabic script Anthropic: supported countries Google: where the Gemini web app is available
Where this advice stops
Everything in this lesson concerns text that is read on a screen and sits in one column. For print the numbers change, because the unit is the point rather than the pixel and the reading distance is more constant. For multi-column text, line length takes on another meaning and the eighty character ceiling arrives far sooner. And a larger boundary worth stating plainly: this is general typography and it is not sufficient for a Persian site. Letter joining, the half-space, Persian numerals and font subsetting are none of them solved here, and the next lesson in this path is devoted to them.
From our own work
This site holds five weights of its typeface in the theme folder and loads only three: 400, 700 and 800. Weights 500 and 600 are dropped on purpose, and the drop is not uncompensated: the CSS font matching rule resolves weight 500 to 400 and weight 600 to 700, and since both 700 and 800 exist the browser is never forced to synthesise a bold. The saving is one 25 KB download on every page, and two on pages that used weight 500.
And the choice that is worth more: this site @font-face block carries font-display: optional rather than swap. One word apart, and the outcomes differ: with swap the browser shows a fallback first and replaces the text when the real font arrives, and the layout shifts at that moment. With optional, if the font does not arrive in time the browser keeps the fallback for that visit and never switches mid-page. Layout shift from a font swap becomes deterministically zero, at the price of a first visit that may be seen in a system font. For a site that measures its speed with Core Web Vitals, we have taken that trade.
Real follow-up questions
What size should body text be?
For text meant to be read at length, do not go below sixteen pixels, and if the text is Persian a pixel or two more usually works better. The exact number depends on the typeface rather than on a rule, because two faces at the identical size have different x-heights; the only sound method is looking at real text on a real phone.
Should I justify text to both margins?
Not on the web. The same WCAG criterion 1.4.8 that sets the line length ceiling says plainly that text should not be justified; the reason is the rivers of white space that open between words and pull the eye off the line. In print, where you have more control over line breaking and hyphenation, the story is different.
Should I self-host the font or load it from Google Fonts?
Self-host. One fewer domain means one fewer connection and one fewer point of failure, plus full control over font-display and subsetting. For a visitor in Iran a practical reason is added: every resource coming from a foreign domain is a dependency that may one day be slow or unreachable while you can do nothing about it.