How to use CSS logical properties to create layouts that automatically adapt to any writing direction.

Karim Benali
Senior frontend developer with 10+ years building RTL-first applications.
For years, CSS has used physical properties like margin-left, padding-right, border-top, and text-align: left. These work perfectly for left-to-right languages, but break down when you need to support RTL languages like Arabic or Hebrew.
Enter CSS Logical Properties—a modern approach that uses start and end instead of left and right. With logical properties, your layouts automatically adapt to any writing direction without a single line of direction-specific CSS.
Consider a simple card layout:
/* Traditional physical properties */
.card {
padding-left: 20px;
margin-right: 16px;
border-left: 4px solid blue;
text-align: left;
}For LTR, this looks great. But for RTL? The padding is on the wrong side, the margin is backwards, and the border is in the wrong place.
Previously, you had to duplicate styles:
/* LTR styles */
.card {
padding-left: 20px;
margin-right: 16px;
border-left: 4px solid blue;
text-align: left;
}
/* RTL overrides */
[dir="rtl"] .card {
padding-left: 0;
padding-right: 20px;
margin-right: 0;
margin-left: 16px;
border-left: none;
border-right: 4px solid blue;
text-align: right;
}This approach has problems:
Logical properties use the concept of start and end relative to the writing mode:
/* Logical properties - works for both LTR and RTL */
.card {
padding-inline-start: 20px;
margin-inline-end: 16px;
border-inline-start: 4px solid blue;
text-align: start;
}Just set dir="rtl" on your HTML, and everything flips automatically!
CSS Logical Properties use two axes:
| Physical | Logical (Inline) | Behavior in RTL |
|---|---|---|
| left | inline-start | Becomes right |
| right | inline-end | Becomes left |
| Physical | Logical (Block) | Behavior |
|---|---|---|
| top | block-start | Usually stays top |
| bottom | block-end | Usually stays bottom |
/* Physical */
margin-top: 10px;
margin-right: 20px;
margin-bottom: 10px;
margin-left: 20px;
/* Logical */
margin-block-start: 10px;
margin-inline-end: 20px;
margin-block-end: 10px;
margin-inline-start: 20px;
/* Shorthand */
margin-block: 10px; /* top and bottom */
margin-inline: 20px; /* start and end */
margin-inline: 20px 30px; /* start end *//* Physical */
padding-left: 15px;
padding-right: 15px;
/* Logical */
padding-inline-start: 15px;
padding-inline-end: 15px;
/* Shorthand */
padding-inline: 15px; /* both start and end */
padding-block: 10px 20px; /* start end *//* Physical */
border-left: 2px solid red;
border-right-width: 1px;
/* Logical */
border-inline-start: 2px solid red;
border-inline-end-width: 1px;
/* Border radius - logical values */
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 *//* Physical */
width: 300px;
height: 200px;
min-width: 100px;
max-height: 500px;
/* Logical */
inline-size: 300px;
block-size: 200px;
min-inline-size: 100px;
max-block-size: 500px;/* Physical */
position: absolute;
top: 0;
left: 0;
right: auto;
bottom: auto;
/* Logical */
position: absolute;
inset-block-start: 0;
inset-inline-start: 0;
inset-inline-end: auto;
inset-block-end: auto;
/* Shorthand */
inset: 0; /* all sides */
inset-block: 10px; /* top and bottom */
inset-inline: 20px; /* start and end */
inset: 10px 20px 10px 20px; /* top right bottom left (physical!) *//* Physical */
text-align: left;
text-align: right;
/* Logical */
text-align: start;
text-align: end;/* Physical */
float: left;
clear: right;
/* Logical */
float: inline-start;
clear: inline-end;.nav {
display: flex;
padding-inline: 20px;
}
.nav-logo {
margin-inline-end: auto;
}
.nav-link {
padding-inline: 16px;
border-inline-end: 1px solid #ccc;
}
.nav-link:last-child {
border-inline-end: none;
}.card {
display: flex;
align-items: center;
padding: 16px;
border-inline-start: 4px solid var(--accent-color);
}
.card-icon {
margin-inline-end: 12px;
flex-shrink: 0;
}
.card-content {
flex: 1;
text-align: start;
}
.card-action {
margin-inline-start: auto;
}.form-group {
margin-block-end: 20px;
}
.form-label {
display: block;
margin-block-end: 8px;
text-align: start;
}
.form-input {
padding-inline: 12px;
padding-block: 8px;
text-align: start;
}
.form-hint {
margin-block-start: 4px;
padding-inline-start: 12px;
font-size: 0.875rem;
}.layout {
display: flex;
}
.sidebar {
inline-size: 250px;
padding-inline-end: 24px;
border-inline-end: 1px solid #e5e5e5;
}
.main-content {
flex: 1;
padding-inline-start: 24px;
}CSS Logical Properties have excellent modern browser support:
| Browser | Support |
|---|---|
| Chrome | 89+ (full) |
| Firefox | 66+ (full) |
| Safari | 15+ (full) |
| Edge | 89+ (full) |
For older browsers, consider using PostCSS with the postcss-logical plugin:
// postcss.config.js
module.exports = {
plugins: [
require('postcss-logical')({
dir: 'ltr', // Fallback direction
preserve: true // Keep logical properties too
})
]
}This generates fallback physical properties:
/* Input */
.card {
margin-inline-start: 20px;
}
/* Output */
.card {
margin-left: 20px;
margin-inline-start: 20px;
}Flexbox is inherently logical and respects direction:
.container {
display: flex;
flex-direction: row; /* Reverses in RTL */
}Items automatically reorder based on direction—no changes needed!
CSS Grid also works logically:
.grid {
display: grid;
grid-template-columns: 200px 1fr; /* Reverses in RTL */
gap: 20px;
}Grid areas and track placement follow the document direction automatically.
Find all physical properties that should be logical:
# Find left/right usage in CSS
grep -r "margin-left\|margin-right\|padding-left\|padding-right" src/Not everything should use logical properties:
Should be logical:
Should stay physical:
Convert components one at a time:
/* Before */
.button {
padding: 8px 16px;
margin-right: 8px;
text-align: left;
}
/* After */
.button {
padding-block: 8px;
padding-inline: 16px;
margin-inline-end: 8px;
text-align: start;
}Delete direction-specific overrides that are now unnecessary:
/* Remove these */
[dir="rtl"] .button {
margin-right: 0;
margin-left: 8px;
text-align: right;
}inset Shorthand is Physical!/* This is still physical! */
inset: 10px 20px 10px 20px;
/* Equivalent to: top right bottom left */
/* Use individual logical properties for RTL support */
inset-block: 10px;
inset-inline: 20px;Transforms use physical coordinates:
/* This doesn't flip in RTL */
transform: translateX(20px);
/* You may need direction-specific values */
[dir="rtl"] .element {
transform: translateX(-20px);
}Background position is physical:
/* Doesn't flip automatically */
background-position: left center;
/* Need manual override or use logical alternative */
background-position-x: left; /* No logical equivalent yet */Use logical properties by default: Start with inline-start/inline-end instead of left/right for all new CSS.
Leverage Flexbox and Grid: They handle direction automatically.
Physical isn't always wrong: Some properties (shadows, decorative borders) should stay physical.
Browser support is excellent: All modern browsers fully support logical properties.
Migration is incremental: Convert component by component, removing RTL overrides as you go.
How to use CSS logical properties to create layouts that automatically adapt to any writing direction.

