Physical CSS properties — width, margin-left,
border-top, top — assume horizontal text flowing left to
right. They bake a specific writing system into the stylesheet. Logical properties
replace physical directions with flow-relative references:
inline (the axis text runs along) and block (the axis paragraphs
stack along). A layout written with logical properties works correctly in Arabic
(right to left), Japanese vertical text (top to bottom), and English — without a
single overriding rule.
Baseline 2023. All logical properties and values covered in this
chapter are supported in every evergreen browser. Writing modes have been supported
since 2016. sideways-rl and sideways-lr are supported
everywhere except Safari 17 and below — use vertical-rl with
rotate as a fallback where needed.
1. The Inline and Block Axes
Every writing mode defines two axes. Inline is the direction
text flows within a line. Block is the direction lines stack.
The mapping of these axes to physical directions depends on the active writing mode
and text direction.
writing-mode: horizontal-tb
direction: ltr
←→
inline axis
↑↓
block axis
inline-size = width block-size = height
writing-mode: vertical-rl
Japanese / CJK
↑↓
inline axis
→←
block axis (RTL)
inline-size = height block-size = width
The key insight: inline-size means "the size along the inline axis."
In horizontal writing that is the width. In vertical writing that is the height.
Write inline-size: 300px once and it works correctly in both.
2. Writing Modes
horizontal-tb
Hello World こんにちは
vertical-rl
Hello World こんにちは
vertical-lr
Hello World こんにちは
sideways-rl
Hello World
/* writing-mode values */writing-mode:horizontal-tb;/* Default. Text flows left→right (or right→left), lines stack top→bottom. *//* Used by: Latin, Arabic, Hebrew, Greek, Cyrillic, Thai, Devanagari, etc. */writing-mode:vertical-rl;/* Text flows top→bottom. Lines (columns) stack right→left. *//* Used by: Traditional Chinese, Japanese, Korean (tategumi / vertical layout). *//* The 'rl' means new columns appear to the LEFT of earlier columns. */writing-mode:vertical-lr;/* Text flows top→bottom. Lines stack left→right. *//* Used by: Mongolian script (traditional). */writing-mode:sideways-rl;/* Entire glyphs are rotated 90° clockwise. Used for rotated Latin labels. *//* Unlike vertical-rl, letters themselves are rotated — not individual glyphs. */writing-mode:sideways-lr;/* Entire glyphs rotated 90° counter-clockwise. *//* Axis mapping per writing mode *//* inline-axis block-axis *//* horizontal-tb: horizontal → vertical ↓ *//* vertical-rl: vertical ↓ horizontal ← *//* vertical-lr: vertical ↓ horizontal → *//* sideways-rl: vertical ↓ horizontal ← */
text-orientation
/* text-orientation — applies only in vertical writing modes *//* Controls how individual glyphs are oriented within a vertical line */text-orientation:mixed;/* Default. CJK characters stand upright; Latin/digits are rotated 90° */text-orientation:upright;/* All characters (including Latin) stand upright */text-orientation:sideways;/* All characters are rotated 90° as a group (same as sideways-rl) *//* Practical use: chapter numbers in a Japanese sidebar */.chapter-number{writing-mode:vertical-rl;text-orientation:upright;/* digits stand upright in the vertical line */}
3. Physical → Logical Property Mapping
Physical property
Logical equivalent
Notes
Sizing
width
inline-size
Size along the inline axis
height
block-size
Size along the block axis
min-width / max-width
min-inline-size / max-inline-size
min-height / max-height
min-block-size / max-block-size
Margin
margin-top
margin-block-start
margin-bottom
margin-block-end
margin-left
margin-inline-start
RTL: maps to right
margin-right
margin-inline-end
RTL: maps to left
margin: top right bottom left
margin-block: start end; margin-inline: start end
Two separate shorthands
Padding
padding-top
padding-block-start
padding-bottom
padding-block-end
padding-left
padding-inline-start
padding-right
padding-inline-end
Border
border-top
border-block-start
Shorthand: width style color
border-bottom
border-block-end
border-left
border-inline-start
The accent border pattern
border-right
border-inline-end
border-top-width
border-block-start-width
Longhand for width/style/color
Position / Inset
top
inset-block-start
bottom
inset-block-end
left
inset-inline-start
right
inset-inline-end
top:0; right:0; bottom:0; left:0
inset: 0
inset is the shorthand for all four
Border Radius
border-top-left-radius
border-start-start-radius
block-start + inline-start corner
border-top-right-radius
border-start-end-radius
block-start + inline-end corner
border-bottom-left-radius
border-end-start-radius
block-end + inline-start corner
border-bottom-right-radius
border-end-end-radius
block-end + inline-end corner
Text and Float
text-align: left
text-align: start
start/end are logical values for text-align
text-align: right
text-align: end
float: left
float: inline-start
float: right
float: inline-end
resize: horizontal
resize: inline
resize: vertical
resize: block
overflow-x / overflow-y
overflow-inline / overflow-block
4. Shorthands and Two-Value Syntax
/* Axis shorthands — apply to start and end together */margin-block:1rem;/* both block-start and block-end: 1rem */margin-block:1rem 2rem;/* block-start: 1rem; block-end: 2rem */margin-inline:auto;/* centre horizontally (like margin: 0 auto but logical) */margin-inline:1rem 3rem;/* start: 1rem; end: 3rem */padding-block:0.5rem 1rem;padding-inline:1.5rem;border-block:1px solid var(--border);/* top and bottom borders */border-block-start:3px solid var(--accent);/* top border (or left in vertical) */border-inline-start:3px solid var(--accent);/* left border (or top in vertical) */inset:0;/* top:0; right:0; bottom:0; left:0 */inset:10px 20px;/* block:10px; inline:20px */inset-block:0;/* top:0; bottom:0 */inset-inline:0;/* left:0; right:0 — stretch horizontally *//* Common pattern: stretch an element to fill its container */.overlay{position:absolute;inset:0;/* replaces top:0;right:0;bottom:0;left:0 */}/* Centre with margin-inline: auto */.container{max-inline-size:1200px;margin-inline:auto;/* perfectly centred in any writing mode */}
5. direction and RTL Layouts
/* direction controls text direction within horizontal-tb writing mode */direction:ltr;/* default — inline-start = left, inline-end = right */direction:rtl;/* Arabic/Hebrew — inline-start = right, inline-end = left *//* In practice: set direction via HTML lang attribute and the browser user-agent *//* Or use dir="rtl" on the HTML element or a section *//* CSS direction should match the HTML dir attribute — don't set them independently *//* The html element approach *//* <html lang="ar" dir="rtl"> *//* Logical properties respond automatically to dir="rtl" */.card{border-inline-start:3px solid var(--accent);padding-inline-start:1rem;/* LTR: left border and left padding *//* RTL: right border and right padding — no override needed */}/* Physical properties require explicit RTL overrides */.card--physical{border-left:3px solid var(--accent);padding-left:1rem;}[dir="rtl"] .card--physical{border-left:none;border-right:3px solid var(--accent);padding-left:0;padding-right:1rem;/* Four extra declarations for what logical did in zero */}
LTR — dir="ltr"
📄
Article Title
border-inline-start creates a left accent border in LTR layout. padding-inline-start adds left padding.
RTL — dir="rtl"
📄
عنوان المقال
نفس خصائص CSS تماماً — ينعكس تلقائياً إلى اليمين في وضع RTL.
Both panels above use exactly the same CSS — border-inline-start and
padding-inline-start. In LTR the accent appears on the left; in RTL
it appears on the right. Zero extra CSS was written for the RTL version.
6. Logical Border Radius
/* Logical border-radius — format: border-{block-position}-{inline-position}-radius *//* In horizontal-tb LTR: */border-start-start-radius:8px;/* top-left */border-start-end-radius:8px;/* top-right */border-end-start-radius:8px;/* bottom-left */border-end-end-radius:8px;/* bottom-right *//* In horizontal-tb RTL: */border-start-start-radius:8px;/* top-right (inline-start = right in RTL) */border-start-end-radius:8px;/* top-left *//* In vertical-rl: */border-start-start-radius:8px;/* top-right (block-start = top, inline-start = right in vertical-rl) *//* Practical use: tab component that rounds the correct corners regardless of mode */.tab{border-start-start-radius:6px;border-start-end-radius:6px;border-end-start-radius:0;border-end-end-radius:0;/* LTR: rounded top-left and top-right → correct horizontal tab shape *//* vertical-rl: rounded top-right and bottom-right → correct vertical tab shape */}
7. Flexbox, Grid, and Logical Properties
/* Flexbox already uses logical concepts internally *//* flex-direction: row aligns items along the inline axis *//* flex-direction: column aligns items along the block axis *//* This means flex row items reverse order correctly when dir="rtl" *//* However: explicit margins and padding still need logical properties */.nav-item{margin-inline-end:1rem;/* gap between items: right in LTR, left in RTL */}/* CSS Grid: column and row always mean physical axes *//* But gap, padding, and margin on grid children should use logical properties */.grid-item{padding-inline:1rem;padding-block:0.75rem;}/* Writing mode changes in flexbox — the axis swaps */.vertical-nav{writing-mode:vertical-rl;display:flex;/* flex-direction: row still means "along the inline axis" *//* but in vertical-rl the inline axis is vertical *//* so items stack vertically by default — no flex-direction change needed */}/* Scroll bar position with logical overflow */.scrollable{overflow-block:auto;/* vertical scrollbar in horizontal writing */overflow-inline:hidden;/* no horizontal overflow */}/* text-align: start / end — logical values */.caption{text-align:start;/* left in LTR, right in RTL */}.label{text-align:end;/* right in LTR, left in RTL */}
8. Vertical Text Patterns
/* ── Vertical navigation labels (sideways) ─────────────────── */.sidebar-label{writing-mode:sideways-rl;white-space:nowrap;text-align:center;}/* Fallback for Safari < 18 using rotate */.sidebar-label{transform:rotate(90deg);white-space:nowrap;}@supports(writing-mode:sideways-rl) {.sidebar-label{transform:none;writing-mode:sideways-rl;}}/* ── Japanese magazine-style layout ────────────────────────── */.article--tategumi{writing-mode:vertical-rl;block-size:80vh;/* height of the reading column */overflow-inline:auto;/* horizontal scroll (now the "block" axis) */text-align:justify;hanging-punctuation:force-end;/* Japanese punctuation rules */}/* ── Inline-size and block-size swap in vertical mode ────────── */.vertical-card{writing-mode:vertical-rl;inline-size:200px;/* this is the HEIGHT in vertical mode */block-size:auto;/* this is the WIDTH — expands with content */}/* ── Chapter headings with combined writing-mode + rotation ─── */.chapter-heading{writing-mode:vertical-lr;text-orientation:upright;font-variant-numeric:tabular-nums;/* Digits stand upright in vertical text */}
9. When to Use Logical vs Physical Properties
Default to logical. For any new CSS you write — sizing, spacing,
borders, positioning — use logical properties. They work correctly across all
writing modes and directions without any extra overrides. The cognitive overhead
of mapping start/end to left/right is minimal and the payoff in
internationalisation correctness is large.
/* When physical properties ARE correct *//* 1. Truly physical UI that must not flip *//* A progress bar that fills left-to-right regardless of document direction */.progress-fill{width:60%;/* must be physical — filling left-to-right is the metaphor */left:0;/* must anchor to the physical left edge */}/* 2. Geometric/decorative elements that are direction-agnostic */.avatar{width:48px;/* a circle is a circle — physical or logical makes no difference */height:48px;}/* 3. Fixed-position elements anchored to physical screen edges */.corner-badge{position:fixed;top:1rem;/* physical screen corner — intentionally not logical */right:1rem;}/* 4. Images and media with inherent physical direction */.hero-image{width:100%;/* images are physical — width/height are correct */height:auto;}/* The rule of thumb: *//* If the property describes a relationship to the READING DIRECTION → logical *//* If the property describes a geometric fact about PHYSICAL SPACE → physical */
Mixing logical and physical on the same property causes conflicts.margin-left and margin-inline-start both set the same
computed value in LTR horizontal writing. If you set both, the one that appears
later in the stylesheet wins (source order). Avoid mixing them — pick one model
per component and be consistent.
Chapter Summary
Concept
Key point
Inline axis
The direction text flows within a line. Horizontal in horizontal-tb (LTR/RTL), vertical in vertical-rl/vertical-lr. inline-size, margin-inline, padding-inline, border-inline, inset-inline.
Block axis
The direction lines stack. Vertical in horizontal-tb, horizontal in vertical writing modes. block-size, margin-block, padding-block, border-block, inset-block.
start / end
Flow-relative start and end of an axis. In LTR: inline-start = left, inline-end = right. In RTL: reversed. In vertical-rl: block-start = right.
writing-mode
horizontal-tb (default), vertical-rl (Japanese), vertical-lr (Mongolian), sideways-rl/lr (rotated Latin labels). Changes which axis is inline and which is block.
ltr (default) or rtl. Controls inline-start/end mapping within horizontal-tb. Set via HTML dir attribute; match with CSS direction. Never set direction independently of dir.
inset
Shorthand for all four position offsets (top/right/bottom/left). inset: 0 replaces the four-property stretch pattern. inset-block and inset-inline for axis shorthands.
Logical border-radius
border-start-start-radius etc. First word = block position (start/end), second word = inline position. Useful for tabs and dialog corners that must work in all writing modes.
text-align: start/end
Logical values for text-align. start = left in LTR, right in RTL. Prefer over text-align: left for internationalised content.
When to use physical
Truly physical UI (progress bars, fixed corners, geometric shapes, images). Use physical when the meaning is about screen geometry, not reading direction.
Exercises
Full logical property audit: Take a component you've built previously (card, nav, or form). Replace every physical property with its logical equivalent: width → inline-size, margin-left → margin-inline-start, border-top → border-block-start, top/left → inset-block-start/inset-inline-start, text-align: left → text-align: start. Wrap the component in a div dir="rtl" and confirm the layout mirrors correctly without any additional CSS.
Writing mode sidebar label: Create a fixed-width sidebar. Add section labels that display as vertical rotated text using writing-mode: sideways-rl. Implement the @supports fallback using transform: rotate(90deg) for browsers without support. Confirm that inline-size on the label element controls its height (because the writing mode is vertical) and block-size controls its width.
RTL-safe card component: Build a card with an icon on the start side, a title and body in the remaining space, and a "learn more" arrow link on the end side. Use only logical properties. Toggle between dir="ltr" and dir="rtl" on the card's container and verify: icon moves to the right, text aligns correctly, and the arrow points the right way (use inline-end margin on the arrow). Document in a comment which property would break if you had used its physical equivalent.
Vertical tab navigation: Create a tab component where the tab list runs vertically along one side. Use writing-mode: vertical-rl on the tab labels so they read top-to-bottom. Apply border-start-start-radius and border-end-start-radius to the active tab to give it the correct rounding for a vertical tab shape. Then switch the whole component to dir="rtl" and observe which border-radius corners flip automatically.
Container query + logical: Combine what you've learned in Chapters 2 and 6: create a card with container-type: inline-size. Write container queries that change the card's layout at a certain inline-size threshold. Inside the card, use only logical properties for all spacing and borders. Confirm the card works correctly when placed inside a writing-mode: vertical-rl container — the container query threshold should now be relative to the vertical axis since that is the inline axis in vertical writing.
Next: Chapter 7 — Advanced Color.
The oklch() color space — why it produces perceptually uniform color scales,
how to build a complete design token palette with it, the color-mix()
function, wide-gamut colors with display-p3,
light-dark(), relative color syntax, and @media (color-gamut)
for progressive enhancement.