ADR-0033: Right-to-left Studio — logical properties, dir from the registry, a Kumo patch until upstream
- Status: Accepted (2026-10-18, by Halil, as recommended). The open questions below stay open for the owner: the first RTL language and its numerals.
- Date: 2026-10-18
- Related: an internal review (2026-10) (Phase 3), DESIGN.md §1 (“Languages”, “Adding a language”), §2v “Kumo locale”, ADR-0032,
docs/design/kumo-upstream-notes.md - Sources:
- Tailwind CSS 4 logical utilities (
ms-*,me-*,ps-*,pe-*,start-*,end-*,text-start,border-s,rounded-s) and thertl:/ltr:variants: https://tailwindcss.com/docs/margin, https://tailwindcss.com/docs/hover-focus-and-other-states#rtl-support - Base UI
DirectionProvider(keyboard arrows and placement followdir): https://base-ui.com/react/utils/direction-provider - CSS logical properties: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_logical_properties_and_values
- Phosphor’s
mirroredprop (@phosphor-icons/reactIconProps,dist/lib/types.d.tsin 2.1.10) - Material guidance on which icons mirror: https://m3.material.io/foundations/layout/understanding-layout/bidirectionality-rtl
- Tailwind CSS 4 logical utilities (
Context
No right-to-left language (Arabic, Hebrew, Persian, Urdu) can be added to Studio yet. DESIGN.md §1 lists RTL as a prerequisite. Measured on main at 7b1ec23 with node scripts/i18n-audit.ts:
| Where | Physical utilities (ml/mr/pl/pr/left/right/text-left/text-right/border-l/…) |
Logical utilities |
|---|---|---|
apps/studio/src class names |
34 (text-right 25, pl 4, text-left 3, ml 2) |
0 |
packages/ui/src blocks |
39 (ml 9, text-right 7, pl 6, text-left 6, …) |
3 |
Kumo 2.14 dist (compiled classes) |
239 | 12, plus 3 rtl: variants |
Icons that point (Caret*/Arrow*Left/Right) |
77 uses | — |
What already works:
- the locale registry has
dirand sets<html dir>together withlang(apps/studio/src/i18n/locales.ts, PR #152); - untrusted reader text uses
dir="auto"(blocks/CommentText.tsx); - Base UI, under Kumo, supports direction through
DirectionProvider, and Kumo re-exports it as@cloudflare/kumo/primitives/direction-provider.
What is missing:
- Kumo styles. Its own styles are physical, so the sidebar, dialogs’ close buttons, input addons, table alignment, popovers’ offsets and the toolbar would not mirror.
- Icons. Nothing mirrors directional icons.
Options
A. Where Kumo’s physical styles get fixed
| Option | Pros | Cons |
|---|---|---|
A1. Upstream first, with a pnpm patch until it lands (as @cloudflare__kumo@2.14.0.patch does today) |
No fork. The fix benefits Cloudflare’s own RTL plans. The patch is mechanical (ml-→ms-, left-→start-, …) and one file. |
A patch over compiled dist must be redone on every Kumo upgrade until upstream ships, and the class list in dist is long (239). |
| A2. Fork Kumo | Full control | Owning a design system that Cloudflare evolves weekly. Breaks “Kumo via @cloudhal/ui” and the kumo-design skill. |
A3. A wrapper layer: CSS overrides under [dir=rtl] in @cloudhal/ui/styles.css that flip margins, paddings and positions of Kumo’s classes |
No change to Kumo’s files | Hundreds of overrides that silently drift from Kumo’s classes, and specificity fights. It fixes classes, not components (popover placement is still physical). |
| A4. Wait for Kumo to support RTL | No work | Unknown timeline; RTL languages stay blocked |
B. dir handling
<html dir>comes from the registry, which is done.DirectionProviderwraps the app at the root (main.tsx), withdirection={LOCALE_REGISTRY[locale].dir}, so Base UI’s arrows and placement follow it.dir="auto"stays on user content (comments, titles shown verbatim, editor bodies), because a Turkish Studio can show Arabic comments.
C. Icon mirroring
| Option | Pros | Cons |
|---|---|---|
C1. A DirectionalIcon block (or a directional prop on Kumo-wrapped buttons) that sets Phosphor’s mirrored when dir is rtl, used for icons that mean a direction: back, forward, next, previous, breadcrumbs’ carets, “open in new”, indent |
Explicit: only meaning-carrying icons flip, never clocks, media play or checkmarks (Material’s guidance) | 77 call sites to review once |
C2. CSS rtl:-scale-x-100 on every Caret*/Arrow* |
One rule | Flips icons that must not flip (a chart’s trend arrow, “sort ascending”) |
D. Studio’s and the blocks’ own 73 physical classes
A lint rule (design lint, like i18n-format) forbids physical utilities in apps/studio/src and packages/ui/src from now on, and a codemod converts the 73 occurrences. Directional exceptions (a chart axis, a slider that means “left”) carry i18n-ignore-style reasons.
Decision (recommended)
- A1:
- convert Kumo’s physical classes with a
pnpm patch, generated by a script so each Kumo upgrade can re-run it; - file the upstream issue the same day (
kumo-upstream-notes.md§7); - drop the patch when Kumo ships logical properties.
- convert Kumo’s physical classes with a
- B:
DirectionProviderat the root, driven by the registry. Keepdir="auto"on user content. - C1: a
DirectionalIconblock in@cloudhal/uiand a reviewed list of directional icons. - D: convert Studio’s and the blocks’ 34 + 39 physical utilities with a codemod, and add a design-lint rule against new ones now. The rule costs nothing even before any RTL language exists.
- Verification:
pnpm design:shotswith an RTL pseudo-locale, a mirrored variant of Phase 5’s pseudo-locale withdir="rtl"and an Arabic font stack, at 1440 and 390 px, before the first real RTL language. - Order: D and B first (cheap, no new language needed), then A1 and C1 when an RTL language is scheduled.
Consequences
- Positive:
- RTL languages become possible;
- logical properties also make the code clearer (“start” means the reading start);
- the lint keeps Studio honest without waiting.
- Negative:
- a second Kumo patch to maintain until upstream;
- a one-time review of 77 icon uses.
- Risks:
- Kumo’s compiled class names change between versions and the patch script drifts. Mitigation: a test that fails when the patch no longer applies, as for the current patch.
- Charts (ECharts) need their own RTL settings (axis and legend placement), out of this ADR’s scope; noted for the analytics screens.
Decisions (2026-10-18)
The owner delegated the open questions; decided in the session that coordinates the i18n work:
- First right-to-left language: Arabic (
ar), with Western digits. The registry entry is keyedar, and Studio formats with the tagar-u-nu-latn. Studio shows ids, versions, revisions and code next to numbers, and Western digits keep them readable together. Arabic-Indic digits (nu-arab) may become a person’s preference later. - Order:
- A1: the generated Kumo patch, with a test that fails when it no longer applies, and the upstream issue text ready for the owner to file;
- C1:
DirectionalIcon, converting only icons that mean a direction; - then the Arabic catalogs, verified with
SHOTS_LOCALE=ar pnpm design:shotsat 1440 and 390 px.
- ECharts: its right-to-left settings (axis side, legend order) are a follow-up if the Arabic step does not cover them.
Open questions
None left from this ADR; the questions below were answered above.
- (answered) Does Cloudflare plan RTL in Kumo? The upstream issue (
kumo-upstream-notes.md§7) asks; the patch does not wait for the answer. - (answered) Which RTL language first, which numerals? Arabic, Western digits (
ar-u-nu-latn).