---
title: 'Right-to-Left Languages'
source: 'https://academia.sh/en/courses/accessible-patterns/right-to-left-languages'
course: 'Accessible Component Patterns'
language: en
updated: '2026-08-19T05:19:53+00:00'
license: 'CC BY-SA 4.0'
---

# Right-to-Left Languages

The style layer's counterpart to a direction change, logical properties taking precedence over physical ones, icon mirroring tied to a criterion, isolation in bidirectional text, and keyboard contracts written by flow direction.

Every example in the previous lesson used languages written left to right. When the
catalog opens into a right-to-left language, what changes is not just the text's
direction: a record card's border moves to the opposite side, the filter panel relocates
to the other edge of the screen, a forward arrow flips, and a clock icon stays exactly
as it was.

This lesson treats the direction change not as a translation problem but as an
**architectural decision**. The decision is made in the style layer, and its scale is
how many declarations are affected by the direction change.

## Logical, Not Physical, Axes

Left and right are the screen's fixed directions. Start and end depend on **flow**: in a
left-to-right layout, start is on the left; in a right-to-left layout, it is on the
right. A component whose border is written as "left" stays on the left even after the
direction flips, and the layout breaks; the same border written as "inline start"
follows the flow.

The computation below lists declarations gathered from catalog components alongside
their physical and logical counterparts, and counts how many are direction-sensitive.

```js
// direction.mjs — direction-sensitive layout: logical properties, mirrored and unmirrored elements

// 1. Physical properties' logical counterparts.
const MAPPING = {
  "margin-left": "margin-inline-start",
  "margin-right": "margin-inline-end",
  "padding-left": "padding-inline-start",
  "padding-right": "padding-inline-end",
  "border-left": "border-inline-start",
  "text-align: left": "text-align: start",
  "left": "inset-inline-start",
  "width": "inline-size",
  "margin-top": "margin-block-start",
  "border-bottom": "border-block-end",
};
// Declarations gathered from catalog components.
const DECLARATIONS = [
  { component: "record-card", declaration: "padding-left" },
  { component: "record-card", declaration: "border-left" },
  { component: "record-card", declaration: "margin-top" },
  { component: "filter-panel", declaration: "width" },
  { component: "filter-panel", declaration: "text-align: left" },
  { component: "borrow-button", declaration: "margin-right" },
  { component: "notification-banner", declaration: "left" },
  { component: "notification-banner", declaration: "border-bottom" },
];
const horizontal = (d) => /left|right|width|inline/.test(d);
console.log("component            physical declaration  logical counterpart      direction-sensitive");
let sensitive = 0;
for (const d of DECLARATIONS) {
  if (horizontal(d.declaration)) sensitive++;
  console.log(
    `${d.component.padEnd(21)} ${d.declaration.padEnd(21)} ${MAPPING[d.declaration].padEnd(25)} ${horizontal(d.declaration) ? "yes" : "no"}`
  );
}
console.log(`direction-sensitive declarations: ${sensitive} / ${DECLARATIONS.length}`);
console.log("declarations on the vertical axis are unaffected by direction change; writing");
console.log("them logically still pays off the same way in vertical writing modes.");

// 2. Mirrored and unmirrored elements.
const ELEMENTS = [
  { name: "back arrow", mirrors: true, reason: "direction follows the flow" },
  { name: "forward arrow", mirrors: true, reason: "direction follows the flow" },
  { name: "breadcrumb separator", mirrors: true, reason: "shows hierarchy direction" },
  { name: "progress bar", mirrors: true, reason: "increase direction follows the flow" },
  { name: "clock icon", mirrors: false, reason: "direction is fixed in the real world" },
  { name: "checkmark", mirrors: false, reason: "direction carries no meaning" },
  { name: "search magnifier", mirrors: false, reason: "the object's own shape" },
  { name: "volume icon", mirrors: true, reason: "increase direction follows the flow" },
  { name: "pencil/edit", mirrors: false, reason: "the object's own shape" },
];
console.log("\nelement                mirrors  reason");
for (const e of ELEMENTS) console.log(`  ${e.name.padEnd(21)} ${(e.mirrors ? "yes" : "no").padEnd(8)} ${e.reason}`);
console.log(`mirrored: ${ELEMENTS.filter((e) => e.mirrors).length} / ${ELEMENTS.length}`);

// 3. Numbers and embedded Latin terms keep their own direction.
// Bidirectional text: the paragraph's base direction is right-to-left, the number inside it is left-to-right.
const SEGMENTS = [
  { text: "shelf code", direction: "rtl" },
  { text: "QB-141", direction: "ltr" },
  { text: "year", direction: "rtl" },
  { text: "1998", direction: "ltr" },
];
console.log("\nparagraph base direction: rtl");
console.log("segment      direction  isolation needed");
for (const s of SEGMENTS) {
  console.log(`  ${s.text.padEnd(11)} ${s.direction.padEnd(10)} ${s.direction === "ltr" ? "yes" : "no"}`);
}
console.log("without isolation, the punctuation next to a left-to-right segment drifts to");
console.log("the wrong side; the embedded segment is isolated for this reason.");

// 4. A string's logical order versus its visual order: isolated and not.
// Simple model: in an rtl paragraph, segments reverse order; an ltr segment stays flat inside itself.
function visualOrder(segments, base) {
  const list = base === "rtl" ? [...segments].reverse() : segments;
  return list.map((s) => s.text).join(" ");
}
console.log("\nlogical order : " + SEGMENTS.map((s) => s.text).join(" "));
console.log("visual order  : " + visualOrder(SEGMENTS, "rtl"));

// 5. Scrolling and the keyboard: arrow keys keep their physical meaning.
const KEYS = [
  { key: "Right arrow", ltr: "next item", rtl: "previous item" },
  { key: "Left arrow", ltr: "previous item", rtl: "next item" },
  { key: "Home", ltr: "first item", rtl: "first item" },
  { key: "End", ltr: "last item", rtl: "last item" },
];
console.log("\nkey           left-to-right       right-to-left");
for (const k of KEYS) console.log(`  ${k.key.padEnd(13)} ${k.ltr.padEnd(19)} ${k.rtl}`);
console.log("arrow keys follow physical direction: the right arrow always moves right");
console.log("on screen, so in a right-to-left layout it reaches the previous item.");

// 6. Audit: components with a remaining physical declaration cannot ship.
const remaining = DECLARATIONS.filter((d) => horizontal(d.declaration));
const components = [...new Set(remaining.map((d) => d.component))];
console.log(`\ncomponents with a remaining physical declaration: ${components.length}`);
for (const c of components) {
  const count = remaining.filter((x) => x.component === c).length;
  console.log(`  ${c.padEnd(21)} ${count} declaration${count === 1 ? "" : "s"}`);
}
```

