Logical Properties and Internationalization
Left and Right Are Not Universal
Here's something most developers never think about: English reads left-to-right, top-to-bottom. Arabic reads right-to-left. Japanese can read top-to-bottom, right-to-left. If you hardcode margin-left: 20px for an indent, Arabic users see the indent on the wrong side. Logical properties replace physical directions (left, right, top, bottom) with flow-relative ones (inline-start, inline-end, block-start, block-end) that automatically adapt to any writing direction.
Think of logical properties as directions relative to reading flow, not the screen. "Start" is where reading begins — left in English, right in Arabic. "End" is where reading finishes. "Block" is the direction paragraphs stack — top-to-bottom in most languages, right-to-left in vertical Japanese. If your CSS uses logical properties, flipping the language flips the layout automatically — no separate RTL stylesheet needed.
Physical vs Logical Properties
| Physical | Logical | Description |
|---|---|---|
margin-left | margin-inline-start | Start of reading direction |
margin-right | margin-inline-end | End of reading direction |
margin-top | margin-block-start | Start of block flow |
margin-bottom | margin-block-end | End of block flow |
padding-left | padding-inline-start | Start inline padding |
padding-right | padding-inline-end | End inline padding |
width | inline-size | Size in reading direction |
height | block-size | Size in block direction |
top | inset-block-start | Block start offset |
left | inset-inline-start | Inline start offset |
border-left | border-inline-start | Border at inline start |
text-align: left | text-align: start | Align to start of reading |
Shorthand Properties
.card {
/* Horizontal padding (inline axis) */
padding-inline: 1.5rem; /* Sets both inline-start and inline-end */
/* Vertical padding (block axis) */
padding-block: 1rem; /* Sets both block-start and block-end */
/* Individual values */
margin-inline: 2rem 1rem; /* inline-start: 2rem, inline-end: 1rem */
/* Size */
inline-size: 300px; /* Width in LTR, adapts in vertical modes */
block-size: auto;
/* Border */
border-inline-start: 3px solid blue; /* Left border in LTR, right in RTL */
/* Position */
inset-inline-start: 0; /* left: 0 in LTR, right: 0 in RTL */
}
Building RTL-Ready Layouts
/* Physical (breaks in RTL) */
.sidebar {
float: left;
margin-right: 2rem;
border-left: 3px solid blue;
text-align: left;
}
/* Logical (works in any direction) */
.sidebar {
float: inline-start;
margin-inline-end: 2rem;
border-inline-start: 3px solid blue;
text-align: start;
}
Setting the Direction
<!-- Document level -->
<html lang="ar" dir="rtl">
<!-- Section level -->
<blockquote dir="rtl">Arabic quote here</blockquote>
/* Or via CSS (HTML attribute is preferred for semantics) */
:root[dir="rtl"] {
direction: rtl;
}
/* Detect direction in CSS */
:dir(rtl) .icon { transform: scaleX(-1); } /* Flip directional icons */
Not everything should flip in RTL. Phone numbers, code snippets, mathematical formulas, and progress bars usually maintain LTR direction. Icons that represent directional concepts (back arrow, forward arrow) should flip, but icons that are symmetric (home, search, settings) should not. Use the :dir(rtl) pseudo-class to selectively flip only what needs flipping.
Writing Modes
Most of us won't need this daily, but understanding it makes logical properties click.
For vertical text and advanced internationalization:
/* Default: horizontal, top-to-bottom */
.default { writing-mode: horizontal-tb; }
/* Vertical: right-to-left columns (traditional CJK) */
.vertical-rl { writing-mode: vertical-rl; }
/* Vertical: left-to-right columns */
.vertical-lr { writing-mode: vertical-lr; }
And this is where it gets mind-bending: when writing mode changes, the meaning of inline and block axes swap:
.vertical-text {
writing-mode: vertical-rl;
/* Now: inline axis = vertical, block axis = horizontal */
/* inline-size = what was height */
/* block-size = what was width */
/* margin-inline-start = what was margin-top */
}
This is exactly why logical properties exist — they follow the text flow regardless of writing mode.
The practical impact on flexbox and grid
Flexbox and grid already use logical directions. flex-direction: row follows the inline axis — in RTL, a row flex container starts items from the right. justify-content: flex-start means inline-start. Grid's grid-column-start follows the inline direction. So if you're using flex/grid, your layout already adapts to writing direction without explicit logical properties. But margins, padding, borders, and widths still need the logical property equivalents.
Production Scenario: Navigation with RTL Support
Let's see how this works in a real component.
.nav {
display: flex;
gap: 1rem;
padding-inline: 2rem;
}
.nav-logo {
margin-inline-end: auto; /* Pushes other items to the end */
}
.nav-item {
padding-inline: 0.75rem;
}
/* Dropdown arrow flips in RTL */
.dropdown-arrow {
margin-inline-start: 0.25rem;
}
:dir(rtl) .dropdown-arrow {
transform: scaleX(-1);
}
/* Breadcrumb separator */
.breadcrumb-separator {
margin-inline: 0.5rem;
/* In LTR: > */
/* In RTL: need to flip or use a neutral separator */
}
:dir(rtl) .breadcrumb-separator {
transform: scaleX(-1);
}
| What developers do | What they should do |
|---|---|
| Using margin-left/margin-right for layout spacing Physical directions break when the document direction changes to RTL or when writing mode is vertical | Use margin-inline-start/end or the margin-inline shorthand |
| Flipping all icons in RTL layouts Not everything has direction. A house icon doesn't need to face right-to-left. | Only flip directional icons (arrows, progress). Keep symmetric icons (home, search) unchanged. |
| Using CSS direction property instead of the HTML dir attribute The dir attribute affects the bidirectional algorithm for text, form inputs, and accessibility. CSS direction only affects rendering. | Use the HTML dir attribute for document direction — it's semantic and affects non-CSS behavior |
| Forgetting that width/height are physical — using them for responsive layouts In vertical writing modes, width becomes the block axis. inline-size always follows the text flow. | Use inline-size and block-size for layouts that should adapt to writing direction |
- 1Use logical properties (margin-inline, padding-block, inline-size) instead of physical (margin-left, padding-top, width)
- 2Flexbox and grid already follow logical direction — items flow with the document's inline axis
- 3Use the HTML dir attribute for direction, not the CSS direction property
- 4Only flip directional icons in RTL — symmetric icons stay unchanged
- 5Shorthands like margin-inline and padding-block set both start and end values