HTML dir="rtl": the complete guide

Learn where dir="rtl" belongs, how direction inherits, when to use ltr or auto, and how to debug Arabic pages that still behave left to right.

Guide14 min read

An Arabic page can contain translated text and still behave as LTR because lang="ar" does not set direction. The opposite failure is just as common: CSS makes the page look RTL, but the document has no dir attribute, so components that depend on direction semantics disagree with what is painted.

For an Arabic page, the stable baseline is simple:

html
<html lang="ar" dir="rtl">

Put the base direction on the root, let descendants inherit it, and override direction only at boundaries whose content genuinely has another base direction. The difficult work is knowing where those boundaries are and not confusing page direction with the direction of a phone number, email address or unknown user string.

What the dir attribute controls#

The HTML dir attribute declares the base direction of an element's text and directional layout context. Its supported values are:

  • rtl for a known right-to-left base direction;
  • ltr for a known left-to-right base direction;
  • auto when the content decides its own direction.

Direction is inherited. A root dir="rtl" gives ordinary descendants an RTL base direction without repeating the attribute on every container. That base direction participates in the Unicode bidirectional algorithm, affects alignment defaults and changes the inline axis used by layout and logical CSS properties.

It does not translate content, choose a font, select a calendar, localize validation messages or reflect every image. Those are separate concerns. A page can have correct direction and still fail Arabic QA in each of those layers.

The HTML standard's directionality model treats direction as document semantics. That is why the attribute belongs in markup even when a stylesheet also contains direction-sensitive rules.

Put dir="rtl" on the html element#

For a document whose primary language and reading flow are Arabic, place dir="rtl" on <html>, not on a wrapper several levels below it.

html
<!doctype html>
<html lang="ar" dir="rtl">
  <head>
    <meta charset="utf-8">
    <title>لوحة الطلبات</title>
  </head>
  <body>
    <!-- Arabic application -->
  </body>
</html>

The root declaration covers content rendered outside the application's usual wrapper: skip links, dialogs mounted under body, error fallbacks, cookie notices and third-party surfaces that inherit document direction. A direction set only on #app leaves those siblings in the browser default.

R001 reports an Arabic page whose root direction is missing or LTR. R002 separately reports a missing, non-Arabic or contradictory root language. Treat both as release blockers for an Arabic route, but do not merge their jobs. lang="ar" identifies language; dir="rtl" establishes direction.

In Chromium 151, measured on 2026-09-17, a page with lang="ar" and no dir still laid out left to right and computed to direction: ltr. Firefox and Safari were not measured for the browser-specific observations in this guide, so verify them in the support matrix rather than assuming identical results.

Server-render the correct attributes on the first response whenever the locale is known. If client code adds dir after hydration, users can see an LTR first paint followed by a full-page rearrangement. It also makes screenshots, crawler output and failures before application startup disagree with the intended locale.

lang and dir solve different problems#

lang helps browsers and assistive technology identify the language. It also supports language-specific styling, spell checking and pronunciation behavior. dir establishes directional context. Neither substitutes for the other.

These are incomplete:

html
<!-- Arabic language, wrong default direction -->
<html lang="ar">

<!-- RTL direction, language remains unknown -->
<html dir="rtl">

Use both:

html
<html lang="ar" dir="rtl">

A language subrange can override lang without changing the whole page direction. For example, an English product title inside Arabic content may use lang="en" so its language is exposed correctly. Its directional behavior is a separate decision based on the content boundary.

Do not infer dir from the route name alone inside reusable components. The document or nearest ancestor already provides direction. Reading the actual directional context also handles an Arabic preview embedded in an English administration page and an LTR code example inside an Arabic guide.

HTML dir is not the same as CSS direction#

CSS has a direction property, and direction: rtl can make content render from an RTL base. It is still the wrong primary mechanism for document direction.

Fragile

html
<html lang="ar" class="arabic">
css
.arabic {
  direction: rtl;
}

