A bilingual site usually gets built once, in English, and then has Arabic poured into the same containers. The result is legible. It is rarely good, and the reasons are structural rather than aesthetic — Arabic script has different metrics, and a layout tuned for one will be wrong for the other in predictable ways.
There are no capitals, so there are no anchors
Latin readers navigate a page partly by capital letters. They mark the start of a sentence, a proper noun, a heading. Arabic has no case distinction at all.
Two consequences follow. First, techniques that lean on capitals stop working:
text-transform: uppercase does nothing to Arabic, and small-caps has no
meaning. An eyebrow label styled as uppercase-with-wide-tracking simply renders
as normal Arabic text at a small size, which reads as an afterthought rather
than a deliberate label.
Second, and more importantly, hierarchy has to come from somewhere else — weight, size, colour and space, rather than case. A design that separated a label from body text only by capitalisation has no hierarchy at all once translated.
Arabic needs more line height, not the same
Arabic letterforms carry marks above and below the baseline, and the script has
a taller effective ascender-descender range than Latin at the same nominal size.
Set both at line-height: 1.5 and the Arabic looks cramped — diacritics come
close to colliding with the line above.
A reasonable starting point is a noticeably looser leading for Arabic than for the same Latin size. The mechanism matters more than the exact number: apply it by language, not by a wrapper class someone has to remember.
:lang(ar) {
line-height: 1.85;
}
:lang(ar) h1,
:lang(ar) h2 {
line-height: 1.4;
}
:lang() matches on the document’s declared language, so it follows <html lang="ar"> without any component needing to know which locale it is in. That
detail is what keeps it from rotting: a new component inherits correct Arabic
leading for free, and nobody has to remember a modifier.
One caveat if you use a utility framework: those utilities are usually generated
into a cascade layer, and an unlayered rule beats a layered one regardless of
specificity. If your :lang() rules sit outside the layer, they will win over
leading-* utilities — which is what you want here, but it is worth knowing
rather than discovering.
Font size is not comparable across scripts
The same font-size produces visually smaller Arabic than Latin, because the
x-height equivalent sits differently relative to the em box. Arabic set at the
same nominal size as its English counterpart looks a step down in the hierarchy
even when it is the same heading.
You cannot fix this by picking a bigger number everywhere — that breaks the vertical rhythm. Set it per language, in the same place you set the leading, and verify by looking at the two side by side rather than by comparing the numbers.
Not every font has an Arabic counterpart
This is the constraint that most often derails a brand system. A brand picks a Latin display face, and there is no matching Arabic face — not merely no official one, but nothing with the same voice, weight range or proportions.
There are three honest options and one dishonest one.
The dishonest one is letting the browser substitute. If your font-family lists
only a Latin face, Arabic falls through to whatever the operating system
provides, which means your Arabic pages are typeset in a different font on
Windows, macOS, Android and iOS. You have not chosen a typeface; you have
declined to.
The honest options: pair the Latin face with an Arabic family designed as a companion; use a superfamily that ships both scripts; or accept a deliberate mismatch you have actually looked at and approved. All three are defensible. Substitution by accident is not.
Whatever you choose, declare it explicitly and per-language, so no page is ever typeset by a fallback nobody selected.
Numbers stay left-to-right, always
Digits, prices, phone numbers, product codes and dates run left-to-right inside
Arabic text. The bidirectional algorithm handles most of this, but it fails
around punctuation — a phone number with + and spaces, or a range like
2020–2024, can reorder in ways that change meaning.
Mark the run explicitly, on the smallest inline element that contains it:
<span dir="ltr">+963 99 20 444 50</span>
Do not put that dir on the containing block. dir sets alignment as well as
bidi context, so a block-level dir="ltr" inside an Arabic page will
left-align its contents while everything around it stays right-aligned. It looks
like a broken grid and the cause is one attribute one level too high.
Also decide whether you are using Western digits (0-9) or Eastern Arabic
numerals (٠-٩). Both are correct; mixing them within one interface is not.
Most contemporary web products in the region use Western digits, and consistency
matters more than the choice.
Justification behaves differently
Arabic traditionally justifies by extending the connecting strokes between
letters — kashida — rather than by stretching word spaces. Browsers do not do
this well. text-align: justify on Arabic produces uneven word gaps that are
more distracting than a ragged edge.
Leave it aligned to start. Ragged is fine; rivers of white space are not.
What to check before shipping
- Render every page in both languages at the same viewport and compare heading sizes optically, not numerically.
- Confirm no Arabic text falls back to a system font. Check computed
font-familyon an Arabic paragraph in a browser, not in the stylesheet. - Confirm
text-transform: uppercaseis not carrying hierarchy anywhere. - Check every number, price and date renders in the order it was written.
- Read one long paragraph in Arabic at body size. If your eye loses the line, the leading is too tight.
The underlying point
None of this is about translation quality. A perfectly translated page set in a substituted font, at Latin leading, with uppercase labels that do nothing, will still read as a second-class version of the English.
Treating Arabic as a first-class typographic system — its own leading, its own size, its own declared typeface — costs a handful of CSS rules if you do it at the start. It costs a redesign if you do it later.