Type something to search...
How to handle right-to-left typography?

How to handle right-to-left typography?

To handle right-to-left typography, declare the language and direction in HTML with lang and dir="rtl", build your layout with logical CSS properties such as margin-inline-start and text-align: start instead of left and right, and choose fonts designed for the script you are setting. Arabic, Hebrew, Persian and Urdu each have their own shaping, spacing and size needs, so they need a dedicated font rather than a Latin font's fallback. You also need to manage bidirectional text, where right-to-left words mix with left-to-right content such as numbers, brand names and URLs. Finally, mirror the parts of the interface that imply direction, like arrows and progress indicators, and leave the rest alone.

Hundreds of millions of people read right-to-left scripts every day, and a site that simply flips its CSS often ends up with broken punctuation, cramped Arabic text and icons pointing the wrong way. In this article you'll learn how the browser decides text direction, how to write direction-agnostic CSS, how to handle mixed-direction text safely, which fonts and spacing settings suit Arabic and Hebrew, and how to test the result.

How Browsers Decide Text Direction

Every block of text has a base direction, which controls where lines start, which side the text aligns to, and the order of inline content such as table columns and flex items. The browser takes that base direction from the dir attribute, inherited from the nearest ancestor that sets it.

Inside a line, the Unicode Bidirectional Algorithm (often called bidi) decides the visual order of characters. Each character has a direction type. Hebrew and Arabic letters are strongly right-to-left, Latin letters are strongly left-to-right, and digits, spaces and punctuation are weak or neutral, taking their direction from what surrounds them. This is why a sentence of Arabic with an English product name in the middle still reads correctly without any extra markup.

The algorithm works well for most text, but it can only guess when neutral characters sit between runs of different directions. A full stop, a bracket or a phone number at the edge of a run can end up on the wrong side. The fixes are mostly in HTML, not CSS.

Set Direction in HTML, Not CSS

Always set direction with the dir attribute, not with the CSS direction property. Direction is part of the meaning of the content, so it belongs in the markup. Screen readers, search engines, copy and paste, and pages with CSS disabled all rely on it.

<!doctype html>
<html lang="ar" dir="rtl">
  <head>
    <meta charset="utf-8" />
    <title>متجر الكتب</title>
  </head>
  <body>
    <h1>مرحبًا بكم في متجرنا</h1>
    <p>نشحن الطلبات إلى جميع أنحاء العالم.</p>
  </body>
</html>

The lang attribute matters too. It tells the browser which font fallback, hyphenation dictionary and spell checker to use, and it lets assistive technology switch to the right voice. Arabic (ar), Persian (fa) and Urdu (ur) share a script but differ in letters, numerals and typographic conventions, so be precise.

Write Direction-Agnostic CSS With Logical Properties

The biggest source of RTL bugs is CSS that assumes text starts on the left. Physical properties like margin-left, padding-right, left and text-align: left don't change when direction does. Logical properties do, because they're defined relative to the start and end of the line.

Physical propertyLogical equivalent
margin-leftmargin-inline-start
padding-rightpadding-inline-end
border-leftborder-inline-start
leftinset-inline-start
text-align: lefttext-align: start
float: leftfloat: inline-start
border-top-left-radiusborder-start-start-radius

Logical properties are supported in all current browsers, so there's little reason to keep physical ones for anything that relates to text flow.

.callout {
  border-inline-start: 4px solid var(--accent);
  padding-inline: 1.25rem 1rem;
  margin-block: 1.5rem;
  text-align: start;
}

.callout__icon {
  margin-inline-end: 0.5rem;
}

.sidebar {
  position: absolute;
  inset-inline-start: 0;
  inset-block: 0;
  inline-size: 18rem;
}

With this CSS, the callout border sits on the left in English and on the right in Arabic, without a single direction-specific rule. Flexbox and grid already follow the writing direction, so a flex-direction: row container lays out its children from right to left in an RTL document.

When You Need Direction-Specific Styles

