searched again 2026-05-06
Where does 1.7 come from?
Search for the right line-height for Arabic web text and the same figure comes back from a dozen agency posts and design blogs: 1.7, sometimes 1.8, occasionally phrased as one and a half times whatever you use for Latin. The posts agree with each other. None of them agrees with a source.
Following the citations is instructive. Most link to another post that gives the same number and no source. Several cite a report that does not exist. The rest offer the number as self-evident, which for a script whose vertical extent varies more between typefaces than Latin does is the one thing it cannot be.
That does not make 1.7 wrong. For a lot of Arabic faces at body size it is comfortably above the floor, which is why nobody has been badly served by it. It makes it unfalsifiable, and an unfalsifiable number is a number you cannot debug when your headings collide anyway.
CSS Inline Layout, half-leading
What actually collides is ink, not boxes
A line box is not the same shape as the ink inside it. The browser builds each line from the font metrics: the ascent above the baseline, the descent below it, and the gap between that sum and the line-height, which is split evenly above and below as half-leading. When line-height is smaller than the font content box, half-leading goes negative and the ink overflows its own line box in both directions.
This is why an Arabic paragraph can look fine and an Arabic heading can still collide at the same ratio. The paragraph is set at a size where the ascent and descent are small in absolute terms; the heading is not. And it is why the question has to be asked in ems of ink rather than in multiples of font size.
Arabic makes the gap between box and ink larger than Latin does, for a specific reason: marks. A final yeh carries two dots that hang nearly half an em under the baseline, and a hamza-bearing alef carries a mark that sits well above the plain alef. Neither is an outlier — both are ordinary letters in ordinary words.
- ascent
- How far the tallest ink on a line reaches above the baseline, as a fraction of the em. In Zain this varies between 0.716 for a plain alef and 1.024 for a hamza-bearing one.
- descent
- How far the deepest ink reaches below it. The dots under a final yeh, and the bowl of a final ain, are the usual candidates.
- size-adjust
- A multiplier applied to the font as a whole, including its metrics. A floor computed before it is wrong by exactly that factor.
fontTools 4.x, five minutes
How to compute the floor for your own text
The method is short enough to run once per typeface and forget. It needs the font file, the strings you ship, and a way to read glyph bounding boxes — fontTools is the usual one.
from fontTools.ttLib import TTFont
font = TTFont(path)
upem = font[head].unitsPerEm
glyf = font[glyf]
cmap = font.getBestCmap()
def extent(text):
lo, hi = 0, 0
for ch in text:
g = glyf[cmap[ord(ch)]]
if g.numberOfContours == 0: continue
lo = min(lo, g.yMin); hi = max(hi, g.yMax)
return lo / upem, hi / upem
# floor for one wrap = |descent of the line above| + ascent of the line below
floor = abs(extent(upper)[0]) + extent(lower)[1]
- List the strings that matter Not your whole content. The display strings — headings, impact lines, anything set large enough that a collision would be visible. Prose at body size is almost never the binding case.
- Enumerate the wraps For each string, every word boundary it could break at. A three-word heading has two. Each wrap gives you an upper line and a lower line.
- Measure the two extremes For the upper line, the deepest yMin across its glyphs. For the lower line, the tallest yMax. Both as fractions of unitsPerEm.
- Add them The floor for that wrap is the absolute descent of the upper line plus the ascent of the lower one. Below that number the two lines share pixels.
- Apply size-adjust Multiply by whatever size-adjust the face is declared with. The specification applies the adjustment to the font metrics, so the ink moves with it.
- Take the maximum, then add air The worst wrap across all your strings is the floor. Ship above it by a margin you choose deliberately rather than by a ratio you inherited.
Two details are easy to get wrong. Skip glyphs with no contours, or a space will drag your descent to zero and hide the collision. And measure the composed form — in Arabic the dots and the hamza are separate components assembled at render time, and a naive lookup that misses them will tell you a hamza-bearing alef is the same height as a plain one.
Zain, size-adjust 112% and 103%
The four strings that set the number on this site
This site publishes four Arabic display strings that can wrap. Running the measurement over all of them, at every word boundary each can break at, produced four floors and one decision.
| String | Where it appears | Computed floor |
|---|---|---|
| دائمًا | أستكشف الفكرة التالية | the footer statement | 1.3944 |
| مشاريع | مختارة | the projects heading | 1.2952 |
| خمس | دورات شهادة واحدة | the credential section | 1.2412 |
| لغات | & تقنيات | the stack grid | 1.1549 |
The shipped value is 1.45. It clears the worst of the four by 5.6 per cent of an em, which is enough that a font update with slightly different metrics does not immediately become a collision, and small enough that Arabic headings do not float apart from each other.
Note how far the four are from one another. The gap between the tightest string and the loosest is 0.24 em — more than the distance between 1.45 and any ratio in the advice. A number chosen for the average of your content is a number that is wrong for the extremes of it.
measured on the Zain outlines
Why the binding case is a hamza-bearing alef
The footer string wins by 0.10 em over the next worst, and it wins on one letter. Its second line begins with an alef carrying a hamza, and that mark is what changes the arithmetic. The two forms are set side by side below at the same size.
- 0.716 plain alef ا the ordinary vertical
- 1.024 hamza-bearing alef أ the mark clears the em box
- +43% the difference one diacritic, over the whole line
A display sentence that can put one of these on a second line is what sets the number. That is a content fact, not a typographic one — and it is why the measurement has to run over your strings rather than over the alphabet. A site whose Arabic headings happen never to break before a hamza-bearing alef has a genuinely lower floor, and is entitled to use it.
four roles, four values
One number cannot serve prose and display
The floor answers when ink collides. It does not answer what is comfortable to read, and those are different questions with different answers for different roles. This site ends up with four Arabic line-heights, and each one has its own reason.
Prose gets 1.8 because it is read in long lines and the return sweep needs room, not because anything collides at 1.5. Labels get 1.55 because they are small, uppercase-adjacent and sit inside a clip window that has to clear their descenders. Display gets the measured 1.45. And one section gets 1.25, for a reason worth its own paragraph.
The label line-height above is set by a clip window rather than by a collision, and that window has its own story — the flip that lands off the baseline.
measured at 130.5px, intended 112.5
The exception: a heading that cannot wrap
One section of this site stacks two one-word headings, each a single word over another single word. Neither can wrap, so the only collision available is the first element descenders against the second element ascenders — and both are known glyphs rather than a range of possibilities.
Measured over the Zain cuts those two elements select: a descent of 0.4262 em and an ascent of 0.7158 em, for a floor of 1.1420. The shipped value is 1.25, which clears it by 10.8 per cent of an em and takes the Arabic block from 391 pixels to 337 at 1440 against the Latin 247.
html[lang='ar'] .otot-home-text-w :is(h1, h2, h3) {
line-height: 1.25;
}
- الجانب / البرمجي
- The code half of the diptych. Descent 0.4262 em on the upper word, ascent 0.7158 em on the lower, for a floor of 1.1420.
- الجانب / التصميمي
- The design half. The upper word is the same, and the lower one is shorter in the vertical, so this pair is not the binding case.
It shipped in the section file first, and rendered at 130.5 pixels where 112.5 was intended, because the blanket rule for display headings in the locale sheet was in a later layer and won. That is how the rule was found, and it is the reason it now sits beside the number it is an exception to.
The same cascade-layer argument decides several other Arabic corrections on this site — see the seven right-to-left bugs logical properties cannot fix.
a checklist, not a ratio
What to do on your own site
If you take one thing from the arithmetic above, take the shape of the question rather than any of the numbers. Mine are Zain at two size-adjust values over four specific strings; yours will be a different face over different words, and the floor will land somewhere else.
- Measure before you choose
- Run the extents over your own display strings once. It takes minutes and it replaces a number nobody can defend with one you can.
- Do it after size-adjust
- If you are matching an Arabic face to a Latin one by size-adjust, the ink moves with the adjustment. Compute the floor at the shipped value, not at 100 per cent.
- Split display from prose
- They are read at different heights and they collide at different ones. Two values is not indecision, it is two answers to two questions.
- Watch the clip windows
- Any effect that clips a label — a roll, a mask, a fixed-height box — has its own relationship with the descenders, and opening the line box without moving the clip is how you cut the dots off a final yeh.
- Never letter-space
- Unrelated to line-height and worth saying in the same breath: tracking pulls a joined script apart. It is the one blanket override worth making site-wide.
The clip-window problem has its own piece, because it is where a wrong line-height turns into a wrong letter: sizing an Arabic typeface when x-height means nothing.
If you are choosing an Arabic face for a bilingual site, the metrics half of this question is the tooth-and-alef measurement.