Better

html
<html lang="ar" dir="rtl">

The difference is observable. In the measured Chromium version, CSS direction: rtl without a dir attribute displayed RTL, but the element did not match :dir(rtl). A component using that pseudo-class could therefore render its text RTL while selecting its LTR icon or animation.

The markup also survives missing styles and communicates intent to code that inspects attributes. Use CSS for presentation that responds to direction, not as a replacement for the document's directional metadata.

Avoid setting direction through a universal selector. It overrides legitimate nested boundaries and makes inheritance hard to debug:

css
/* Do not do this */
* {
  direction: rtl;
}

Set one base direction. Add local attributes where the content model justifies them.

How direction inheritance works in practice#

Most elements should have no local dir attribute. They inherit from the nearest ancestor, which keeps a page coherent and makes locale switching manageable.

html
<main dir="rtl">
  <section>
    <h2>تفاصيل الطلب</h2>
    <p>تم استلام الطلب</p>
  </section>
</main>

Both the heading and paragraph use the section's inherited RTL context. Repeating dir="rtl" on each node adds noise and can hide the real boundary when a local direction later changes.

Inspect direction in DevTools at the element that looks wrong. Check, in order:

  1. the nearest explicit dir attribute;
  2. the computed CSS direction value;
  3. an ancestor rule that overrides direction;
  4. whether the content is inside a nested LTR region;
  5. whether the visible problem is bidi ordering rather than base direction.

A component with correct inherited direction: rtl can still contain an unisolated order number that looks reversed. Changing the whole component to LTR repairs that one value by breaking the Arabic around it. Debug the smallest semantic boundary.

R020 reports a forced-LTR container whose content is majority Arabic. That pattern often comes from a local workaround that grew beyond the value it was meant to protect.

Use dir="ltr" for known LTR boundaries#

Phones, emails, URLs, source code, serial numbers and some account identifiers are read as LTR data even when their label and surrounding sentence are Arabic. Apply dir="ltr" to the value, not to the complete card or row.

Too broad

html
<div dir="ltr">
  <span>البريد الإلكتروني</span>
  <span>billing@example.com</span>
</div>

The Arabic label now has an LTR base direction, and the row lays out from the left like an English one.

Bounded to the data

html
<div>
  <span>البريد الإلكتروني</span>
  <span dir="ltr">billing@example.com</span>
</div>

This is safe when the value's direction is known in advance. Do not apply dir="ltr" to arbitrary user-generated text merely because one test user entered English.

Direction and alignment are related but not identical design decisions. An LTR phone value inside an RTL form can keep its character order while the field itself occupies the logical start or end defined by the form. Test cursor movement, selection, copy and paste instead of judging only the static alignment.

Use dir="auto" only for genuinely unknown content#

dir="auto" asks the browser to determine direction from the first strong directional character in the element's content. It is useful for user names, comments, messages and other strings that may begin in Arabic or Latin text.

html
<p dir="rtl">
  آخر تعديل بواسطة <bdi>Karim Mansour</bdi>
</p>

The <bdi> element isolates its contents and behaves as an auto-direction boundary. An ordinary element can also use dir="auto":

html
<p dir="auto">محتوى يحدده المستخدم</p>

The boundary matters. If an Arabic label and an unknown value sit inside the same auto-direction element, the label can become the first strong text and force the complete element RTL. Wrap only the value whose direction is unknown.

Digits and punctuation are another edge case. In Chromium 151, a value with no letters, such as the invoice number 3318-0457, resolved LTR under dir="auto" and <bdi>, as the HTML standard specifies when no strong character is found. That may be useful, but it does not mean every numeric grouping will display as the product expects. Arabic-Indic digit groups separated by spaces behaved differently and could reverse even inside <bdi> or dir="ltr".

Use auto because input direction is unknown, not because it sounds more adaptive. If a field is specifically an Arabic address, set RTL. If a field is specifically a phone number, keep it LTR. Known data types deserve explicit direction.