A few things genuinely differ between directions, such as an arrow icon or a background gradient. Use the :dir() pseudo-class, which matches an element's computed direction even when the attribute is set on an ancestor. It's supported in current versions of Chrome, Edge, Firefox and Safari.

.icon-next {
  transform: none;
}

.icon-next:dir(rtl) {
  transform: scaleX(-1);
}

If you need to support older browsers, the attribute selector [dir="rtl"] .icon-next works too, although it only matches when an ancestor has the attribute explicitly.

Handle Bidirectional Text Safely

Mixed-direction text is where right-to-left typography gets tricky. User names, product titles, search queries and comments can be in any script, and if you insert them into a sentence without isolation, punctuation and numbers can jump around.

Isolate Content of Unknown Direction

Wrap user-generated or dynamic text in a bdi element, or give its container dir="auto". Both isolate the text from its surroundings and let the browser determine its direction from the first strong character.

<p dir="rtl">علّق <bdi>Sarah_92</bdi>: 3 تعليقات جديدة</p>

<ul>
  <li><bdi>שלום עולם</bdi> - 12 views</li>
  <li><bdi>Hello world</bdi> - 8 views</li>
</ul>

<input type="text" name="q" dir="auto" />

Without bdi, a Latin user name followed by a colon and a number inside an Arabic sentence can be reordered so that the number appears next to the wrong word. With isolation, the name is treated as a single unit.

The CSS equivalent is unicode-bidi: isolate, but as with direction, prefer the HTML version so the meaning travels with the content.

Mark Left-to-Right Islands

Code samples, email addresses, URLs and file paths are always left-to-right, even inside Arabic or Hebrew text. Give them an explicit direction so their punctuation stays in place.

<p dir="rtl">
  أرسل بريدًا إلى <span dir="ltr">support@example.com</span> للمساعدة.
</p>

<pre dir="ltr"><code>npm install --save-dev stylelint</code></pre>

Form fields for phone numbers, card numbers and emails should usually be dir="ltr" as well, because that's how users expect to type and read them.

Numbers and Numerals

Digits are weakly left-to-right, so a number reads left to right even in a right-to-left sentence, which is correct. What changes is the digit shapes. Many Arabic-speaking regions use Western digits (0 to 9), while others prefer Arabic-Indic digits, and Persian uses its own Eastern Arabic-Indic set. Don't hard-code one. Use Intl.NumberFormat with the right locale:

const n = 1234567.89;

console.log(new Intl.NumberFormat("ar-EG").format(n));
console.log(
  new Intl.NumberFormat("ar-AE", { numberingSystem: "latn" }).format(n),
);
console.log(new Intl.NumberFormat("fa-IR").format(n));
console.log(new Intl.NumberFormat("he-IL").format(n));
١٬٢٣٤٬٥٦٧٫٨٩
1,234,567.89
۱٬۲۳۴٬۵۶۷٫۸۹
1,234,567.89

Hebrew uses Western digits in everyday text, so you rarely need to change anything there.

Choose Fonts Built for the Script

A Latin font with no Arabic glyphs will fall back to whatever the operating system provides, which may not match your design in weight, size or tone. Load a font designed for the script and put it in a deliberate stack.

Arabic, Persian and Urdu

Arabic script is cursive. Letters change shape depending on whether they sit at the start, middle or end of a word, or stand alone, and the font's shaping tables handle those connections. Some widely used, well-made options are:

  • Noto Naskh Arabic: A traditional Naskh style suited to long-form reading.
  • Noto Sans Arabic: A simpler, lower-contrast design for interfaces.
  • IBM Plex Sans Arabic: Designed to pair with IBM Plex Sans, useful for bilingual products.
  • Vazirmatn: A popular open-source font for Persian, with good Arabic coverage too.
  • Noto Nastaliq Urdu: Urdu is traditionally set in the Nastaliq style, which Naskh fonts don't reproduce.

Hebrew