```
component            physical declaration  logical counterpart      direction-sensitive
record-card           padding-left          padding-inline-start      yes
record-card           border-left           border-inline-start       yes
record-card           margin-top            margin-block-start        no
filter-panel          width                 inline-size               yes
filter-panel          text-align: left      text-align: start         yes
borrow-button         margin-right          margin-inline-end         yes
notification-banner   left                  inset-inline-start        yes
notification-banner   border-bottom         border-block-end          no
direction-sensitive declarations: 6 / 8
declarations on the vertical axis are unaffected by direction change; writing
them logically still pays off the same way in vertical writing modes.

element                mirrors  reason
  back arrow            yes      direction follows the flow
  forward arrow         yes      direction follows the flow
  breadcrumb separator  yes      shows hierarchy direction
  progress bar          yes      increase direction follows the flow
  clock icon            no       direction is fixed in the real world
  checkmark             no       direction carries no meaning
  search magnifier      no       the object's own shape
  volume icon           yes      increase direction follows the flow
  pencil/edit           no       the object's own shape
mirrored: 5 / 9

paragraph base direction: rtl
segment      direction  isolation needed
  shelf code  rtl        no
  QB-141      ltr        yes
  year        rtl        no
  1998        ltr        yes
without isolation, the punctuation next to a left-to-right segment drifts to
the wrong side; the embedded segment is isolated for this reason.

logical order : shelf code QB-141 year 1998
visual order  : 1998 year QB-141 shelf code

key           left-to-right       right-to-left
  Right arrow   next item           previous item
  Left arrow    previous item       next item
  Home          first item          first item
  End           last item           last item
arrow keys follow physical direction: the right arrow always moves right
on screen, so in a right-to-left layout it reaches the previous item.

components with a remaining physical declaration: 4
  record-card           2 declarations
  filter-panel          2 declarations
  borrow-button         1 declaration
  notification-banner   1 declaration
```

