Files
atelier/domains/i18n/rtl-bidi.md
T
Jon Chery 9ebc9c8868 docs(milestone): complete v0.3 — GitOps+Operators/AI-ML/i18n/Compliance
---ci---
project: atelier
phase: 6
milestone: v0.3
status: complete
requirements:
  covered: [ATELIER-60..91]
  partial: []
---/ci---
2026-08-05 03:45:38 +00:00

6.2 KiB

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 (<html dir="rtl">) and on subtrees whose direction differs from the document (<span dir="ltr"> 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.