margin-inline-start vs margin-left
Compare margin-inline-start with margin-left, see how each maps in RTL, and migrate existing CSS without changing the LTR layout.
Guide15 min read
margin-left and margin-inline-start can produce the same layout in English and opposite layouts in Arabic. That is why a replacement can look perfect in the default route, pass review, and then put the space on the wrong side as soon as dir="rtl" appears. CSS has waited patiently for everyone to remember that left and start are not synonyms.
The useful distinction is about intent:
margin-leftmeans “put space on the physical left edge.”margin-inline-startmeans “put space before this box in the inline reading direction.”
In the horizontal writing mode used by ordinary English and Arabic interfaces, inline start is left in LTR and right in RTL. Use the logical property when the space belongs to the reading flow. Keep the physical property when the space genuinely belongs to the left side of the screen or component.
That one decision solves most of the comparison. The remaining trouble comes from finding out what old CSS intended, handling auto, and avoiding collisions between logical and physical declarations. Naturally, the small margin has brought paperwork.
The difference in one table#
For writing-mode: horizontal-tb, which covers ordinary English and Arabic pages, the mapping is:
| Declaration | LTR result | RTL result | What it expresses |
|---|---|---|---|
margin-left: 1rem | 1rem on the left | 1rem on the left | A physical side |
margin-right: 1rem | 1rem on the right | 1rem on the right | A physical side |
margin-inline-start: 1rem | 1rem on the left | 1rem on the right | Space before the box in the inline flow |
margin-inline-end: 1rem | 1rem on the right | 1rem on the left | Space after the box in the inline flow |
The MDN reference for margin-inline-start defines it in terms of the element's writing mode, direction and text orientation. The CSS Logical Properties specification defines the logical and physical margin properties as one property group whose sides are mapped from those computed values.
That language matters. margin-inline-start does not mean “the Arabic version of margin-left.” It does not even mean “right.” It means inline start. In a vertical writing mode, inline start may map to the top or bottom instead. Calling it “the margin that flips” is a useful shortcut until somebody adds vertical labels and the shortcut sends an invoice.
For a normal horizontal Arabic interface, though, the practical result is simple: margin-inline-start computes to the right margin. The brief's Chromium 151 measurement confirms that mapping for an RTL page.
Start with the job the margin is doing#
Do not migrate a declaration by looking only at its current side. Ask why the margin exists.
Suppose an editorial review toolbar shows a review state at inline start and an action at inline end. In English, that means the state is on the left and the action is on the right. In Arabic, they should trade physical sides while keeping the same relationship to the reading flow.
Problem
<div class="review-toolbar">
<span class="review-toolbar__state">Needs legal review</span>
<button class="review-toolbar__action" type="button">Open comments</button>
</div>.review-toolbar {
display: flex;
align-items: center;
gap: 0.75rem;
}
.review-toolbar__action {
margin-left: auto;
}In LTR, margin-left: auto absorbs the free space before the button and pushes it to the physical right. In RTL, the flex row begins on the right, but the physical left margin is now on the button's outer side. It does not express “push this action to the end of the row.” The state and action can remain bunched together instead of separating across the toolbar.
Better
<div class="review-toolbar">
<span class="review-toolbar__state">Needs legal review</span>
<button class="review-toolbar__action" type="button">Open comments</button>
</div>.review-toolbar {
display: flex;
align-items: center;
gap: 0.75rem;
}
.review-toolbar__action {
margin-inline-start: auto;
}The LTR layout is unchanged because inline start maps to left there. In RTL, inline start maps to right, so the auto margin consumes the free space between the state and the action. The action moves to the left edge, which is inline end for that row.
This is more than a property rename. The physical rule described a screenshot. The logical rule describes the relationship.
auto makes the difference easier to see#
A fixed value such as 1rem creates a visible gap on one side. An auto margin participates in layout by absorbing available space, so choosing the wrong side can move an item to a completely different place.
In a flex container, margin-inline-start: auto is useful when one item should separate from the items before it and sit at inline end. It is the flow-relative version of a familiar LTR pattern:
.publication-actions {
display: flex;
align-items: center;
gap: 0.5rem;
}
.publication-actions__primary {
margin-inline-start: auto;
}Do not translate that intent to justify-content: space-between without checking the component. space-between distributes space between all flex items. An auto margin creates one deliberate break before the selected item. Those can look identical with two children and part company as soon as a third arrives. The third child always arrives. It has excellent timing.
The same warning applies outside flexbox: auto margins depend on the layout mode and available space. MDN's margin-left reference documents that the result varies between block, positioned, flex and other layouts. Logical properties change which physical side participates; they do not give auto a new layout algorithm.
A safe replacement preserves the LTR layout#
Replacing margin-left with margin-inline-start is correct only when the existing left margin represents start-side spacing.
Consider four declarations found in an LTR stylesheet:
.review-note {
margin-left: 1rem;
}
.review-count {
margin-left: 0.5rem;
}
.fixed-scale-label {
margin-left: 6px;
}
.chip + .chip {
margin-left: 0.5rem;
}They all mention the same physical side. They may need three different decisions.
Space before an item#
If .review-note follows a start-side marker and the margin means “leave room after the marker,” then the item needs space on its inline-start side:
.review-note {
margin-inline-start: 1rem;
}In LTR, this stays on the left. In RTL, it moves to the right. That preserves the relationship between the marker and note.
Space after an item#
Sometimes a left margin in the current stylesheet is compensating for an item that sits on the physical right. If the intended relationship is “space after the item,” the correct logical property may be margin-inline-end, not margin-inline-start.
The conversion rule is not left becomes start. It is:
- Describe the existing LTR geometry.
- Name the relationship as start, end, block start, block end, or physical.
- Choose the property that keeps the existing LTR geometry.
- Check that the RTL result matches the intended relationship.
This little procedure lacks a dramatic name. It also works.
A physical left edge#
If .fixed-scale-label belongs six pixels from the left edge of a map, image, canvas or measurement scale, keep margin-left. The left side is part of the visual coordinate system, not the reading direction.
Logical CSS is not a purity test. Replacing a correct physical coordinate with a flow-relative one makes the rule less accurate, which is an unusual way to improve code.
Repeated spacing between siblings#
The chip rule is probably asking margin to do a container's job. Prefer gap when the requirement is equal space between flex or grid children:
.chip-list {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
}gap does not need a start or end side. It inserts space between tracks or items, so there is no first-child exception and no selector that quietly becomes wrong when DOM order changes. Use margin-inline-start when one particular edge matters. Use gap when the relationship is simply “space between these siblings.”
The mapping follows the element, not the page title#
Logical margin mapping uses the element's own computed writing mode and direction. This becomes visible when a page contains nested direction boundaries.
An Arabic publishing dashboard may contain an LTR source-code preview:
<main dir="rtl">
<section class="source-preview" dir="ltr">
<span>Revision EDT-K6-518</span>
<button class="source-preview__action" type="button">View source</button>
</section>
</main>.source-preview {
display: flex;
align-items: center;
}
.source-preview__action {
margin-inline-start: auto;
}The button inherits LTR from .source-preview, so its inline start is left. The outer page's RTL direction does not reach through the explicit dir="ltr" boundary and remap the button back to the right. This is often exactly what an embedded LTR component needs.
It can also surprise someone debugging from the document root. Inspect the target element's computed direction, not merely the dir value on <html>. If the component has a legitimate local direction, fix the expectation. If the local direction was added as a layout patch, remove the patch and set direction according to the content. The guide to dir="rtl" covers document and component direction in depth.
Vertical writing modes reveal the same principle more dramatically. The inline axis can run vertically, so margin-inline-start can map to a top or bottom margin. margin-left remains left. The Arabic product interfaces in scope here use horizontal-tb, but shared components and editorial content can meet other writing modes. A logical name is a promise about flow, not a clever alias for one physical side.
Mixing logical and physical margins creates cascade traps#
Logical and physical declarations are not separate layers that politely avoid each other. When they map to the same side, they compete in the cascade.
The specification gives a precise model: corresponding physical and logical properties share a computed value, and the declaration with higher cascade priority wins. With equal origin, layer, importance and specificity, later source order decides.
.review-toolbar__action {
margin-inline-start: auto;
margin-left: 1rem;
}In LTR, both declarations map to the left margin. The later margin-left wins, so the auto margin stops pushing the action across the toolbar.
In RTL, the two declarations address different sides. margin-inline-start maps to the right, while margin-left remains left. The button now has an auto margin on one side and a fixed margin on the other. The same rule block has changed from a conflict into a combination. Delightful, if your goal was to make the bug depend on direction.
A later physical shorthand can cause a similar problem:
.review-toolbar__action {
margin-inline-start: auto;
margin: 0;
}The later margin declaration sets the physical margins and wins on the side to which inline start maps. The auto spacing disappears. Reverse the order if both declarations are intentional, or better, avoid mixing the two models for the same component edge.
DevTools can make this look stranger than it is. The rules pane shows the declarations you wrote. The computed pane shows their mapped physical result. When a logical declaration appears active but the physical margin is not the value you expected, search for:
- a later
marginshorthand; - a physical longhand with higher specificity;
- a declaration in a later cascade layer;
!importanton either version;- a local
directionorwriting-modechange.
The cascade is not confused. It has simply read more of the stylesheet than you have.
Do not stack a fallback without clearing its other side#
margin-inline-start is widely available in current browsers. MDN marks it as broadly supported since January 2020. If your support policy includes older browsers, a tempting fallback is:
.review-note {
margin-left: 1rem;
margin-inline-start: 1rem;
}This appears sensible in LTR because both declarations map to the left and the later logical one wins. In a supporting RTL browser, though, they no longer map to the same side. The physical fallback remains on the left while the logical declaration adds space on the right. The component gets two margins.
If a legacy fallback is genuinely required, isolate it and clear the physical side in supporting browsers:
.review-note {
margin-left: 1rem;
}
@supports (margin-inline-start: 1rem) {
.review-note {
margin-left: 0;
margin-inline-start: 1rem;
}
}On an old browser, the physical fallback remains. On a supporting LTR browser, the logical declaration restores the left margin. On a supporting RTL browser, the left margin is cleared and the logical margin maps right.
This does not give an old browser an adaptive RTL layout. Supporting legacy RTL would require an explicit RTL override too:
[dir="rtl"] .review-note {
margin-left: 0;
margin-right: 1rem;
}Before adding all three branches, check the actual browser policy. A fallback nobody needs is still code somebody must debug. It is also code with a surprisingly active social life.
Use margin-inline when both inline sides belong together#
The margin-inline shorthand sets inline start and inline end. One value applies to both. Two values use start first and end second:
.manuscript-page {
margin-inline: auto;
max-inline-size: 72rem;
}
.annotation {
margin-inline: 1.25rem 0;
}The page receives auto margins on both inline sides, a common centering pattern. The annotation receives 1.25rem at inline start and zero at inline end. In RTL, the physical sides swap while the start/end meaning stays intact.
Do not read the two-value form as left then right. It is start then end. That sentence is worth keeping near a migration review because muscle memory has had several decades to become unhelpful.
The four-value margin shorthand remains physical in current CSS. Its values follow top, right, bottom, left order:
.annotation {
margin: 0 0 0 1.25rem;
}That last value is still physical left. If the intended spacing is flow-relative, use logical longhands or margin-inline. MDN's logical margins, borders and padding guide documents these mappings and shorthands.
Keep physical margins when the design is physical#
An RTL codebase can contain margin-left. The question is whether the left edge has meaning independent of language.
Physical margins are reasonable for:
- labels aligned to the fixed left side of a chart or map coordinate system;
- controls positioned around media whose geometry does not mirror;
- print marks and measurement guides tied to a physical page edge;
- deliberate asymmetric artwork that should not change with text direction;
- browser chrome simulations or device frames where “left” names the object itself.
Flow-relative margins are the better fit for:
- space before or after text, icons, badges and controls;
- pushing a flex item toward inline end with
auto; - indentation that follows the reading direction;
- metadata offsets beside a start-side marker;
- component spacing that should mirror with the surrounding interface.
There is a useful review question: if the whole component were viewed in a mirror, should this space move too? If yes, it is probably logical. If the space belongs to a fixed coordinate, keep it physical. “Probably” is doing honest work there. Maps, charts, media controls and culturally asymmetric artwork deserve inspection rather than a slogan. For broader decisions about mirrored geometry, see what should be mirrored in RTL.
Migrate an existing stylesheet without guessing#
A global replacement of margin-left with margin-inline-start will fix some declarations, leave others conceptually wrong, and move any genuinely physical layout. It is fast in the same way that dropping the stylesheet is fast.
Use a small migration pass instead.
1. Inventory directional margin declarations#
Search for:
margin-left
margin-right
margin:
margin-inline
margin-inline-start
margin-inline-endInclude CSS-in-JS objects, component variants, utility classes and styles injected by third-party widgets. Search for shorthands because a later margin can override a logical longhand even when no physical longhand is visible nearby.
2. Classify each margin by intent#
Assign one of four labels:
- Start-side relationship: replace with
margin-inline-start. - End-side relationship: replace with
margin-inline-end. - Space between siblings: consider
gapon the container. - Physical geometry: keep
margin-leftormargin-rightand add a comment if the reason is not obvious.
This classification is the part a codemod cannot infer safely. Class names help, but .left-panel may be a physical map legend, an old name for a sidebar that now mirrors, or a small monument to a redesign from 2019.
3. Preserve the LTR snapshot first#
After each conversion, compare the LTR component before and after. A change from physical left to logical end can alter the English layout immediately. Catch that before assessing RTL, or you will be debugging two directions at once and blaming the one with unfamiliar letters.
4. Test the component under a real direction boundary#
Render the same component twice:
<section dir="ltr" lang="en">
<!-- Render the review toolbar here. -->
</section>
<section dir="rtl" lang="ar">
<!-- Render the same review toolbar here. -->
</section>Do not fake the RTL state with flex-direction: row-reverse. Direction affects logical-property mapping across the component; reversing one flex row does not. It can also produce a visual order that disagrees with keyboard order, a separate problem with its own paperwork.
5. Remove obsolete RTL overrides#
An older stylesheet may already contain:
.review-note {
margin-left: 1rem;
}
[dir="rtl"] .review-note {
margin-right: 1rem;
margin-left: 0;
}After converting the base declaration to margin-inline-start, delete the physical override:
.review-note {
margin-inline-start: 1rem;
}Leaving both versions behind creates redundant rules at best and cascade conflicts when a variant changes one of them. The benefit of a logical property is not merely that it works in RTL. It lets the component state its layout once.
Verify the computed sides, not just the screenshot#
Visual comparison catches a misplaced gap, but computed styles explain why it moved. Inspect the target element in both directions and record the element's direction plus both physical margins:
const action = document.querySelector(".review-toolbar__action");
const styles = getComputedStyle(action);
console.table({
direction: styles.direction,
marginInlineStart: styles.marginInlineStart,
marginLeft: styles.marginLeft,
marginRight: styles.marginRight,
});For an LTR element with margin-inline-start: 16px, expect the computed left margin to be 16px. For an RTL element using the same declaration, expect the computed right margin to be 16px. If the result differs, inspect the cascade and local direction before changing the property again.
The specification requires computed-style APIs to return the shared computed value for a mapped logical and physical pair. That gives you a useful debugging clue: under LTR, marginInlineStart pairs with marginLeft; under RTL, it pairs with marginRight.
Test more than the quiet default state:
- the normal LTR and RTL routes;
- a nested LTR component inside the RTL route;
- narrow widths where the toolbar wraps or changes layout;
- variants that apply a
marginreset; - long translated labels that reduce free space around
automargins; - hover, focus, expanded and disabled states if they load extra styles;
- document width after the change, especially when a fixed-size component sits near an edge.
If a physical declaration is direction-sensitive and has no RTL override, R030 reports it. That makes the check useful during migration, but a report still needs human classification. Some physical margins are defects; others are the correct expression of physical geometry. The property name alone cannot settle the design intent.
If a changed margin contributes to content extending beyond the page, follow the debugging steps in how to fix horizontal overflow in RTL. A small side margin can be the last few pixels that create a large and extremely unnecessary scrollbar.
Common mistakes#
Replacing every left word with start#
margin-left does not automatically become margin-inline-start. A left margin can represent start, end, sibling spacing or a fixed coordinate. Translate the relationship, then choose the property.
Adding an RTL override instead of removing the physical assumption#
An override can be valid when supporting old browsers or a third-party stylesheet you cannot change. In code you own, one logical declaration is easier to reason about than a physical base rule plus mirrored exceptions across components and variants.
Testing only the document root#
Nested dir values change the computed direction of descendants. A source preview, code editor, phone field or embedded English panel can map inline start differently from the Arabic page around it. Test the component at the boundary where it actually renders.
Assuming margin-inline-start changes DOM order#
Margins move space. They do not reorder elements, reverse flex items, change focus order or alter the Unicode direction of text. If the children are in the wrong sequence, fix the layout or markup responsible for sequence. A margin can move the evidence around, which is not quite the same achievement.
Leaving both models active#
Physical and logical declarations can target the same side in one direction and different sides in another. Remove replaced declarations and audit shorthands. Otherwise, the RTL route becomes the first place the stylesheet reveals its collection of mutually confident opinions.
Make the margin describe the relationship#
Use margin-left when you mean left. Use margin-inline-start when you mean before the item in the inline flow. The browser can map start to the correct side, but it cannot infer which relationship the designer intended from a twenty-line component and a class named .thing-secondary-final.
A free Ritla scan can surface direction-sensitive physical CSS through R030, giving you a focused list to review against the component's intent. Then keep the logical declarations that follow reading flow, keep the physical ones that describe real coordinates, and let each margin do one job without acquiring an RTL alter ego.
For the rest of the same migration, continue with RTL CSS: the complete developer guide. If a component maps start from an unexpected direction boundary, return to the guide to dir="rtl" before adding another override. The cascade already has enough hobbies.
FAQ
Is margin-inline-start always the same as margin-right in RTL?
Only in a horizontal RTL writing mode such as the one used by ordinary Arabic web interfaces. The mapping also depends on writing-mode and text orientation. In a vertical writing mode, inline start can be a top or bottom edge.
Is margin-left wrong on an Arabic page?
No. It is wrong when the intended spacing should follow reading direction and the rule leaves it physically left. It is correct when the design genuinely refers to the left side. The decision depends on meaning, not locale alone.
Can I use margin-left as a fallback before margin-inline-start?
Not safely as two bare declarations if the component runs in RTL. A supporting RTL browser can apply the physical left margin and the logical right margin together. Use a feature query that clears the fallback, or keep explicit LTR and RTL physical overrides for the legacy browsers you actually support.
Should I replace sibling margins with logical margins?
Use gap for equal spacing between flex or grid children. Use a logical margin when one item's start or end edge needs special spacing, including an auto margin that absorbs free space.
Does declaration order matter when both forms are present?
Yes. When a logical and physical declaration map to the same side, the normal cascade decides which value wins. With otherwise equal priority, the later declaration wins. When direction changes, those declarations may map to different sides and both can apply.