Right, so you’ve built a beautiful, left-to-right (LTR) site. Everything is neatly aligned to the left, your navigation is on the left, your “close” button is in the top-right corner, and your design has a certain… well, let’s call it a Western sensibility. Now you need to support a language like Arabic or Hebrew, and the entire visual logic of your layout flips on its axis. Welcome to the wonderfully disorienting world of Right-to-Left (RTL).

The first thing to understand is that RTL isn’t just about swapping left and right in your CSS. It’s a complete mirroring of the layout. Think of it like holding your site up to a mirror. Text flows from right to left. The “start” of everything is now on the right. The “end” is on the left. Your margin-left becomes margin-right. Your “previous” button now needs to be on the right of the “next” button. It’s a fundamental shift, and if you do it right, it’s seamless. If you do it wrong, it’s a usability nightmare that makes you look like you don’t care about a huge portion of your audience.

The Foundation: The dir Attribute

This is the single most important thing you will do. The HTML dir attribute on the <html> tag is the signal that tells the browser, “Hey, everything inside here should be RTL.” This triggers the browser’s built-in bidirectional (bidi) algorithms.

<!DOCTYPE html>
<html lang="ar" dir="rtl">
<head>
    <meta charset="UTF-8">
    <title>موقعي</title>
</head>
<body>
    <!-- Your mirrored world begins here -->
</body>
</html>

The dir attribute does a ton of heavy lifting for free. It changes the base text direction, and critically, it sets the default text-align to right instead of left. But don’t rely on it for complex layout. For that, we need CSS.

CSS Logical Properties: Your New Best Friends

The old way of doing RTL was a nightmare. You’d write a bunch of LTR styles, then painstakingly override every left, right, margin-left, padding-right, float: left, etc., in a huge [dir="rtl"] override stylesheet. It was error-prone, brittle, and a maintenance hellscape. I’ve been there. It sucks.

The modern, correct way is to use CSS Logical Properties. Instead of defining physical directions (left, right), you define logical ones (inline-start, inline-end, block-start, block-end). “Inline” refers to the direction of text flow. “Block” refers to the perpendicular direction.

Here’s the translation guide:

Physical (Bad for RTL)Logical (Good for Everything)
leftinline-start
rightinline-end
margin-leftmargin-inline-start
padding-rightpadding-inline-end
float: leftfloat: inline-start
text-align: lefttext-align: start

So, instead of this LTR-prone code:

.old-way {
    margin-left: 1rem;
    text-align: right; /* I'm already confused */
    float: left;
}

You write this future-proof, RTL-ready code:

.new-way {
    margin-inline-start: 1rem; /* Will be left in LTR, right in RTL */
    text-align: start; /* Will be left in LTR, right in RTL */
    float: inline-start;
}

The browser handles the flipping automatically based on the dir attribute. It’s pure magic. You write one set of styles, and it works for both LTR and RTL. This is the hill I will die on. Use logical properties.

Flipping Entire Layouts with direction

Sometimes you need to flip a specific component, not the whole page. Or maybe you’re stuck maintaining a legacy codebase that used the physical property method. For those scenarios, you can use the CSS direction property.

.legacy-component {
    direction: rtl;
}

This will flip the text direction and the layout of just that component. It’s a blunt instrument, but it works. Use it sparingly, and know that it’s not a substitute for building with logical properties from the ground up.

The JavaScript You (Hopefully) Won’t Need

Most of your work should be in HTML and CSS. But sometimes you need to know the current direction in JavaScript, perhaps to position a tooltip or a dynamic element.

// Get the direction of a specific element
const elementDirection = window.getComputedStyle(myElement).direction; // "rtl" or "ltr"

// Or, check the document's direction
const docDirection = document.documentElement.getAttribute('dir'); // "rtl" or null
if (docDirection === 'rtl') {
    // Do something specific for RTL layouts
}

The Gotchas (Because Of Course There Are Gotchas)

  1. Icons and Imagery: A classic “do not flip” example is a magnifying glass icon for search. Its meaning is universal. A “play” button triangle should still point to the right (→), indicating forward progress, even in an RTL context. However, a “next page” chevron (>) absolutely must flip to become (<). This is where attention to detail separates the pros from the amateurs. Use transform: scaleX(-1) for flipping individual icons if needed.
  2. Bidirectional Text (Bidi): Sometimes, you’ll have an RTL language paragraph that contains a company name or a technical term in LTR. The Unicode bidirectional algorithm usually handles this seamlessly, but it can get weird. For these edge cases, know about the <bdi> element and the unicode-bidi CSS property. You probably won’t need them often, but it’s good to know they’re there when things go haywire.
  3. Third-Party Libraries: Not all libraries are created equal. Some handle RTL beautifully, some require a separate RTL CSS file, and some just completely fall apart. Always check the docs and test thoroughly. A library that positions something with left: 0 hardcoded in JavaScript will ignore all your beautiful logical CSS.

The goal isn’t just to make your site work in RTL; it’s to make it feel like it was designed for RTL from the beginning. It’s a sign of respect for your users, and frankly, it’s just good engineering. Using logical properties is the clearest signal that you know what you’re doing and you’ve thought more than one step ahead. Now go build something that doesn’t look backwards to half the planet.