Isolate mixed-direction values inside Arabic text#

Base direction is not enough when a paragraph combines Arabic, Latin text, numbers and neutral punctuation. The Unicode Bidirectional Algorithm resolves those runs from their character types and context.

Consider an invoice number after an Arabic label:

html
<p dir="rtl">رقم الفاتورة 3318-0457</p>

In the measured Chromium version, it displayed as 0457-3318. The hyphen did not keep the numeric parts in authored order, and the root dir="rtl" on the page was not at fault: base direction was exactly right. A plain span around the number changes nothing, because it creates no directional boundary.

Isolate the semantic value:

html
<p dir="rtl">
  رقم الفاتورة <bdi>3318-0457</bdi>
</p>

When the value is defined as LTR, an explicit boundary is also suitable:

html
<p dir="rtl">
  رقم الفاتورة <span dir="ltr">3318-0457</span>
</p>

R021 reports unisolated phones, emails and Latin tokens inside Arabic text. R024 reports punctuation at the wrong visual end of Arabic text.

Do not validate a bidi fix by copying the value. The measured broken number copied in authored order even while displaying in the wrong order, and its accessibility-tree text also kept logical order. Compare the rendered value with an explicit expectation.

dir, bdi and bdo are not interchangeable#

These tools operate at different levels:

ToolPurposeAppropriate use
dir="rtl" or dir="ltr"Establish a known base direction for an elementDocuments, sections and values whose direction is defined
dir="auto"Derive base direction from the contentOne unknown string whose first strong character is meaningful
<bdi>Isolate unknown content from surrounding bidi contextUser names, comments and inserted values
<bdo dir="ltr"> or <bdo dir="rtl">Override the bidi algorithm with a forced ordering directionA fixed-format value whose required display order is known

<bdi> protects both sides of a boundary. That matters when an inserted user name begins with Latin characters or digits and sits next to an Arabic count. Applying dir="auto" to a large parent does not provide the same isolation between each inserted value and its neighbors.

<bdo> is much stronger. It tells the browser to lay its contents out in the specified direction rather than resolving their character types normally. Card numbers written in Arabic-Indic digits show why it exists. In Chromium 151, ٤٥٣٢ ٠١٥١ ١٢٨٣ ٠٣٦٦ displayed with its four groups in reverse order inside <bdi> and inside dir="ltr" alike, because spaces between Arabic-Indic digits resolve right to left whatever the boundary says. Only the override kept the groups in the order printed on the card:

html
<p dir="rtl">
  رقم البطاقة <bdo dir="ltr">٤٥٣٢ ٠١٥١ ١٢٨٣ ٠٣٦٦</bdo>
</p>

Use an override only when the display contract is fixed and tested. It is unsuitable for an unknown message, a paragraph or a value whose internal direction can vary. Forcing order can make content look stable in one fixture while corrupting another.

Also distinguish isolation from styling. unicode-bidi, direction and visual alignment can reproduce parts of this behavior in CSS, but semantic HTML boundaries stay attached to the content when styles fail or components move. Prefer the HTML element or dir attribute that describes the data.

Form controls need direction by data type#

An RTL form does not imply that every input value should be RTL.

html
<form lang="ar" dir="rtl">
  <label for="name">الاسم الكامل</label>
  <input id="name" name="name" dir="rtl" autocomplete="name">

  <label for="email">البريد الإلكتروني</label>
  <input id="email" name="email" type="email" dir="ltr" autocomplete="email">

  <label for="phone">رقم الجوال</label>
  <input id="phone" name="phone" type="tel" dir="ltr" autocomplete="tel">
</form>

In Chromium 151, type="tel" was LTR by default. Email, URL, number, search and text controls inherited the page direction unless they set their own. An Arabic search field can therefore inherit RTL, while an email or URL field usually needs an explicit LTR boundary.

