skip to content

Logical Properties and Writing Modes

Block and inline logical directions replace top/right/bottom/left so a layout mirrors itself in RTL or vertical writing modes without a second stylesheet. Increasingly asked now that internationalization is table stakes.

part ofCSSoverview, primer and where to startread it →
on this pageshow

questions

6

In CSS logical properties, what do the block and inline axes mean, and which physical sides do margin-block-start and margin-inline-start map to in a horizontal-tb, left-to-right document?

level: middleimportance: must knowfreq 55%

answer

  1. text direction versus line stacking
  2. start and end, not left and right
  3. writing-mode plus direction decide the mapping
  4. horizontal-tb ltr: inline-start is left
  5. one stylesheet mirrors itself for RTL

basics

~10 s

The inline axis runs along the text direction; the block axis is the direction lines stack. In writing-mode: horizontal-tb with direction: ltr, margin-inline-start resolves to margin-left and margin-block-start resolves to margin-top.

solid answer

~50 s

Logical properties describe a box relative to text flow instead of the screen. The **inline axis** is the one text runs along inside a line, so its ends are `inline-start` (where a line begins) and `inline-end`. The **block axis** is the one lines and block boxes stack along, with `block-start` and `block-end`. In the default `writing-mode: horizontal-tb` with `direction: ltr`, inline-start is left, inline-end is right, block-start is top, block-end is bottom — so `margin-inline-start` is `margin-left` and `margin-block-start` is `margin-top`. Flip to `direction: rtl` and only the inline ends swap; switch to a vertical writing mode and the two axes trade places entirely. The mapping is decided by the element's own computed `writing-mode` and `direction`, both of which inherit, which is why one logical stylesheet mirrors itself for Arabic or Hebrew without a second build.

code

css · 11 lines
css
.quote {
  border-inline-start: 3px solid #888;
  padding-inline-start: 1rem;
  margin-block: 1.5rem;
  margin-inline: auto;
  max-inline-size: 60ch;
}

[dir="rtl"] .quote {
  /* nothing needed: the bar, padding and max width all mirror themselves */
}

go deeper

for a junior

Learn the vocabulary: inline runs along the text, block is how lines stack, and start/end replace left/right. Be able to translate margin-top and margin-left into their logical names for a normal English page.

for a middle

Be ready to explain that writing-mode and direction, both inherited, resolve the mapping at style time, and to walk through what changes when direction flips versus when writing-mode flips.

for a senior

Show that you know the boundary of the abstraction: box properties mirror, but shadows, transforms, gradients and the four-value physical shorthands do not, so an RTL launch still needs a visual audit.

for a principal

Own the policy question — whether logical properties are the mandated default in the design system, how legacy and third-party CSS is handled, and what a lint rule or codemod enforces so the codebase does not drift into a physical/logical mix.