The first section's output shows that six of eight declarations are direction-sensitive.
The two declarations on the vertical axis are unaffected by the direction change, but
writing them logically still pays off the same way in vertical writing modes. The rule
is simple: every declaration that touches the horizontal axis is written logically, and
this is a decision made before translation begins.

The last section turns the same data into an audit finding: four components still carry
a physical declaration. This is the work item that has to close before a language ships,
and it is automated the same way as the audits in the Tokens topic.

## Not Every Icon Mirrors

The second section separates the icons. The mirroring criterion is a single question:
does the icon's direction depend on **flow**, or is it the object's own shape? A forward
arrow depends on flow and mirrors; a breadcrumb separator shows hierarchy direction and
mirrors; a progress bar takes its increase direction from the flow and mirrors.

A clock icon does not mirror, because a clock's direction is fixed in the real world. A
checkmark does not mirror, because its direction carries no meaning. A magnifier and a
pencil do not mirror, because they are the object's own shape. Five of nine icons mirror;
the distinction is made by what the icon represents, not by a blanket transform. A rule
that mirrors every icon flips the clock.

## Numbers and Embedded Terms Keep Their Own Direction

The third and fourth sections take on bidirectional text. Inside a right-to-left
paragraph, numbers and Latin-script terms — a shelf code, an ISBN, a unit of measure —
are written in their own direction. When the paragraph's base direction and a segment's
direction differ, which side the surrounding punctuation falls on becomes ambiguous.

The fix is to isolate the embedded segment: it declares its own direction and does not
affect its neighbors'. The fourth section's comparison shows that logical order and
visual order are not the same — the text's order in memory differs from its order on
screen, and that difference also shows up in copying, search, and sorting.

## The Keyboard Follows Physical Direction

The fifth section writes down a rule that is often overlooked. Arrow keys follow
physical direction: the right arrow always moves right on screen. In a right-to-left
list, moving right on screen means reaching the list's **previous** item.

This means the keyboard contracts from the Tabs and Menus lessons have to be rewritten
for right-to-left layout: "the right arrow moves to the next item" is a direction-bound
sentence. The correct wording is flow-based — "the arrow in the flow direction moves to
the next item." Home and End are unaffected by the direction change, because the first
and last items are defined by the flow.

## Measurable Constraints

- Every declaration that touches the horizontal axis is written with logical
  properties; the audit reports components with a remaining physical declaration.
- Icon mirroring is decided one at a time; mirroring everything breaks a clock, a
  checkmark, and shape-based icons.
- Embedded reversed-direction segments are isolated; unisolated concatenation leaves
  punctuation position ambiguous.
- Arrow-key contracts are written by flow direction rather than physical direction.
- The text-growth rule from the previous lesson still applies in right-to-left
  languages; the two constraints are audited together.

## Common Mistakes

The most common mistake is treating direction support as a translation task and never
touching the style layer — the text gets translated but the layout stays left to right,
and the interface becomes unreadable. The second is mirroring every icon in bulk. The
third is declaring the direction change with a single root attribute and leaving
components' physical declarations untouched: a root declaration alone is not enough
without fixing declarations at the component level.

## Summary

- Left and right are the screen's fixed directions; start and end depend on flow. Every
  declaration on the horizontal axis is written logically — six of the eight
  declarations in the model are direction-sensitive.
- Icon mirroring is tied to a criterion: an icon whose direction follows the flow
  mirrors, one that is the object's own shape does not; five of nine icons mirror.
- In a right-to-left paragraph, numbers and Latin-script terms keep their own direction
  and are isolated; the text's logical order and its visual order are not the same.
- Arrow keys follow physical direction, so keyboard contracts are written by flow
  direction.
- The audit lists components carrying a physical declaration; this list closes before a
  language ships.

## Next Step

Direction and formatting decisions adapted the interface's container to the language.
What is left is the **data** that goes inside the container. Every form that asks a
user for a name, an address, or an identity number carries an assumption: that a name
has two parts, that an address is completed by a postal code, that an identity number
has a fixed length. Every one of these assumptions breaks somewhere. The course's last
lesson takes on these fields and turns the assumptions into rules.