dir="auto" on inputs has a visible empty-state edge case. The measured empty field resolved LTR, so an Arabic placeholder appeared at the left edge. Typing the first Arabic letter changed the field to RTL and moved alignment. Use auto-direction only when this content-driven change is the intended behavior.

R023 reports text inputs rendered LTR where Arabic input is expected. R070 reports telephone, email or URL inputs rendered RTL. R072 reports a select rendered LTR with Arabic options.

Test each field empty and populated. Type, paste, select, move the caret across punctuation, delete and submit invalid values. Direction that looks correct in a screenshot can still make editing confusing.

dir changes layout, but it does not mirror everything#

In an RTL container, the browser's inline axis starts on the right. In Chromium 151, the first item of a normal flex row, grid and table row appeared at the right edge. margin-inline-start mapped to a right margin.

That behavior removes much of the need for language-specific CSS when components use logical properties. A pull quote written this way needs no Arabic override at all, because its rule follows the dir it inherits:

css
.pull-quote {
  border-inline-start: 4px solid currentColor;
  padding-inline-start: 1rem;
  text-align: start;
}

Physical properties and graphics do not automatically mirror. left, margin-left, translateX() and SVG drawings keep their authored geometry. That is correct when the meaning is physical, and a problem when the relationship is logical start or end. R030 reports direction-sensitive physical properties without an RTL override. R033 reports a translateX that moves an element off-screen in RTL.

Do not add flex-direction: row-reverse just to make an RTL row start on the right. Direction already does that. In the measured browser, row-reverse inside an RTL page laid items out left to right in DOM order, and Tab moved left to right. R081 reports this attempt to fake RTL when tab order fights visual order.

dir also does not decide whether an arrow, photo, chart or media control should mirror. That decision comes from the object's meaning. Keep the direction problem at the HTML and layout layers instead of reflecting the complete page.

Local direction changes should be narrow and explainable#

Every explicit override should answer one question: what content inside this boundary has a different base direction from its parent?

Good candidates include:

  • a code block inside an Arabic guide;
  • a phone, URL, email or machine identifier;
  • an English quotation or product title;
  • unknown user-generated text wrapped in <bdi> or dir="auto";
  • an Arabic preview embedded in an English administration interface.

Weak candidates include “this component was hard to fix” or “the numbers looked strange.” Those reasons usually indicate that a value needs isolation or that physical CSS needs logical properties.

Keep language metadata aligned with the override where relevant:

html
<blockquote lang="en" dir="ltr">
  Payment could not be completed.
</blockquote>

The lang says how the content should be processed as language; the dir says how its base direction runs. An intentional foreign-language segment without language markup can also affect pronunciation. R080 reports those missing language boundaries.

Common fixes that solve the wrong problem#

Several shortcuts produce a convincing static screenshot while leaving the directional model broken:

ShortcutWhy it failsReplace it with
text-align: right on the pageMoves text but leaves base direction and inline layout LTRRoot dir="rtl" and text-align: start where needed
direction: rtl only in CSSOmits document semantics and can disagree with :dir()An HTML dir attribute
dir="rtl" only on an app wrapperLeaves body-level dialogs, fallbacks and siblings outside the boundaryRoot direction on <html>
dir="auto" on the full pageLets the first strong content decide a direction already known from localeExplicit root rtl or ltr
dir="rtl" on every elementHides real boundaries and overrides nested LTR valuesInheritance plus narrow exceptions
dir="ltr" on a complete card to fix one phoneGives Arabic labels the wrong base directionLTR on the phone value or field
row-reverse for every RTL flex rowReverses an axis already controlled by directionNormal row flow under the inherited dir
scaleX(-1) on the pageReflects glyphs, values, images and interaction geometryLogical layout and semantic icon variants

Right alignment is the most persistent confusion. Alignment chooses where a line sits in its box; direction tells the browser how to resolve bidi text and what inline start means. An Arabic paragraph can be right-aligned while its base direction remains LTR. Inspect both computed text-align and computed direction when diagnosing it.