## The problem logical properties solve Physical CSS names four fixed sides — `top`, `right`, `bottom`, `left` — anchored to the screen. Text is not anchored to the screen. English runs left-to-right in lines that stack downward; Arabic and Hebrew run right-to-left; Japanese can run top-to-bottom in lines that stack right-to-left. A stylesheet written in physical sides encodes exactly one of those arrangements, so shipping a right-to-left locale historically meant generating a second, mirrored stylesheet. Logical properties re-describe the box in terms of *flow*: not "the left side" but "the side where a line of text starts". ## The two axes and their four ends - **Inline axis** — the axis text advances along within a line. Its ends are `inline-start` (where a line begins) and `inline-end` (where it ends). - **Block axis** — the axis along which lines, and block-level boxes, stack. Its ends are `block-start` (the side the first line sits against) and `block-end`. In the default `writing-mode: horizontal-tb` (horizontal lines, stacking top-to-bottom) with `direction: ltr`: | Logical | Physical | |---|---| | block-start | top | | block-end | bottom | | inline-start | left | | inline-end | right | Set `direction: rtl` and only the inline ends swap: inline-start becomes the right side. Set `writing-mode: vertical-rl` and the axes exchange orientation — the inline axis becomes vertical and the block axis horizontal. ```css .card { margin-block-start: 1rem; /* horizontal-tb ltr -> margin-top */ margin-block-end: 2rem; /* -> margin-bottom */ margin-inline-start: 1rem; /* -> margin-left, but margin-right in rtl */ margin-inline-end: 1rem; /* -> margin-right, but margin-left in rtl */ } ``` ## The property families Every physical box property has a flow-relative counterpart: - **Margins**: `margin-block-start/end`, `margin-inline-start/end`, plus the two-value shorthands `margin-block` and `margin-inline` (`margin-inline: 1rem 2rem` sets start then end). - **Padding**: `padding-block`, `padding-inline`, and the four long-hands. - **Borders**: `border-block-start`, `border-inline-end`, and the component long-hands `border-inline-start-width`, `-style`, `-color`. - **Offsets**: `inset-block-start`, `inset-inline-end`, and the shorthands `inset-block`, `inset-inline`. - **Sizes**: `inline-size` and `block-size`, with `min-inline-size`, `max-inline-size`, `min-block-size`, `max-block-size`. - **Corners**: `border-start-start-radius`, `border-start-end-radius`, `border-end-start-radius`, `border-end-end-radius` — the first word is the block end, the second the inline end. - **Values, not just properties**: `text-align: start` and `text-align: end` are the flow-relative counterparts of `left` and `right`. ## What decides the mapping The element's own computed `writing-mode` and `direction`. Both are inherited properties, so setting them once near the root cascades to the whole subtree, and setting them on one branch (a quoted Arabic passage inside an English page, say) makes only that branch mirror. Nothing in the resolution needs a build step: the browser resolves the mapping at style time, which is why a single logical stylesheet is correct for both directions simultaneously on the same page. Sizing behaves exactly like `width`/`height` in every other respect — `inline-size` honours `box-sizing` the same way, for example. Logical is a naming layer over the same box, not a different box. ## Practical wins - `margin-inline: auto` centres a block along the text axis — the direction-agnostic replacement for `margin: 0 auto`. - `padding-inline: 1rem` is the standard gutter that stays a gutter in both directions. - `border-inline-start: 3px solid` is the blockquote bar that moves to the correct side automatically. ## Pitfalls worth naming 1. **"Inline means horizontal"** — only in horizontal writing modes. The word refers to text flow, not to the screen. 2. **The four-value shorthands are still physical.** `margin: 1rem 2rem 3rem 4rem` and `inset: 0 1rem` remain top/right/bottom/left order; there is no shipped four-side logical `margin` shorthand, so mixed codebases still contain physical shorthands. 3. **Non-box properties do not follow.** `box-shadow` offsets, `transform: translateX()`, `background-position: left`, and gradient direction keywords stay physical no matter how logical your margins are. 4. **Mixing physical and logical declarations for the same side** creates order-dependent cascade behaviour rather than a clean override. Logical box properties are supported across all current browsers, so on a greenfield stylesheet there is no compatibility argument for preferring the physical names.

  • If only direction changes from ltr to rtl, which logical sides move and which stay put?
    Only the inline ends move: `inline-start` swaps from the left side to the right side and `inline-end` swaps the other way. The block axis is untouched, so `block-start` remains the top and `block-end` the bottom, because `direction` describes text flow within a line, not how lines stack.
  • Is there a logical equivalent of the four-value margin shorthand?
    No shipped one. `margin` with multiple values is still physical top/right/bottom/left order. The logical shorthands are two-value and per-axis: `margin-block: 1rem 2rem` and `margin-inline: 1rem 2rem`, each taking start then end. In practice you write two declarations instead of one, which is the small tax for direction independence.
  • How do the logical border-radius corner names work?
    Each is `border-<block-end>-<inline-end>-radius`. `border-start-start-radius` is the corner at block-start and inline-start — the top-left corner in horizontal-tb ltr, the top-right in rtl. The other three are `border-start-end-radius`, `border-end-start-radius` and `border-end-end-radius`. They matter for asymmetric shapes like chat bubbles that must flip with direction.

saying these in an interview costs you the question

  • Says the inline axis is always the horizontal one
  • Claims block-start is the top regardless of writing-mode
  • Thinks logical properties are pure aliases with no mapping logic
  • Believes margin: 1rem 2rem 3rem 4rem is already flow-relative
  • Assumes logical properties also mirror shadows and transforms

context

open as a page

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?

level: seniorimportance: must knowfreq 34%

basics

~20 s

The 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.

open as a page

In CSS, how does inline-size differ from width, and when do the two set different physical dimensions?

level: juniorimportance: should knowfreq 40%

basics

~10 s

width always sizes the horizontal dimension, while inline-size sizes the box along the text axis. They are identical in horizontal writing modes and swap roles in vertical ones, where inline-size controls height instead.

open as a page

What does writing-mode: vertical-rl do to an element's block and inline axes, and which physical dimension does inline-size then control?

level: middleimportance: should knowfreq 28%

basics

~10 s

writing-mode: vertical-rl makes text run top-to-bottom in lines that stack right-to-left. The inline axis becomes vertical and the block axis horizontal, so inline-size sets the element's rendered height and block-size its width.

open as a page

After switching a page to direction: rtl, which CSS declarations keep pointing the same physical way even when every box property has been converted to logical form?

level: seniorimportance: should knowfreq 30%

basics

~20 s

Logical properties only cover the box: margins, padding, borders, insets, sizes and corner radii. Shadows, transforms, background-position keywords, gradient directions and physical text-align values stay fixed to the screen and need a manual right-to-left pass.

open as a page

For a product adding an Arabic locale, how would you choose between refactoring the stylesheet to CSS logical properties and generating a mirrored stylesheet with a build-time RTL post-processor such as rtlcss or CSSJanus?

level: principalimportance: should knowfreq 20%

basics

~20 s

Logical properties resolve per element at style time and need one stylesheet, but require touching every rule. A post-processor flips physical declarations at build time with no refactor, yet ships two bundles and cannot handle both directions on one page.

open as a page