ESC

Type to search the knowledge base.

CSS Logical Properties

margin-inline, padding-block, inset-inline-start — write layouts that flip with writing-mode and direction instead of hard-coded left/right.

intermediate3 min read
  • css
  • logical-properties
  • i18n

Physical properties (margin-left, padding-top, border-right) assume horizontal TB LTR pages. Logical properties map to flow-relative edges: inline (text direction) and block (line stacking). When direction or writing-mode changes, logical spacing flips correctly without duplicate RTL stylesheets.

Docs: MDN logical properties, writing modes.

Block vs inline

Concept Horizontal LTR meaning Logical
Inline axis left ↔ right *-inline-*
Block axis top ↔ bottom *-block-*
Inline start left *-inline-start
Inline end right *-inline-end
Block start top *-block-start
Block end bottom *-block-end
.card {
  margin-block: 1rem 1.5rem;      /* top & bottom in LTR horizontal */
  margin-inline: auto;            /* horizontal centering */
  padding-inline: 1rem;
  border-inline-start: 4px solid var(--brand); /* “left border” in LTR, “right” in RTL */
  border-radius: 0.5rem;
}

Common mappings

/* Physical → logical */
margin-left → margin-inline-start
margin-right → margin-inline-end
padding-top → padding-block-start
padding-bottom → padding-block-end
top/right/bottom/left → inset-block-start / inset-inline-end / …

width → inline-size
height → block-size
min-width → min-inline-size
max-height → max-block-size
.sidebar {
  inline-size: 16rem;
  min-block-size: 100%;
}

.drawer {
  position: absolute;
  inset-block: 0;
  inset-inline-end: 0;
  inline-size: min(20rem, 100%);
}

Text alignment and floats

.caption {
  text-align: start; /* not left — respects direction */
}

.pull {
  float: inline-start;
}

Prefer start/end over left/right for text-align when supporting RTL.

Worked: badge on a card

<article class="card" dir="auto">
  <h2 class="card__title">فاتورة</h2>
  <span class="card__badge">New</span>
</article>
.card {
  position: relative;
  padding-inline: 1rem;
  padding-block: 1rem 1.25rem;
}

.card__badge {
  position: absolute;
  inset-block-start: 0.75rem;
  inset-inline-end: 0.75rem;
}

With dir="rtl", the badge sits on the opposite physical corner automatically.

When physical still wins

  • Drawing a map pin that must stay geographic east/west.
  • Aligning to a physical screenshot in a design tool for a fixed LTR-only canvas.
  • Some third-party widgets that only document physical props.

For product chrome, forms, spacing, and typography — logical first.

Interview out-loud

“Logical properties use block and inline axes instead of top/right/bottom/left. margin-inline-start follows the start of the text direction, so RTL flips automatically. I use inline-size/block-size for dimensions and inset-* for positioning. Physical properties remain for true physical layouts, but UI spacing should be logical in multilingual apps.”

Footguns

  1. Mixing left and inline-start on the same component inconsistently.
  2. Assuming logical props change language — they follow direction/writing-mode, not locale alone.
  3. Forgetting dir / lang on the document so nothing ever flips.
  4. Using width percentages inside vertical writing modes without retesting.
  5. Shorthands like margin: 1rem 2rem are still physical-order; prefer logical shorthands when intent is flow-relative.

Shorthands cheat sheet

margin-block: 1rem 1.5rem;   /* block-start block-end */
margin-inline: auto;         /* both inline sides */
padding-block: 0.5rem;
padding-inline: 1rem;
inset: 0;                    /* physical-ish shorthand for all sides — prefer logical longhands when direction matters */
inset-inline: 1rem;
border-inline-end: 1px solid var(--border);

Migrate incrementally: new components logical-only; touch legacy physical props when you open the file for RTL bugs. Lint rules (postcss-logical or stylelint plugins) help in multi-locale codebases.

Testing

  1. Toggle dir="rtl" on <html>.
  2. Check icons that imply direction (carets, back arrows).
  3. Check absolute badges (inset-inline-end).
  4. Check scroll containers and sticky sidebars.
  5. Verify screenshots in CI for LTR and RTL if you ship both.

Further reading

Related guides