Section 3 · Responsive and Robust
Logical Properties and RTL
17 min4 min readCode playground
Lesson at a glance
Replace physical properties with logical ones so the schedule page works in right-to-left languages like Arabic, and handle the few things that don’t flip on their own.
- 1Inline and block axes
- 2Converting the session card
- 3Sizes are logical too
- 4What doesn’t flip by itself
- 5Arabic typography in two rules
- 6Your turn
No video here — just the real thing: a written lesson, runnable examples, a quiz and practice.
Waypoint is adding an Arabic edition of the schedule for its Cairo satellite event. The HTML change is one attribute, <html lang="ar" dir="rtl">, and the first load was half right. The session list mirrored correctly, because it’s a flex row. But every card had its colored border on the left, which in Arabic is the end of the line, the time column had its spacing on the wrong side, and the “Details →” arrow pointed backwards.
The layout was written in physical directions: left, right, top, bottom. Text doesn’t flow in physical directions; it flows from a start to an end.
Inline and block axes
Logical properties name sides by the flow of text:
- The inline axis is the direction text runs within a line. In English it’s left to right; in Arabic, right to left.
- The block axis is the direction lines stack: top to bottom in both.
Every physical property has a logical twin:
| Physical | Logical |
|---|---|
margin-left, margin-right |
margin-inline-start, margin-inline-end |
padding-top, padding-bottom |
padding-block-start, padding-block-end |
border-left |
border-inline-start |
left, right, top |
inset-inline-start, inset-inline-end, inset-block-start |
width, height |
inline-size, block-size |
text-align: left |
text-align: start |
There are shorthands too: margin-inline: 1rem 2rem sets start and end, padding-block: 1rem sets both block sides, and inset-inline: 0 replaces left: 0; right: 0.
Converting the session card
/* Before: physical */
.session-card {
border-left: 6px solid var(--track-color);
padding: 12px 8px 12px 20px;
text-align: left;
}
.session-card .time { margin-right: 12px; }
/* After: logical */
.session-card {
border-inline-start: 6px solid var(--track-color);
padding-block: 12px;
padding-inline: 20px 8px;
text-align: start;
}
.session-card .time { margin-inline-end: 12px; }In English the result is pixel-identical. In Arabic, the border moves to the right, where lines start, and the spacing follows. You write the card once.
Flexbox and grid already think this way. flex-direction: row, justify-content: flex-start and grid column 1 all follow the inline direction, so a flex row mirrors in RTL without any extra CSS. That’s why Waypoint’s list worked on the first try.
Sizes are logical too
inline-size and block-size are the logical names for width and height, and max-inline-size: 40ch is the logical version of the readable-width cap from the intrinsic sizing lesson. In Arabic and English they behave exactly like width and height, because both scripts write horizontally. They only differ in vertical writing modes, such as writing-mode: vertical-rl for Japanese or a rotated table header, where the inline axis runs top to bottom. Using them costs nothing today and means a component survives a writing-mode change later. A sensible team rule is: logical properties for everything that relates to text flow, physical ones only for things that are physical by nature, like a shadow that should always fall downward.
What doesn’t flip by itself
A few things are physical by nature and need an explicit RTL rule:
- Transforms:
translateX(8px)always moves right. - Directional icons: arrows, “next” chevrons, a progress indicator’s direction.
- Background positions and shadows:
box-shadow: 4px 0 …always throws its shadow to the right.
The :dir() pseudo-class matches the element’s computed direction, inherited from the nearest dir attribute:
.more .arrow {
display: inline-block;
margin-inline-start: 4px;
}
.more .arrow:dir(rtl) {
scale: -1 1; /* mirror horizontally */
}:dir(rtl) is better than [dir="rtl"] .arrow because it doesn’t care where the attribute lives or whether a nested element switches back to LTR. It has been Baseline since December 2023 and widely available since mid-2026.
Not every icon should flip. A play button, a clock, or a checkmark means the same in both directions; a “back” arrow or a slider’s direction doesn’t. Ask a native reader when you’re unsure.
Arabic typography in two rules
First, never apply letter-spacing to Arabic. Arabic letters join to their neighbours; spacing them apart breaks the joins and makes words hard to read. If your design system adds tracking to uppercase labels, reset it with :lang(ar) { letter-spacing: 0; }. Second, Arabic often needs a slightly larger font size and line height than Latin text at the same visual weight. Set those per language with :lang(ar) rather than per component.
Your turn
The exercise card is written with physical properties. Convert it to logical ones and flip the arrow in RTL. The checks render it in both directions. Next, you’ll replace the card’s hex colors with oklch() and build track palettes with color-mix().
Key takeaways
- Logical properties describe layout relative to the text direction:
margin-inline-startis the left margin in English and the right margin in Arabic. - Setting
dir="rtl"on the root mirrors flexbox, grid and logical properties automatically; physical properties likepadding-leftstay where they are. - Transforms, background positions, shadows and directional icons don’t flip, so handle them explicitly with
:dir(rtl). - Never add
letter-spacingto Arabic text; it breaks the joins between letters.