Hebrew letters don't connect, so the shaping is simpler, but the letters have a different rhythm and many sit on the baseline with few descenders. Noto Sans Hebrew, Heebo, Assistant, Rubik and Frank Ruhl Libre (a serif) are all open-source and cover Hebrew well. Check whether you need vowel points (niqqud) and cantillation marks, because not every font includes them.

Build a Font Stack Per Language

Use :lang() to switch font stacks per language, so a bilingual page gets the right font for each passage.

:root {
  --font-latin: "Inter", system-ui, sans-serif;
  --font-arabic:
    "Noto Sans Arabic", "Segoe UI", "Geeza Pro", Tahoma, sans-serif;
  --font-hebrew: "Heebo", "Segoe UI", "Arial Hebrew", Arial, sans-serif;
}

body {
  font-family: var(--font-latin);
}

:lang(ar),
:lang(fa) {
  font-family: var(--font-arabic);
}

:lang(he) {
  font-family: var(--font-hebrew);
}

If you self-host, subset by script and use unicode-range so Latin-only pages don't download Arabic glyphs.

Adjust Size, Spacing and Line Height

Arabic and Hebrew fonts often look smaller than Latin fonts at the same font-size, because their main letter bodies are shorter relative to the em square. Arabic also has tall ascenders, deep descenders and diacritics stacked above and below the letters, which need room.

  • Increase line height for Arabic: A value around 1.7 to 1.9 for body text keeps diacritics on adjacent lines from colliding. Latin text that's comfortable at 1.5 often looks cramped in Arabic.
  • Slightly enlarge the font size: Many bilingual sites set Arabic body text about 10 to 15 per cent larger than Latin so the two appear similar in size. Judge by eye with your chosen fonts.
  • Never add letter spacing to Arabic: Positive letter-spacing pulls connected letters apart and breaks the joins. Browsers try to suppress letter spacing for cursive scripts, but behaviour varies, so reset it explicitly.
  • Avoid synthetic italics: Arabic and Hebrew don't have a tradition of italic type. A slanted Arabic word looks broken, not emphasised. Use weight or colour for emphasis instead.
:lang(ar) {
  font-size: 1.125em;
  line-height: 1.8;
  letter-spacing: 0;
  word-spacing: 0.05em;
}

:lang(ar) em,
:lang(he) em {
  font-style: normal;
  font-weight: 700;
}

:lang(ar) .eyebrow {
  text-transform: none;
  letter-spacing: 0;
}

The last rule matters for design systems. Small uppercase labels with wide tracking are common in Latin interfaces, but Arabic and Hebrew have no case, so text-transform does nothing and the tracking does harm.

Justification

Justified Latin text stretches the spaces between words. Traditional Arabic typesetting can also lengthen connecting strokes, a technique called kashida or tatweel. Browser support for kashida justification is limited and inconsistent, so justified Arabic on the web tends to rely on word spacing alone, which can leave gaps. Left-aligned text in Latin becomes start-aligned text in RTL, and text-align: start is usually the safest choice for body copy in both. See left-aligned vs justified text for the general trade-offs.

Mirror the Interface Carefully

When the reading direction flips, much of the layout should flip with it, but not everything.

Mirror These

  • Navigation and progress arrows: "Back" points right and "next" points left in RTL.
  • Layout order: Sidebars, breadcrumbs, step indicators and table columns follow the reading direction.
  • Icons with a direction of travel: Send, reply, undo and redo icons, and anything showing forward motion.
  • Text-related icons: Align-start, indent and list icons.

Don't Mirror These

  • Media controls: Play buttons and video timelines usually stay left-to-right because they represent tape direction, not reading direction.
  • Clocks and circular progress: Clockwise is clockwise in every language.
  • Logos and brand marks: Never flip a logo.
  • Images of real objects: A photo of a hand, a keyboard or a product shouldn't be mirrored.
  • Checkmarks and common symbols: These are universal shapes.
