RTL web design: the rules that matter for Arabic websites
RTL design mirrors layout, navigation and arrows, but not logos, numbers or media controls. The rules, the CSS that makes it cheap, and what to test.
By Sohail Hussain31 October 20267 min read
Right-to-left (RTL) design means the page starts at the right edge. Text, navigation order, layout columns and any icon that points forward or back are mirrored. Logos, digits, phone numbers, media controls, clocks and photos stay exactly as they are. Technically it comes down to two decisions: set dir="rtl" on the document, and write the CSS with logical properties so the layout mirrors itself instead of needing a second stylesheet.
This is the part of our guide to building an Arabic website that covers layout. It is also the first thing we check when a client asks us to add Arabic to a site, because it decides whether the job is a few weeks of work or a rebuild. Our Arabic and multilingual websites work starts from these rules on the first wireframe.
What does the dir attribute do, and where does it go?
dir sets the base direction for an element and everything inside it. Put it on the html element, next to the language:
<html lang="ar" dir="rtl">
From that one attribute the browser right-aligns text, starts tables and lists on the right, places the first flex item on the right, and runs the bidirectional algorithm that orders mixed Arabic and Latin text. The W3C recommends setting direction in the markup rather than with the CSS direction property, because direction belongs to the content and must survive when stylesheets fail to load. MDN's reference for the dir attribute lists its three values: ltr, rtl and auto.
Use dir="auto" where you cannot know the direction in advance: a search box, a name field, a comment or a review. The browser then sets the direction from the first strongly directional character typed, so an Arabic name aligns right and an English one aligns left.
Which parts of a page should mirror, and which should not?
Mirror anything whose position or direction follows reading order. Leave anything that represents a fixed thing in the world.
| Element | Mirror in RTL? | Why |
|---|---|---|
| Navigation order, breadcrumbs, tabs | Yes | They follow reading order |
| Back and next arrows, chevrons, "read more" arrows | Yes | Forward points left in Arabic |
| Sidebar and main column | Yes | The main column starts where reading starts |
| Progress bars, steppers, sliders, carousels | Yes | Progress runs in the reading direction |
| An icon beside a label | Its position, yes | The icon itself stays the same |
| Logos and brand marks | No | A logo is a fixed image |
| Digits, prices, phone numbers | No | Numbers run left to right in Arabic too |
| Play, pause, fast-forward | No | They describe playback direction, not reading |
| Clocks, refresh and loading spinners | No | Clockwise is clockwise in every language |
| Photos, maps, product shots | No | Flipping them makes them wrong |
| Icons with no direction: close, plus, settings | No | There is nothing to flip |
The mistakes go both ways: logos and phone numbers mirrored when they should not be, and "Next" buttons whose arrow was never flipped, so on the Arabic page the arrow tells the reader to go back.
How do CSS logical properties make RTL cheap?
Physical properties such as margin-left and right describe sides of the screen. Logical properties describe sides of the text flow. inline-start is where a line begins: left in English, right in Arabic. Write the layout once with logical properties and it mirrors as soon as dir changes.
| Physical (avoid for anything that should mirror) | Logical equivalent |
|---|---|
margin-left, margin-right | margin-inline-start, margin-inline-end |
padding-left, padding-right | padding-inline-start, padding-inline-end |
border-left, border-right | border-inline-start, border-inline-end |
left, right on positioned elements | inset-inline-start, inset-inline-end |
text-align: left | text-align: start |
float: left | float: inline-start |
border-top-left-radius | border-start-start-radius |
A card with an accent border and a badge pinned to its starting corner looks like this, and needs no RTL override:
.card {
position: relative;
padding-inline: 1.5rem 1rem;
border-inline-start: 4px solid var(--accent);
text-align: start;
}
.card .badge {
position: absolute;
inset-inline-start: 1rem;
inset-block-start: 1rem;
}
Flexbox and grid already follow direction. With dir="rtl", flex-direction: row lays items out from the right and grid column 1 is the rightmost column. Use gap for spacing between items rather than margins on one side, and most layouts need nothing else. MDN's guide to CSS logical properties and values has the full list, and every current browser supports the common ones.
Tailwind CSS has logical utilities as well: ms-4 and me-4 for margins, ps-4 and pe-4 for padding, start-0 and end-0 for positioning, text-start, and rtl: and ltr: variants for the few rules that must differ.
What does not flip on its own?
Some things ignore dir, and you have to handle them yourself.
- Transforms.
translateX(100%)moves an element right in both languages. Off-canvas menus, drawers and carousels built with transforms need an RTL value. - Directional icons. An SVG arrow is a picture, not text. It needs an explicit flip.
- Background images positioned with
leftorright, such as a decorative shape in a hero. - Shadows with a horizontal offset. Small, but a shadow falling the wrong way on every card is noticeable.
- JavaScript that positions tooltips and dropdowns with left offsets, or reads horizontal scroll positions. Scroll positions in RTL containers count in the opposite direction, which breaks many carousel scripts.
The :dir(rtl) pseudo-class, supported in all major browsers since the end of 2023, matches elements by their computed direction. A component can then style itself correctly wherever it is placed:
.icon-arrow:dir(rtl) {
transform: scaleX(-1);
}
How do you handle English words inside Arabic text?
Arabic pages are full of Latin text: brand names, model numbers, email addresses, URLs, "AED". The browser's bidirectional algorithm orders these runs, and it usually gets the words right and the punctuation wrong. A sentence ending in an English name can show its full stop at the wrong end. A phone number written as +971 can display with the plus sign after the digits.
Three habits fix almost every case:
- Wrap embedded Latin runs in a
bdielement, or give the elementunicode-bidi: isolate, so the run is laid out as a self-contained block and cannot pull neighbouring punctuation into it. - Set
dir="ltr"on phone numbers, email addresses, URLs and code, wherever they appear. - Set
dir="ltr"on form fields that only take Latin input, such as email, website and phone. The typed text and placeholder then read correctly.
Text pasted from Word documents and PDFs often carries invisible direction marks and control characters that produce the same symptoms. Paste a suspicious string into our Unicode character inspector to see every hidden character by name, then delete the ones that should not be there.
How should you test an RTL layout?
Test with real Arabic copy, not mirrored English or placeholder text. Arabic has different word lengths, tall letters and deep descenders, and no capitals, so headings that fit in English wrap or clip in Arabic. The type-specific problems are covered in Arabic typography mistakes.
Run this pass on every template:
- Load the page with
dir="rtl"and real Arabic text in every component. - Check the order: logo, navigation, sidebar, breadcrumbs and pagination all start on the right.
- Check that every arrow and chevron points in the reading direction.
- Check that digits, phone numbers and email addresses read correctly and are not mirrored.
- Open every menu, modal, tooltip, dropdown and carousel. Transforms and JavaScript positioning hide here.
- Submit every form, including the error messages and the confirmation email.
- Repeat on a phone. Off-canvas menus that slide in from the wrong side are a common RTL bug that only shows on small screens.
The language switcher needs its own check, since it moves to the other end of the header in Arabic; language switcher design covers the patterns.
Is a separate RTL stylesheet ever the right choice?
For a new build, no. Logical properties cost nothing extra when written from the start, and they keep one stylesheet for both languages, so a change made for English cannot silently break Arabic.
For an existing site with thousands of lines of physical CSS, converting everything by hand may not be worth it. Tools such as RTLCSS generate a flipped copy of a stylesheet automatically, and that copy is served on Arabic pages. It works as a stopgap. The cost comes later: every CSS change must be tested in both directions, because the generated file also flips things that should stay fixed, and someone has to remember to regenerate it.
If the site is due for a redesign within a year, put the money into the redesign and build it with logical properties from the start. Adding a second language to an existing site walks through how to make that call.
Was this guide useful?