Another warning sign is an override named after a symptom, such as .fix-arabic-numbers { direction: ltr; } around a large region. Name and scope the boundary after its data instead, such as an order ID or phone value. That keeps the exception valid when the surrounding copy or layout changes.

Change direction with locale, not with viewport#

Direction belongs to content and language, not screen size. Do not switch an Arabic page to LTR at a desktop breakpoint or use RTL only on mobile because a design happens to place navigation differently.

When a user changes locale without a full navigation, update both lang and dir on the root as one state transition:

js
function applyLocale(locale) {
  const isArabic = locale.startsWith("ar");

  document.documentElement.lang = locale;
  document.documentElement.dir = isArabic ? "rtl" : "ltr";
}

This example is appropriate for an application whose supported Arabic locales are RTL and whose other supported locales are LTR. A broader multilingual product should map locale metadata explicitly rather than treat “not Arabic” as LTR.

After switching, inspect open dialogs, portals, cached screens and components that chose an icon or animation at mount time. Direction-aware CSS updates immediately, but application state derived once from the old locale may not.

Persist the locale according to the product contract, then test refresh, deep links, authentication redirects, a second tab and browser navigation. The root direction should not depend on visiting the language selector first.

Debug dir problems from the root inward#

Use a repeatable sequence when an Arabic component appears wrong:

  1. Confirm the route is the intended Arabic locale.
  2. Inspect <html lang> and <html dir> in the live DOM.
  3. Check the component's computed direction.
  4. Find the nearest ancestor with an explicit dir or CSS direction.
  5. Disable local LTR overrides and physical positioning rules one at a time.
  6. Reduce mixed content to one known value, such as the invoice number above.
  7. Compare DOM order, visual order and keyboard order.
  8. Repeat after navigation, validation, asynchronous loading and locale switching.

Classify the result before changing code:

SymptomLikely layerFirst evidence
Entire Arabic page starts leftRoot documentLive lang, dir, computed direction
One Arabic component runs LTRLocal overrideNearest explicit boundary and CSS rule
Only an ID or phone reordersBidi isolationExact authored and rendered value
Icon or drawer uses wrong edgePhysical CSS or transformComputed inset and transform
Tab sequence disagrees with rowDOM versus visual orderDOM order, flex direction, focus trace
Empty input changes side on typingAuto directionEmpty and populated computed direction

This prevents a common regression: widening an LTR override until the screenshot looks correct, then discovering that Arabic labels, focus order or another value now fail.

Test the root attribute through a complete journey#

A root inspection on the homepage is necessary but not sufficient. Test a deep Arabic URL in a fresh session, then navigate through a representative journey with loading, empty, invalid, failed and success states.

At each transition, verify:

  • the URL and locale state still agree;
  • root lang and dir remain correct;
  • components inherit the expected direction;
  • known LTR values keep their internal order;
  • unknown user strings are isolated;
  • dialogs and toasts inherit the current document direction;
  • no unexpected horizontal overflow appears;
  • visual order and keyboard order preserve the task.

Run the journey at the minimum supported width, an intermediate breakpoint and wide desktop. Direction-sensitive problems often appear only when a long Arabic label wraps or an absolutely positioned control approaches the left edge.

Ritla reports the root document failures with R001 and R002, and forced-LTR Arabic containers with R020. A free scan reads the rendered page after scripts run, so it sees the dir a user actually gets rather than the one in the template. Reproduce each result in the route and state where users encounter it.

Once the root is right, the problems that remain are usually inside lines of text, which is where bidirectional text picks up, or in the stylesheet, which is where RTL CSS does.

Checks in this guide

Show every check in this guideShow fewer

See what your Arabic pages are hiding

Paste a URL. Ritla renders the page on desktop and mobile, runs every check, and shows the top issues with screenshot evidence.