CSS Logical Properties: Write Once, Support Every Writing Direction
Logical properties replace left, right, top and bottom with rules that follow the writing direction, which is what RTL support actually needs.
Physical properties like margin-left and border-top assume text runs left to right and top to bottom. Set dir="rtl" on a page and a sidebar that looked fine starts pushing the wrong way. Change the writing mode and the layout is sideways.
Logical properties name the same edges relative to the writing mode instead of the screen, so you declare them once and they follow the text.
The Core Concept
Put left, right, top and bottom aside for a moment:
- Inline axis: the direction text flows (horizontal in LTR, horizontal but reversed in RTL, vertical in some East Asian modes).
- Block axis: perpendicular to text flow (vertical in most cases).
| Physical | Logical Equivalent |
|---|---|
top | block-start |
bottom | block-end |
left | inline-start |
right | inline-end |
Margin and Padding
/* Physical — breaks in RTL */
.card {
margin-left: 1rem;
padding-right: 1.5rem;
}
/* Logical — works in all directions */
.card {
margin-inline-start: 1rem;
padding-inline-end: 1.5rem;
}That is the whole change. inline-start resolves to left in LTR and right in RTL; same numbers, different mapping, nothing to override.
Shorthand Logical Properties
/* Set inline (left + right) and block (top + bottom) together */
.section {
margin-inline: auto; /* Centers horizontally */
padding-block: 2rem; /* Top and bottom padding */
padding-inline: 1.5rem; /* Left and right padding */
}This replaces:
.section {
margin-left: auto;
margin-right: auto;
padding-top: 2rem;
padding-bottom: 2rem;
padding-left: 1.5rem;
padding-right: 1.5rem;
}Converting our design system buttons was mostly deletion in the diff.
Width and Height
/* Physical */
.box {
width: 300px;
height: 200px;
min-width: 100px;
max-height: 500px;
}
/* Logical */
.box {
inline-size: 300px;
block-size: 200px;
min-inline-size: 100px;
max-block-size: 500px;
}inline-size is width while text runs horizontally and height once it doesn't, so the same rule gives a different box under writing-mode: vertical-rl.
Borders and Inset
/* Physical — border only on the left */
.highlight {
border-left: 3px solid oklch(0.6 0.2 260);
}
/* Logical — border on the text-start side */
.highlight {
border-inline-start: 3px solid oklch(0.6 0.2 260);
}Positioned elements work the same way:
/* Physical */
.tooltip {
position: absolute;
top: 0;
right: 0;
}
/* Logical */
.tooltip {
position: absolute;
inset-block-start: 0;
inset-inline-end: 0;
}
/* Shorthand for all four sides */
.tooltip {
inset: 0 0 auto auto; /* Still physical-ordered for shorthand */
}The inset shorthand is still ordered top, right, bottom, left. Mixing it with logical longhands in one rule is confusing, and I have done it.
Text Alignment
/* Physical — always left-aligns */
.label {
text-align: left;
}
/* Logical — aligns to text start (left in LTR, right in RTL) */
.label {
text-align: start;
}left still works, and an English-only site won't break either way. I use start so the next person doesn't assume the direction is fixed.
Full Reference Map
| Physical | Logical |
|---|---|
margin-top | margin-block-start |
margin-bottom | margin-block-end |
margin-left | margin-inline-start |
margin-right | margin-inline-end |
padding-top | padding-block-start |
width | inline-size |
height | block-size |
top | inset-block-start |
left | inset-inline-start |
float: left | float: inline-start |
text-align: left | text-align: start |
Browser Support
Logical properties have full support in all modern browsers. The shorthand forms like margin-inline and padding-block have been stable since 2021.
If you're starting a component library, use logical properties from the first commit. Retrofitting a codebase that mixes both is tedious, so existing sites are better off migrating a component at a time.