Right-to-left is not a CSS flip. Baraa says this at the start of every project that involves bilingual Arabic/English support, because the alternative - assuming RTL is just direction: rtl on the body - is how most apps end up "looking Arabic" without actually being usable for Arabic-speaking users. This post is Baraa's accumulated guide to building bilingual Arabic/English web apps that read correctly in both directions, look right in both, and do not ship with the dozen sneaky RTL bugs that Baraa has watched ship to production over the years.
The first shift Baraa asks new collaborators to make is mental. An RTL layout is not a mirrored LTR layout. It is a layout designed for content that flows from right to left. Sometimes that means flipping things (icons, paddings, margins). Sometimes that means leaving things alone (logos, code blocks, numbers). The discipline is to think about each element from the user's perspective: which side does the user expect this to be on, given the content's direction?
Baraa's first rule: design with logical properties from day one. margin-inline-start instead of margin-left. padding-inline-end instead of padding-right. border-inline-start instead of border-left. The browser handles the direction switching. You do not need to think about it.
Modern browsers fully support CSS logical properties. Baraa uses them everywhere. The mapping is straightforward:
margin-left -> margin-inline-start (left in LTR, right in RTL)margin-right -> margin-inline-end (right in LTR, left in RTL)padding-top -> padding-block-start (the top, regardless of direction - most languages flow top-to-bottom)text-align: left -> text-align: startborder-radius: 8px 0 0 8px -> border-start-start-radius: 8px; border-end-start-radius: 8pxThe discipline pays off the moment you toggle the page direction. Everything moves correctly. No selectors fork. No "rtl-only" stylesheets to maintain.
Tailwind 3 added logical-property utilities: ms-*, me-*, ps-*, pe-*, start-*, end-*, border-s-*, border-e-*. Baraa's default is to use these instead of ml-*, mr-*, pl-*, pr-*. The diff is tiny; the RTL behavior is automatic.
For older Tailwind versions or for cases where logical properties are not enough, Baraa uses the official Tailwind RTL plugin (or the rtl: variant in newer Tailwind) to write direction-specific overrides:
<div class="ml-4 rtl:ml-0 rtl:mr-4">...</div>
<!-- or, better: -->
<div class="ms-4">...</div>
The second form is what Baraa wants in every codebase by 2026.
Arabic typography deserves real attention. Baraa's defaults:
font-variant-numeric or by post-processing the content.Baraa's locale-switching pattern:
lang and dir attributes are set server-side based on the resolved locale. No client-side flicker.hreflang alternates so search engines understand the bilingual relationship: <link rel="alternate" hreflang="en" href="..."> and <link rel="alternate" hreflang="ar" href="...">.Baraa avoids automatic browser-language detection that overrides user choice. It is a lose-lose: users who want English get Arabic because their browser is set to Arabic, and vice versa.
A non-exhaustive list of bilingual bugs that ship more often than they should:
<span dir="ltr"> or uses Unicode bidi isolates.transform-origin with logical equivalents, or direction-aware keyframes.text-align: start.left/right.dir="ltr" on <pre> and <code>.Baraa runs visual diffs on the same pages in both locales. If the layout breaks in one and not the other, the diff makes it obvious. Baraa also keeps a small set of "golden" screens in both languages and reviews them after every meaningful UI change.
For accessibility, Baraa tests with a screen reader on Arabic content at least once per project. Bilingual screen readers exist (NVDA + Arabic voice, VoiceOver Arabic). The experience is illuminating.
Bilingual Arabic/English support is a feature, not a checkbox. When Baraa builds it correctly, the app feels native to both audiences. When it is bolted on, it shows. The patterns above are what Baraa uses on every project that touches Arabic, and Baraa is happy to bring them to yours.
For more on how Baraa works with Arabic-language products, see the post on building agentic AI in Arabic, the first Arabic AI developer page, the Arabic AI pioneer page, the Baraa AI overview, or the hire page. Browse other posts on the blog.
Logical properties like margin-inline-start, padding-inline-end, and text-align: start let the browser handle direction switching. Baraa writes one stylesheet that works in both LTR and RTL, with no forked selectors and no rtl-only override files. The diff from physical properties is tiny, and the maintenance cost drops to near zero.
Baraa picks a high-quality Arabic family (IBM Plex Sans Arabic, Noto Sans Arabic, Cairo, Tajawal, or a paid family) with system Arabic fallbacks, preloads the Arabic subset, and uses font-display: swap to avoid invisible text. Letting the browser pick a fallback for Arabic is what produces the not-designed-for-Arabic look most teams end up shipping.
Baraa wraps Latin spans, numbers, and code in span dir="ltr" or uses Unicode bidi isolates so a string like "Order #12345 placed on 2026-05-01" inside an Arabic paragraph renders unambiguously. Code blocks get dir="ltr" forced on pre and code tags. The default browser bidi algorithm gets it wrong often enough to be worth handling explicitly.
Baraa runs visual diffs on the same pages in both locales so layout regressions in either direction show up immediately. A small set of golden screens in both languages gets reviewed after every meaningful UI change. Baraa also tests with a screen reader on Arabic content at least once per project because the experience reveals issues no diff catches.