A single CSS rule sets margin-left: 0 and then margin-inline-start: 16px on an element in a left-to-right document. Which one takes effect, and why is mixing physical and logical properties risky?
answer
- not two independent properties
- same physical side, one cascade
- specificity first, then source order
- mapping is what creates the conflict
- the bug hides in the untested locale
basics
~20 sThe 16px wins: in a left-to-right horizontal document both declarations target the same physical side, so they cascade together and the later one at equal specificity applies. Swap the order and margin-left: 0 wins instead.
solid answer
~50 sWhen a physical and a logical property resolve to the same physical side, they do not sit in separate buckets — they compete in the cascade as though they were one property. Origin, importance and specificity are compared first, and if those tie the declaration that comes later in source order wins. So `margin-left: 0; margin-inline-start: 16px;` in a `horizontal-tb`, `ltr` document yields a 16px left margin, and reversing the two lines yields 0. That order-dependence is the risk: two rules with identical specificity now behave differently depending on which stylesheet the bundler emitted first, and a developer overriding `margin-inline-start` in a later rule may find a physical `margin-left` from a vendor stylesheet silently winning. Note the mapping is what couples them — under `writing-mode: vertical-rl` `margin-inline-start` targets the top edge, so both declarations apply to different sides and neither overrides the other.
code
css · 6 lines.alert {
margin-left: 0;
margin-inline-start: 16px;
}
/* ltr : left margin 16px (later declaration wins) */
/* rtl : left margin 0 AND right margin 16px (no conflict) */go deeper
Know that logical and physical properties can target the same side, and that when two declarations in one rule set the same thing, the last one wins.
Explain the cascade order — origin, then specificity, then source order — and show that reversing the two declarations reverses the outcome, since neither spelling has extra weight.
Bring the writing-mode nuance: the two only collide when the mapping points at the same edge, so a mixed stylesheet behaves differently per locale and the failure surfaces only in the direction you do not test.
Own the prevention strategy — lint enforcement of one spelling, migrating whole cascade layers rather than components, and using @layer so third-party physical CSS cannot win on bundle order.
## Physical and logical are one property in the cascade It is tempting to imagine that `margin-left` and `margin-inline-start` are independent properties that the browser reconciles at the end. They are not. Logical properties resolve to a physical side, and once resolved they contend for that side's value through the ordinary cascade. Concretely, the browser compares: 1. **Origin and importance** — author `!important` beats author normal, and so on. 2. **Specificity** of the selector each declaration came from. 3. **Source order** — the last declaration standing wins. Nothing in that list distinguishes the spelling. A logical property does not gain or lose specificity for being logical. ```css .box { margin-left: 0; margin-inline-start: 16px; } /* horizontal-tb, ltr -> used left margin is 16px */ .box { margin-inline-start: 16px; margin-left: 0; } /* horizontal-tb, ltr -> used left margin is 0 */ ``` ## The coupling depends on the writing mode The two declarations only conflict *because* the mapping happens to point them at the same edge. Change the mapping and the conflict evaporates: - `horizontal-tb` + `ltr`: `margin-inline-start` is `margin-left`. They conflict. - `horizontal-tb` + `rtl`: `margin-inline-start` is `margin-right`. No conflict — `margin-left: 0` and a 16px right margin both apply. - `vertical-rl`: `margin-inline-start` is `margin-top`. Again no conflict. So the same stylesheet produces a different *number of applied margins* depending on the locale. That is far worse than a wrong value, because the bug appears only in the direction nobody tests locally. ## Why this bites in real codebases **Partial migration.** A team adopts logical properties component by component. A migrated card sets `padding-inline`; a global reset or a vendor stylesheet loaded later still sets `padding-left`. The reset wins on source order, and the card loses its start padding — but only in the bundle where the reset happens to come last. **Utility classes.** Atomic CSS depends on a predictable override order. Introduce a `.ml-0` (physical) alongside a `.ps-4` (logical) and the outcome now depends on the order the utilities were generated in, not on which class was applied to the element. **Override attempts.** A developer sees the wrong margin, adds `margin-inline-start: 16px` in a more specific rule, and it works — because specificity outranks order. Then someone flattens specificity during a cleanup and the physical declaration resurfaces. The fix that "worked" was resting on specificity, not on intent. **Shorthands.** The four-value `margin` shorthand is physical and sets all four sides, so `margin: 0` after `margin-inline-start: 16px` clears it. Because there is no four-side logical shorthand, mixed codebases keep re-introducing this collision. ## How teams keep it out - **Pick one spelling per property family and enforce it.** A stylelint rule that forbids the physical longhands (`declaration-property-value-disallowed-list` style enforcement, or a dedicated logical-properties rule) is the mechanical guard. - **Migrate whole layers, not whole components.** The reset, the base layer and the utility layer should convert together, because those are the layers whose position in the cascade you rely on. - **Use `@layer` if you need a stable order** — a layer's position determines the outcome regardless of file concatenation order, which removes the fragility bundlers introduce. - **Audit third-party CSS.** You may not be able to convert it; the practical answer is to keep it in an earlier cascade layer so your logical declarations reliably win. ## The answer to give State the rule (they cascade together, later wins on ties), give the concrete result (16px), show you know the reversal changes the answer, then name the real risk: the coupling depends on the writing mode, so a mixed stylesheet is direction-dependent in ways that never surface in the developer's own locale.
- Does a logical property carry different specificity from its physical counterpart?No. Specificity is a property of the selector, not of the declaration inside it. `margin-inline-start` and `margin-left` written in the same rule have identical weight, which is precisely why the tie falls through to source order and why the outcome flips when you reorder the two lines.
- How would you stop this class of bug in a large codebase?Enforce a single spelling with a lint rule rather than by convention, and migrate whole cascade layers — reset, base, utilities — rather than individual components, since those layers' order is what overrides depend on. Cascade layers help too: `@layer` fixes precedence explicitly, so bundler concatenation order stops mattering.
- If a rule sets margin: 0 after margin-inline-start: 16px, what happens?The shorthand wins for every side, so the start margin is cleared. `margin` is a physical four-value shorthand that resets all four longhands, and there is no logical four-side equivalent to write instead. This makes shorthand resets a recurring source of collisions in half-migrated stylesheets.
saying these in an interview costs you the question
- Says the physical property always wins over the logical one
- Claims logical properties have higher specificity
- Thinks the two margins are added together
- Assumes the two never interact because the names differ
- Believes reordering the declarations cannot change the result