# RTL and Bidi — Derived Rules > Derives from `domains/i18n/first-principles.md`. Covers P6 (Text > Direction is a Layout Primitive) and P7 (Layout Accommodates > Expansion). Referenced by `testing-i18n.md` (RTL coverage is an > e2e tier). Grounded in W3C i18n bidi authoring, UAX #9, and > `domains/uiux/accessibility.md`. ## Text Direction is a Layout Primitive (P6 Text Direction is a Layout Primitive) - RTL and bidi are first-class layout concerns, not a CSS afterthought. The layout is designed for both directions from the first commit, not retrofitted when an RTL locale ships. - Logical CSS properties over physical properties, always. The browser resolves logical → physical from the `dir` attribute; the code never has to. | Physical (LTR-only) | Logical (dir-aware) | Resolves to in RTL | |---------------------|---------------------|--------------------| | `margin-left` | `margin-inline-start` | `margin-right` | | `margin-right` | `margin-inline-end` | `margin-left` | | `padding-left` | `padding-inline-start` | `padding-right` | | `left: 0` | `inset-inline-start: 0` | `right: 0` | | `text-align: left` | `text-align: start` | `text-align: right` | | `float: left` | use flexbox/grid + `inline-start` where supported | mirrored | - The `dir` attribute is set on the document root (``) and on subtrees whose direction differs from the document (`` for an embedded Latin run). `dir` is the contract the bidi algorithm (UAX #9) reads; do not fake direction with `text-align` alone. ## The Bidi Algorithm (UAX #9) - The Unicode bidi algorithm resolves inline reordering of mixed- direction runs. The browser applies it; the author's job is to mark direction correctly, not to reorder by hand. - A string like `"The price is 15 USD"` in an RTL context renders with the Latin run `"15 USD"` in LTR within the RTL line — the algorithm handles it *if* the container's `dir` is set. Without `dir`, numbers and Latin fragments drift to the wrong edge. - `dir="auto"` on a container infers direction from the first strong directional character of its content — useful for user-generated content whose direction is unknown. `dir="auto"` is not a replacement for `dir="rtl"` on a known-RTL document. ## Mirroring (Icons, Controls, Diagrams) - Direction-aware icons mirror in RTL: a "back" arrow pointing left in LTR points right in RTL. A "refresh" circular arrow does not mirror. The rule: icons that imply direction mirror; icons that imply time or rotation do not. - Use `[dir="rtl"]` selectors or logical icon variants — never `transform: scaleX(-1)` as a one-off hack scattered across components. Centralize the mirroring rule (a token, a component prop) so it is auditable. - Numbers do not mirror. `"15 USD"` in an RTL line is still `"15 USD"` left-to-right inside the bidi run; mirroring it to `"DSU 51"` is a correctness violation. - Diagrams and flowcharts: a left-to-right process flow in LTR is a right-to-left flow in RTL. Decide per diagram whether the flow mirrors (most do) or is direction-neutral (some scientific schematics). ## Layout Accommodates Expansion (P7 Layout Accommodates Expansion) - Translated text expands. German is ~30% longer than English; Japanese is often shorter but taller; RTL mirroring shifts every visual anchor. Layouts are flexible: - No fixed pixel widths on translatable text containers. - No `white-space: nowrap` on translatable strings. - No `text-overflow: ellipsis` without a `title` carrying the full string. - Buttons sized to fit their longest locale variant, not the source. - A layout that breaks at +30% width is a layout that is wrong for most of the world's locales. Designing for the worst case up front is cheaper than reworking every screen when the first long-form locale ships. ## Common Pitfalls | Pitfall | Why it breaks | Fix | |---------|---------------|-----| | `margin-left` everywhere | In RTL the start is the right; `margin-left` leaves the right side unstyled | `margin-inline-start` | | `text-align: left` for "default" alignment | In RTL the default is right; `left` pins content to the wrong edge | `text-align: start` | | Icons hardcoded to LTR orientation | "Back" arrow points the wrong way in RTL | Mirror direction-implying icons via `[dir="rtl"]` | | Numbers mirrored with the layout | Numbers are LTR inside RTL; mirroring produces garbage | Leave number runs LTR; the bidi algorithm handles embedding | | Fixed `width: 120px` on a button | German button label overflows and truncates | `min-width` + `max-width` + flex; let content size | | `position: absolute; left: 0` | Pins to the physical left in both directions | `inset-inline-start: 0` | | Fake direction with `text-align` only | The bidi algorithm reads `dir`, not `text-align`; mixed runs reorder wrong | Set `dir` on the container | ## What Violates RTL/Bidi Discipline | Violation | Principle | |-----------|-----------| | A layout with no `dir` attribute, assuming LTR | P6 Text Direction is a Layout Primitive | | `margin-left` / `left: 0` / `text-align: left` throughout | P6 Text Direction is a Layout Primitive | | A "back" arrow that points left in the RTL build | P6 Text Direction is a Layout Primitive | | Numbers mirrored to read right-to-left | P6 Text Direction is a Layout Primitive | | `width: 100px` on a text container that overflows in German | P7 Layout Accommodates Expansion | | `white-space: nowrap` on a translated label | P7 Layout Accommodates Expansion | | `dir` faked with `text-align` and no `dir` attribute | P6 Text Direction is a Layout Primitive | | No RTL build until the first RTL locale ships | P6 Text Direction is a Layout Primitive | ## Relationship to Other Domains - `domains/uiux/accessibility.md` — RTL support is an accessibility concern for non-Latin-script users; WCAG 2.1 AA requires that direction be set correctly. - `domains/uiux/components.md` — components are built with logical properties so they survive direction and expansion without per- locale overrides. - `domains/i18n/testing-i18n.md` — RTL coverage is an e2e-tier test; pseudo-locale mirroring surfaces direction bugs early. - `domains/i18n/locale-resources.md` — the `dir` is part of the locale's metadata, carried alongside the resource bundle.