CSS Logical Properties
Write direction-agnostic CSS that automatically adapts to different writing modes and text directions — essential for internationalization and modern layout.
Physical vs Logical Properties
Traditional CSS properties like margin-left and padding-right are physical — they always refer to the same side of the screen regardless of text direction. Logical properties instead reference the flow-relative direction, automatically adapting when the writing direction changes.
/* Physical — always the left side, even in RTL */ .card { margin-left: 20px; padding-right: 16px; border-top: 2px solid #ccc; width: 300px; }
/* Logical — adapts to writing direction automatically */ .card { margin-inline-start: 20px; padding-inline-end: 16px; border-block-start: 2px solid #ccc; inline-size: 300px; }
dir="rtl". The layout simply mirrors itself.
Block & Inline Axes
Logical properties revolve around two flow-relative axes:
- Block axis — the direction blocks stack. In horizontal writing modes (English), this is vertical (top → bottom).
- Inline axis — the direction text flows. In LTR English, this is horizontal (left → right).
/* Block axis = vertical in horizontal writing modes */ margin-block-start: 1rem; /* = margin-top */ margin-block-end: 1rem; /* = margin-bottom */ /* Inline axis = horizontal in LTR */ margin-inline-start: 1rem; /* = margin-left (LTR) / margin-right (RTL) */ margin-inline-end: 1rem; /* = margin-right (LTR) / margin-left (RTL) */
Writing Mode
The writing-mode property changes which direction is block and which is inline, which redefines how logical properties map to physical ones.
.horizontal { writing-mode: horizontal-tb; /* default — text left→right, blocks top→bottom */ } .vertical-rl { writing-mode: vertical-rl; /* text top→bottom, blocks right→left (Japanese) */ } .vertical-lr { writing-mode: vertical-lr; /* text top→bottom, blocks left→right (Mongolian) */ }
Logical Margins
Logical margin properties replace margin-top/right/bottom/left with flow-relative equivalents.
.element { /* Individual sides */ margin-block-start: 1rem; /* top in LTR horizontal */ margin-block-end: 2rem; /* bottom */ margin-inline-start: 1rem; /* left in LTR */ margin-inline-end: 2rem; /* right in LTR */ /* Shorthand: two values = start end */ margin-block: 1rem 2rem; /* block-start block-end */ margin-inline: 1rem 2rem; /* inline-start inline-end */ /* Single value applies to both sides */ margin-block: 1rem; /* top & bottom */ margin-inline: auto; /* horizontal centering! */ }
Logical Padding
Padding follows the same logical naming pattern as margins.
.card { /* Individual */ padding-block-start: 2rem; padding-block-end: 2rem; padding-inline-start: 1.5rem; padding-inline-end: 1.5rem; /* Shorthand */ padding-block: 2rem; /* top & bottom */ padding-inline: 1.5rem; /* left & right (LTR) */ }
Logical Borders
Borders also have complete logical equivalents including width, style, color, and radius.
.element { /* Block axis borders */ border-block-start: 2px solid #6366f1; border-block-end: 1px solid #ccc; border-block: 1px solid #ccc; /* both */ /* Inline axis borders */ border-inline-start: 4px solid #6366f1; border-inline-end: none; border-inline: 1px solid #ccc; /* both */ /* Logical border-radius */ border-start-start-radius: 8px; /* top-left in LTR */ border-start-end-radius: 8px; /* top-right in LTR */ border-end-start-radius: 8px; /* bottom-left in LTR */ border-end-end-radius: 8px; /* bottom-right in LTR */ }
Logical Sizing
Instead of width and height, use inline-size and block-size.
.box { /* Instead of width/height */ inline-size: 300px; /* = width in horizontal mode */ block-size: 200px; /* = height in horizontal mode */ /* Min / Max variants */ min-inline-size: 100px; /* = min-width */ max-inline-size: 600px; /* = max-width */ min-block-size: 50px; /* = min-height */ max-block-size: 400px; /* = max-height */ } /* Common pattern: responsive max-width replacement */ .container { max-inline-size: 1200px; margin-inline: auto; }
Inset Shorthand
The inset property is the logical shorthand for top, right, bottom, left, and has logical axis variants.
.overlay { position: absolute; /* Physical shorthand — all four sides */ inset: 0; /* top:0 right:0 bottom:0 left:0 */ inset: 10px 20px; /* top/bottom:10px right/left:20px */ inset: 10px 20px 30px 40px; /* top right bottom left */ /* Logical axis shorthands */ inset-block: 0; /* block-start & block-end */ inset-block-start: 10px; /* = top */ inset-block-end: 20px; /* = bottom */ inset-inline: 0; /* inline-start & inline-end */ inset-inline-start: 10px; /* = left (LTR) / right (RTL) */ inset-inline-end: 20px; /* = right (LTR) / left (RTL) */ }
Logical Text Alignment
Use start and end instead of left and right for direction-aware alignment.
.text-start { text-align: start; /* left in LTR, right in RTL */ } .text-end { text-align: end; /* right in LTR, left in RTL */ } /* Also works in Flexbox */ .flex-container { justify-content: flex-start; /* already logical! */ }
Full Mapping Table: Physical → Logical
A complete reference of physical properties and their logical equivalents (in horizontal-tb + LTR).
RTL Live Demo — Auto-Adaptation
This demo uses only logical properties. Toggle the direction to see the entire card layout adapt automatically — no extra CSS needed.
Ahmed Hassan
Senior Frontend Developer
dir="rtl" override in CSS.
Gotchas
margin-left and margin-inline-start together can cause unexpected behavior. Pick one approach and stick with it.
margin shorthand is still physical. margin: 10px 20px 30px 40px maps to top/right/bottom/left. There is no single logical shorthand that replaces the four-value margin. Use margin-block and margin-inline separately.
border-start-start-radius means block-start + inline-start (top-left in LTR). The format is border-{block}-{inline}-radius. Take time to memorize the pattern.
transform: translateX() is always horizontal. There's no logical equivalent for transforms yet.
Pro Tips
margin-inline and padding-block is more expressive and future-proof. Modern browsers have excellent support.
margin-inline: auto for centering. It's the logical equivalent of margin: 0 auto and reads more clearly — you're centering on the inline axis.
:dir() pseudo-class. For the rare cases where you need direction-specific overrides that logical properties can't solve, use :dir(rtl) instead of [dir="rtl"] — it checks the computed direction, not just the attribute.
.card { max-inline-size: 400px; margin-inline: auto; padding-block: 1.5rem; padding-inline: 2rem; border-block-start: 4px solid var(--accent); border-start-start-radius: 12px; border-start-end-radius: 12px; } .card__icon { margin-inline-end: 1rem; inline-size: 48px; block-size: 48px; } .card__meta { margin-block-start: 0.5rem; padding-inline-start: 1rem; border-inline-start: 2px solid var(--muted); text-align: start; }