Flipping the Interface: CSS Logical Properties for RTL Design
Why is dir=”rtl” alone not enough? A beginner’s guide to understanding the difference between physical and logical properties in CSS, complete with a practical user card example that flips correctly and a full browser support table.
Word count: ~2,100 · Reading time: 11 minutes
CSS Logical Properties for Building RTL Interfaces
The first root of the RTL Interface Design Guide — why dir=”rtl” alone fails, and how logical properties fix the problem at its source
A note for the reader: This series picks up where our article CSS Logical Properties: Engineering Bidirectional Interfaces Without Bias from the Hybrid Text Processing series left off. That article focused on protecting text inside an element. This new series goes one layer deeper: engineering the full interface — button placement, shadow direction, visual reading patterns, and everything a single line of code can’t solve.
In this article from Zy Yazan, we open our RTL Interface Design Guide series with a question that sounds simple — then reveals itself as a genuine engineering problem: why do so many websites look “broken” after adding dir="rtl"? Why does the icon end up in the wrong place, the margin on the wrong side, the border on the wrong edge?
The answer in one sentence: because the code was born thinking “left” is a universal truth, not just a cultural convention. CSS logical properties are the tool that breaks that assumption.
Why dir=”rtl” Is Not Enough
When you add dir="rtl" to your <html> or <body> tag, you’re asking the browser to do exactly one thing: reverse the text flow direction. The heading shifts to the right, paragraphs start from the right, lists align from the right. Up to that point, the browser keeps its promise.
But what doesn’t change? Every CSS property you wrote by hand that mentions left or right: margin-left, padding-right, border-left, left: 20px, text-align: right. These properties are hardcoded — they refer to a physical side of the screen regardless of reading direction. dir="rtl" cannot reverse them.
The classic result: an interface that looks like someone grabbed it from both ends and pulled in opposite directions. Text flows right to left, but the arrow icon still points left — so “Next” is pointing backward. Or the decorative border sits on the left side of a card while all the content starts from the right.
Let’s make this concrete with explicit code:
/* What does dir="rtl" actually do? */
/* It flips these ✓ */
p { /* text starts from the right automatically */ }
ul { /* lists align from the right */ }
table { /* table columns start from the right */ }
/* It does NOT flip these ✗ */
.card { margin-left: 20px; } /* stays on the left */
.icon { border-right: 2px solid; } /* stays on the right */
.tooltip { left: 0; } /* stays on the left */
.label { text-align: right; } /* stays right even if the meaning calls for left */
The difference between adding dir="rtl" and building a truly right-to-left interface is the difference between hanging an “Arabic Store” sign on a shop designed for Western customers, and building a shop from the ground up for someone who knows exactly what they want and where to find it.
Physical vs. Logical Properties
Any CSS property with “left” or “right” in its name is called a physical property: it describes a fixed position on the screen as if the screen were a rigid canvas. The direction is absolute — it knows nothing about the user’s language.
A logical property flips that perspective entirely. Instead of “left” and “right,” it speaks the language of flow — start and end. It doesn’t ask: where on the screen? It asks: where in the reading context? When that context changes (by switching dir or writing-mode), the property follows automatically.
To make this tangible, compare two properties:
margin-left: 16px— always means: a margin on the western side of the element. In Arabic or English, the answer is the same: it’s the left side.margin-inline-start: 16px— means: a margin at the start of the flow. In English (LTR) that’s the left side. In Arabic (RTL) that’s the right side. One rule, two contexts, two correct results.
The axis these properties operate on is called the inline axis — the axis along which words flow within a line. Its counterpart is the block axis — the axis along which paragraphs stack vertically — and that axis doesn’t change with dir in horizontal languages like Arabic or English.
The Most-Used Properties at a Glance
This isn’t a comprehensive reference table — MDN has that. This is your daily working table: the properties you’ll hit in every interface project and need to swap out immediately.
| Physical Property | Logical Replacement | What It Controls |
|---|---|---|
margin-left / margin-right |
margin-inline-start / margin-inline-end |
Side margins |
padding-left / padding-right |
padding-inline-start / padding-inline-end |
Side padding |
border-left / border-right |
border-inline-start / border-inline-end |
Side borders |
text-align: left / right |
text-align: start / end |
Text alignment |
left / right (absolute positioning) |
inset-inline-start / inset-inline-end |
Coordinates for position: absolute |
margin-top / margin-bottom |
margin-block-start / margin-block-end |
Vertical margins (block axis) |
width |
inline-size |
Size along the flow axis |
height |
block-size |
Size along the block axis |
Note the last two rows: margin-top and margin-bottom have logical equivalents too, but you’ll rarely need them in horizontal languages like Arabic or English, since the vertical axis doesn’t change with direction. They’re listed here for completeness, but replacing them is optional in most cases.
A Practical Example: A User Card That Flips Correctly
Let’s build a simple user card — an avatar on one side, a name and description on the other — and make it work in both English and Arabic from a single stylesheet. We’ll start with the old approach to see the problem, then rebuild it the right way.
The Old Way — Code That Breaks in RTL
/* ❌ Old approach: physical properties */
.user-card {
display: flex;
align-items: center;
border: 1px solid #e0e0e0;
border-radius: 8px;
padding: 16px;
}
.user-card .avatar {
width: 64px;
height: 64px;
border-radius: 50%;
margin-right: 16px; /* ← locked to the right always */
border-left: 3px solid #c0392b; /* ← border on the left always */
}
.user-card .name {
font-weight: bold;
text-align: left; /* ← left always */
}
/* Then when Arabic is added, you're forced to write this: */
[dir="rtl"] .user-card .avatar {
margin-right: 0; /* reset */
margin-left: 16px; /* flip */
border-left: none; /* reset */
border-right: 3px solid #c0392b; /* flip */
}
[dir="rtl"] .user-card .name {
text-align: right; /* flip */
}
This code works, but it doubles with every new component. And it demands you remember to reset every property before flipping it — otherwise both values stack and break the layout.
The Right Way — One Codebase for Every Direction
/* ✅ Logical approach: one stylesheet for every language */
.user-card {
display: flex;
align-items: center;
border: 1px solid #e0e0e0;
border-radius: 8px;
padding: 16px;
}
.user-card .avatar {
width: 64px;
height: 64px;
border-radius: 50%;
margin-inline-end: 16px; /* space after the avatar in reading direction */
border-inline-start: 3px solid #c0392b; /* border at the avatar's start edge */
flex-shrink: 0;
}
.user-card .name {
font-weight: bold;
text-align: start; /* follows reading direction */
}
/* That's it. This is the complete file. */
In LTR (English): the avatar appears on the left, the margin pushes away from the text on the right side, and the border sits on the avatar’s left edge. In RTL (Arabic): the avatar appears on the right, the margin pushes away on the left side, and the border sits on the avatar’s right edge. All from the exact same code.
Here’s the full HTML for both cards side by side so you can see the difference:
<!-- LTR version -->
<div class="user-card" dir="ltr">
<img class="avatar" src="avatar.jpg" alt="User photo" />
<div class="info">
<p class="name">Ahmed Hassan</p>
<p class="role">Frontend Developer</p>
</div>
</div>
<!-- Same markup, different direction — no CSS changes -->
<div class="user-card" dir="rtl">
<img class="avatar" src="avatar.jpg" alt="صورة المستخدم" />
<div class="info">
<p class="name">أحمد حسن</p>
<p class="role">مطور واجهات أمامية</p>
</div>
</div>
The key idea: HTML doesn’t know CSS, and CSS doesn’t know the language. What makes the system work is that dir in HTML tells CSS about the flow context, and logical properties respond to that context. The three layers work together as one cohesive system.
The deeper reason this works is that Flexbox — as discussed in the Hybrid Text Processing series — was designed from the start around flow logic, not geographic logic. flex-start and flex-end follow dir automatically with no intervention needed. Combining Flexbox with logical properties produces a layout that adapts to any direction completely.
Browser Support — The Current Picture
The question many developers pause at: can these properties be used in production today, or are they still “the future”? The answer since 2023 is clear: yes, fully.
| Browser | First Version with Support | Full Support Today |
|---|---|---|
| Chrome / Edge | Version 87 | ✓ Full |
| Firefox | Version 66 | ✓ Full |
| Safari | Version 14.1 | ✓ Full |
| Samsung Internet | Version 14 | ✓ Full |
| Internet Explorer | — | ✗ No support |
Internet Explorer has no support, but if your project still targets IE, that’s a separate conversation about a completely different kind of legacy. For the modern web — which is what 99% of Arabic website users are on today — logical properties are safe and stable to use right now.
One note: inset-inline-start may need testing on Safari versions older than iOS 14.5. If you’re targeting that slice of users, add a left/right fallback value before it.
If your project targets enterprise environments locked to older browsers, build tools like PostCSS can generate fallback code automatically — but that’s a separate path from interface architecture itself.
Migrating an Existing Project: Where to Start
The most practical question from a developer who’s convinced: I have an existing site with thousands of lines of old CSS. Where do I begin? The answer is not “rewrite everything from scratch.”
Incremental migration means: don’t fix what isn’t broken, fix what is broken first. These three steps work for any project:
Step 1 — Find the actual breakage
Before touching any code, add dir="rtl" to the page and open it. Every element that looks misplaced or visually inconsistent is a candidate for fixing. Screenshot or list them. The visual breakage is your work map.
Step 2 — All new components are born logical
From now on, every new component you write uses logical properties from the start. Never go back to physical properties in new code. This stops technical debt from accumulating instead of trying to pay it all off at once.
Step 3 — Convert the elements that break the design
Now start fixing the broken elements you identified. The swap is usually direct: every margin-left you decide to fix becomes margin-inline-start, every border-right becomes border-inline-end. You don’t need to reset the old property — you’re deleting it entirely.
/* Example of incremental replacement */
/* Before */
.sidebar-link {
padding-left: 12px;
border-left: 2px solid #c0392b;
text-align: left;
}
/* After */
.sidebar-link {
padding-inline-start: 12px;
border-inline-start: 2px solid #c0392b;
text-align: start;
}
A useful tool: search your files
If you want to know how many physical properties need reviewing in your project, run this from any code editor or terminal:
# In Terminal (Linux / Mac / Windows PowerShell):
# Shows every line containing physical CSS properties
grep -rn "margin-left\|margin-right\|padding-left\|padding-right\|border-left\|border-right\|text-align: left\|text-align: right" ./src --include="*.css"
# In VS Code: search with Regex:
# (margin|padding|border)-(left|right)
The output gives you a specific list of files and line numbers. Start with elements that appear on every page (navigation, sidebar, cards) before the rare ones.
Incremental migration doesn’t require a dedicated rewrite day. It requires a decision: every time you touch a CSS file, leave the part you touched cleaner than you found it.
A Common Question: What About Shorthand Properties?
A shorthand like margin: 0 16px places 16px on both the physical left and right — does that break? In practice, no, because margin: 0 16px means equal margins on both sides, and that doesn’t change between RTL and LTR. The problem only appears when the two values are different: margin: 0 16px 0 32px. Here you’re saying 32px on the left and 16px on the right — and that’s exactly what breaks in RTL.
The logical replacement for asymmetric inline shorthand is: margin-inline: 32px 16px — first value is start, second is end, and both flip automatically with direction.
Takeaways and What’s Next
CSS logical properties aren’t just a tool for Arabic or RTL websites — they’re a tool for any developer who wants to write CSS that holds up. In a world where a single site serves 12 languages and a single component gets reused in multiple directions, physical properties are a luxury you can’t afford.
What we covered here is the foundation: the conceptual difference between the two models, a ready-to-use replacement table, and a migration strategy for existing projects. That’s enough to start today.
But there are elements in any interface that intentionally break this rule — not because the developer made a mistake, but because the UI itself carries a fixed directional meaning that shouldn’t change with language. Media players, circular progress bars, timeline charts — these elements need to hold their position even in RTL, and that’s what the next article covers: Fixed Components.
References:
- W3C official specification: CSS Logical Properties and Values Level 1
- MDN comprehensive guide: CSS logical properties and values
- Browser support table — Can I Use: caniuse.com/css-logical-props
- Our companion article from the Hybrid Text Processing series: CSS Logical Properties: Engineering Bidirectional Interfaces Without Bias
— RTL Interface Design Guide —
Current article: 1 — CSS Logical Properties for RTL Interfaces
Next article: 2 — Fixed Components
Related series:
Hybrid Text Processing Guide |
Financial Data Localization Guide |
Web 3.0 & AI Localization Workshop
Zy Yazan © 2026
Localization Series
RTL Interface Design Guide — 4 Articles
Series: RTL Interface Design Guide — 4 Articles | Zy Yazan Platform © 2026