Karim Benali
Senior frontend developer with 10+ years building RTL-first applications.
For years, CSS has used physical properties like margin-left, padding-right, border-top, and text-align: left. These work perfectly for left-to-right languages, but break down when you need to support RTL languages like Arabic or Hebrew.
Enter CSS Logical Properties—a modern approach that uses start and end instead of left and right. With logical properties, your layouts automatically adapt to any writing direction without a single line of direction-specific CSS.
Consider a simple card layout:
/* Traditional physical properties */
.card {
padding-left: 20px;
margin-right: 16px;
border-left: 4px solid blue;
text-align: left;
}For LTR, this looks great. But for RTL? The padding is on the wrong side, the margin is backwards, and the border is in the wrong place.
Previously, you had to duplicate styles:
/* LTR styles */
.card {
padding-left: 20px;
margin-right: 16px;
border-left: 4px solid blue;
text-align: left;
}
/* RTL overrides */
[dir="rtl"] .card {
padding-left: 0;
padding-right: 20px;
margin-right: 0;
margin-left: 16px;
border-left: none;
border-right: 4px solid blue;
text-align: right;
}This approach has problems:
Logical properties use the concept of start and end relative to the writing mode:
/* Logical properties - works for both LTR and RTL */
.card {
padding-inline-start: 20px;
margin-inline-end: 16px;
border-inline-start: 4px solid blue;
text-align: start;
}Just set dir="rtl" on your HTML, and everything flips automatically!
CSS Logical Properties use two axes:
| Physical | Logical (Inline) | Behavior in RTL |
|---|---|---|
| left | inline-start | Becomes right |
| right | inline-end | Becomes left |
| Physical | Logical (Block) | Behavior |
|---|---|---|
| top | block-start | Usually stays top |
| bottom | block-end | Usually stays bottom |
/* Physical */
margin-top: 10px;
margin-right: 20px;
margin-bottom: 10px;
margin-left: 20px;
/* Logical */
margin-block-start: 10px;
margin-inline-end: 20px;
margin-block-end: 10px;
margin-inline-start: 20px;
/* Shorthand */
margin-block: 10px; /* top and bottom */
margin-inline: 20px; /* start and end */
margin-inline: 20px 30px; /* start end *//* Physical */
padding-left: 15px;
padding-right: 15px;
/* Logical */
padding-inline-start: 15px;
padding-inline-end: 15px;
/* Shorthand */
padding-inline: 15px; /* both start and end */
padding-block: 10px 20px; /* start end *//* Physical */
border-left: 2px solid red;
border-right-width: 1px;
/* Logical */
border-inline-start: 2px solid red;
border-inline-end-width: 1px;
/* Border radius - logical values */
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 *//* Physical */
width: 300px;
height: 200px;
min-width: 100px;
max-height: 500px;
/* Logical */
inline-size: 300px;
block-size: 200px;
min-inline-size: 100px;
max-block-size: 500px;/* Physical */
position: absolute;
top: 0;
left: 0;
right: auto;
bottom: auto;
/* Logical */
position: absolute;
inset-block-start: 0;
inset-inline-start: 0;
inset-inline-end: auto;
inset-block-end: auto;
/* Shorthand */
inset: 0; /* all sides */
inset-block: 10px; /* top and bottom */
inset-inline: 20px; /* start and end */
inset: 10px 20px 10px 20px; /* top right bottom left (physical!) *//* Physical */
text-align: left;
text-align: right;
/* Logical */
text-align: start;
text-align: end;/* Physical */
float: left;
clear: right;
/* Logical */
float: inline-start;
clear: inline-end;.nav {
display: flex;
padding-inline: 20px;
}
.nav-logo {
margin-inline-end: auto;
}
.nav-link {
padding-inline: 16px;
border-inline-end: 1px solid #ccc;
}
.nav-link:last-child {
border-inline-end: none;
}.card {
display: flex;
align-items: center;
padding: 16px;
border-inline-start: 4px solid var(--accent-color);
}
.card-icon {
margin-inline-end: 12px;
flex-shrink: 0;
}
.card-content {
flex: 1;
text-align: start;
}
.card-action {
margin-inline-start: auto;
}.form-group {
margin-block-end: 20px;
}
.form-label {
display: block;
margin-block-end: 8px;
text-align: start;
}
.form-input {
padding-inline: 12px;
padding-block: 8px;
text-align: start;
}
.form-hint {
margin-block-start: 4px;
padding-inline-start: 12px;
font-size: 0.875rem;
}.layout {
display: flex;
}
.sidebar {
inline-size: 250px;
padding-inline-end: 24px;
border-inline-end: 1px solid #e5e5e5;
}
.main-content {
flex: 1;
padding-inline-start: 24px;
}CSS Logical Properties have excellent modern browser support:
| Browser | Support |
|---|---|
| Chrome | 89+ (full) |
| Firefox | 66+ (full) |
| Safari | 15+ (full) |
| Edge | 89+ (full) |
For older browsers, consider using PostCSS with the postcss-logical plugin:
// postcss.config.js
module.exports = {
plugins: [
require('postcss-logical')({
dir: 'ltr', // Fallback direction
preserve: true // Keep logical properties too
})
]
}This generates fallback physical properties:
/* Input */
.card {
margin-inline-start: 20px;
}
/* Output */
.card {
margin-left: 20px;
margin-inline-start: 20px;
}Flexbox is inherently logical and respects direction:
.container {
display: flex;
flex-direction: row; /* Reverses in RTL */
}Items automatically reorder based on direction—no changes needed!
CSS Grid also works logically:
.grid {
display: grid;
grid-template-columns: 200px 1fr; /* Reverses in RTL */
gap: 20px;
}Grid areas and track placement follow the document direction automatically.
Find all physical properties that should be logical:
# Find left/right usage in CSS
grep -r "margin-left\|margin-right\|padding-left\|padding-right" src/Not everything should use logical properties:
Should be logical:
Should stay physical:
Convert components one at a time:
/* Before */
.button {
padding: 8px 16px;
margin-right: 8px;
text-align: left;
}
/* After */
.button {
padding-block: 8px;
padding-inline: 16px;
margin-inline-end: 8px;
text-align: start;
}Delete direction-specific overrides that are now unnecessary:
/* Remove these */
[dir="rtl"] .button {
margin-right: 0;
margin-left: 8px;
text-align: right;
}inset Shorthand is Physical!/* This is still physical! */
inset: 10px 20px 10px 20px;
/* Equivalent to: top right bottom left */
/* Use individual logical properties for RTL support */
inset-block: 10px;
inset-inline: 20px;Transforms use physical coordinates:
/* This doesn't flip in RTL */
transform: translateX(20px);
/* You may need direction-specific values */
[dir="rtl"] .element {
transform: translateX(-20px);
}Background position is physical:
/* Doesn't flip automatically */
background-position: left center;
/* Need manual override or use logical alternative */
background-position-x: left; /* No logical equivalent yet */Use logical properties by default: Start with inline-start/inline-end instead of left/right for all new CSS.
Leverage Flexbox and Grid: They handle direction automatically.
Physical isn't always wrong: Some properties (shadows, decorative borders) should stay physical.
Browser support is excellent: All modern browsers fully support logical properties.
Migration is incremental: Convert component by component, removing RTL overrides as you go.