Writing Modes and Direction
direction, writing-mode, and text-orientation — vertical text, RTL layout, and why logical properties beat left/right hardcoding.
- css
- writing-mode
- direction
- i18n
CSS layout has a writing mode (horizontal or vertical) and a direction (LTR/RTL). Together they define the block and inline axes. If your UI hard-codes margin-left and width for everything, vertical text and Arabic/Hebrew layouts become a second product.
Docs: MDN writing modes, writing-mode, direction.
direction and the dir attribute
<html lang="ar" dir="rtl">
. treemirror {
direction: ltr; /* isolate a code block inside RTL page */
unicode-bidi: isolate;
}
Prefer the HTML dir attribute for document/section direction so the whole stack (CSS, form controls, browser UA) agrees. CSS direction is useful for islands (code, phone numbers).
writing-mode
.spine-label {
writing-mode: vertical-rl;
text-orientation: mixed;
}
.timeline-year {
writing-mode: vertical-lr;
}
| Value | Lines progress | Common use |
|---|---|---|
horizontal-tb |
Top → bottom (default) | Most UI |
vertical-rl |
Right → left vertical | CJK spines, some vertical UI |
vertical-lr |
Left → right vertical | Some vertical timelines |
When writing mode is vertical, block axis is horizontal — width/height mental models flip; logical properties (inline-size, block-size) stay stable.
text-orientation
.vertical-nav {
writing-mode: vertical-rl;
text-orientation: upright; /* Latin letters upright stacked */
}
mixed (default) vs upright vs sideways changes how non-CJK characters sit in vertical lines.
Flex/grid interaction
.stack {
display: flex;
flex-direction: column; /* follows writing mode’s block direction? */
}
Flex row/column map to the current writing mode’s axes. Test vertical-rl toolbars explicitly — “row” may not match your physical intuition.
.toolbar {
display: flex;
gap: 0.5rem;
flex-direction: row; /* inline axis */
}
Logical properties are the pairing skill
.card {
margin-inline: auto;
padding-block: 1rem;
border-inline-start: 4px solid var(--brand);
inline-size: min(40rem, 100%);
}
Physical left/right CSS for spacing is the #1 RTL bug source.
Bidirectional isolation
<p dir="rtl">
السعر:
<span dir="ltr" class="price">$42.00</span>
</p>
.price {
unicode-bidi: isolate;
direction: ltr;
}
Keeps numbers and Latin product codes from scrambling under bidi rules.
Interview out-loud
“dir/direction set inline direction (LTR/RTL). writing-mode switches horizontal vs vertical typesetting and redefines block/inline axes. I prefer logical CSS properties so spacing flips correctly, isolate bidi islands for codes and prices, and retest flex/grid when using vertical modes.”
Footguns
- Absolute
leftpositioning for “start-side” icons. - Icons that imply direction (chevrons) not mirrored in RTL.
- Assuming screenshots of LTR for vertical CJK layouts.
- Forgetting form control quirks inside
direction: rtl. - Mixing
writing-modeon a parent without resetting children that shouldn’t inherit it.
Vertical UI navigation example
.spine-nav {
writing-mode: vertical-rl;
display: flex;
gap: 1rem;
height: 100%;
padding: 1rem 0.5rem;
}
.spine-nav a {
text-orientation: mixed;
text-decoration: none;
}
Keyboard order still follows DOM order — structure the HTML in the reading order you want, then apply writing-mode for visual presentation. Do not assume visual top equals first tab stop without checking.
Forms in RTL
Native inputs generally flip correctly under dir=rtl, but custom selects, icon adornments, and clear buttons often hard-code left offsets. Audit:
.field-icon {
inset-inline-start: 0.75rem;
}
.field-input {
padding-inline-start: 2.25rem;
}
Replace left/padding-left with logical equivalents. Number inputs and OTP fields may stay LTR islands with dir="ltr" for readability.
Related
- CSS logical properties
- lang attribute and i18n HTML
- Flexbox fundamentals
- Theming with custom properties
Further reading
Related guides
- CSS Logical Propertiesmargin-inline, padding-block, inset-inline-start — write layouts that flip with writing-mode and direction instead of hard-coded left/right.
- Accent Color and Form ControlsUse accent-color to theme native checkboxes, radios, ranges, and progress — what it paints, what it skips, and when to custom-style instead.
- Auto-fit vs Auto-fillauto-fit collapses empty tracks; auto-fill keeps them. Use both with minmax for responsive card grids without media-query soup.
- BEM Naming MethodologyBlock, Element, Modifier naming for CSS — how it limits specificity wars, what to name, and when utility or CSS modules beat strict BEM.
- blend-mode Basicsmix-blend-mode vs background-blend-mode — how pixels combine, isolation, and the stacking/readability traps in product UI.