[data-mirror-rtl]:dir(rtl) {
  transform: scaleX(-1);
}
<button type="button">
  <svg
    data-mirror-rtl
    aria-hidden="true"
    width="20"
    height="20"
    viewBox="0 0 20 20"
  >
    <path d="M7 4l6 6-6 6" fill="none" stroke="currentColor" stroke-width="2" />
  </svg>
  <span>التالي</span>
</button>

An opt-in attribute like data-mirror-rtl keeps the decision explicit for each icon, which is easier to review than a global rule that flips everything.

Test Right-to-Left Layouts

Testing RTL properly needs real content, not reversed English. Lorem ipsum written in Arabic letters, or real copy from a translator, shows you line heights, diacritics and word lengths that dummy text hides.

  1. Toggle direction in DevTools: Change the dir attribute on the html element and scan for anything still anchored to the wrong side.
  2. Search your CSS for physical properties: A quick search for left, right, margin-left and padding-right finds most problems before you see them.
  3. Check mixed content: Test user names, numbers, emails and dates inside sentences, especially near punctuation.
  4. Test forms: Make sure placeholder text, validation messages and input direction behave sensibly.
  5. Ask a native reader: Fluent readers spot awkward line breaks, wrong numerals and poor font choices immediately.

You can also catch physical properties automatically. Stylelint has plugins that flag them, or you can use a simple search in CI:

grep -rnE "(margin|padding|border)-(left|right)|text-align:[[:space:]]*(left|right)" src/styles/
src/styles/card.css:14:  margin-left: 1rem;
src/styles/table.css:8:  text-align: left;

FAQ: Right-to-Left Typography

Use the dir attribute in HTML. Direction is part of the content's meaning, and assistive technology, search engines and copy and paste all rely on the markup. The CSS direction property is mainly for special cases where you can't change the HTML.

Usually not. If you write layout CSS with logical properties such as margin-inline-start and text-align: start, the same stylesheet works in both directions. You only need a small number of direction-specific rules, such as mirrored icons.

Punctuation is a neutral character, so the bidi algorithm takes its direction from the surrounding text. If a sentence ends with an English word or number, the full stop can attach to the wrong run. Make sure the container has dir="rtl" and isolate embedded left-to-right content with bdi or a span with its own dir.

No. Arabic letters connect to each other, and positive letter spacing breaks those connections. Reset letter-spacing to 0 for Arabic, Persian and Urdu, including in small labels and buttons that use tracking in your Latin design.

Arabic usually needs more line height than Latin text because of its tall ascenders, deep descenders and stacked diacritics. Somewhere around 1.7 to 1.9 for body text is a common starting point, but test with your chosen font and real content.

No. Mirror icons that show reading direction or movement, such as back and next arrows, but leave media controls, clocks, logos, checkmarks and images of real objects unchanged.


Conclusion

Handling right-to-left typography well comes down to treating direction as content, not decoration. Set lang and dir in the HTML, write your layout with logical properties so it flows naturally in both directions, and isolate any text whose direction you can't predict. That foundation removes most of the bugs that come from flipping a left-to-right design.

On top of that, give each script the typography it deserves: a font designed for it, generous line height for Arabic, no letter spacing on connected scripts, and emphasis that doesn't rely on italics. Mirror only the interface elements that imply direction, test with real copy, and get a native reader to review the result before you launch.

Share :

Related Posts

How to make typography accessible?

How to make typography accessible?

You make typography accessible by choosing clear typefaces, setting text in relative units so it scales with user preferences, giving it enough colou

Dive Deeper
What are the parts of a letterform?

What are the parts of a letterform?

A letterform is the shape of a single letter, and typographers break it down into named parts. The main ones are the stem (the main vertical

Dive Deeper
What are ascenders, descenders and the baseline?

What are ascenders, descenders and the baseline?

The baseline is the invisible line that letters sit on. Ascenders are the parts of lowercase letters that rise above the x-height, such as th

Dive Deeper