You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add RTL (right-to-left) language support across the component library
Summary
Support RTL languages (Arabic, Hebrew, Farsi, Urdu) at the component level.
Components should be direction-agnostic by default using CSS logical
properties, with a Storybook toolbar switch (alongside brand / theme / density /
language) to preview RTL.
PR series status (updated 2026-08-07, supersedes #320)
Work lands as a series of small targeted PRs. Each merge cuts a 0.7.2-dev.x
release for in-the-wild validation; full release after the series completes.
Tailwind 4 — native logical utilities (ms-/me-/ps-/pe-/start-/end-/ text-start/rounded-s) and rtl:/ltr: variants are available
Storybook toolbar globals (brand/theme/density/locale) live in .storybook/preview.tsx with a shared applyGlobalTheme() — the right place
to also set dir
No dir/direction infrastructure exists anywhere yet
Guiding principle
Logical properties first, rtl: variants last. Make components
direction-agnostic so RTL "just works" from dir="rtl". Reserve rtl:
overrides for true exceptions (icon flips, transforms). Avoid doubling the
class surface with per-direction overrides.
Phase 0 — Infrastructure (one PR, do first) — ✅ shipped in #356
Add direction Storybook global (ltr / rtl / auto) next to theme/density/locale
auto derives direction from the locale global (rtl for ar, he, fa, ur)
Wire into applyGlobalTheme() to set dir on document.documentElement (works on docs-only MDX pages, same pattern as dark mode)
Add an RTL sample locale (e.g. العربية (sample)) to the locale toolbar for real RTL text
Runtime direction API: useDirection() hook + isRtlLocale/RTL_LOCALES for JS-level direction logic (keyboard arrows, popover placement, drag math). Default: read from closest dir attribute — zero setup for consumers who set ``
Enforcement: CI script (scripts/rtl-scan.mjs ratchet, like ccme-scan.mjs) that fails on new physical-direction classes in src/components — prevents regression while migration is in flight
Phase 1 — Mechanical codemod (bulk of the 137 files)
Scripted, reviewable find/replace over className strings, run per component
batch (small PRs — see PR series status above):
Docs: add "direction-agnostic styling" section to copilot-instructions / lessons so future components are born RTL-safe
First milestone
Phase 0 + codemod on ~10 primitives, verified with the new toolbar switch.
Proves the full pipeline (switch → logical classes → visual check → lint guard)
before scaling to the remaining ~125 files.
Add RTL (right-to-left) language support across the component library
Summary
Support RTL languages (Arabic, Hebrew, Farsi, Urdu) at the component level.
Components should be direction-agnostic by default using CSS logical
properties, with a Storybook toolbar switch (alongside brand / theme / density /
language) to preview RTL.
PR series status (updated 2026-08-07, supersedes #320)
Work lands as a series of small targeted PRs. Each merge cuts a
0.7.2-dev.xrelease for in-the-wild validation; full release after the series completes.
Then Phase 2 (icon flips, JS positioning, LTR islands, third-party RTL flags) as follow-ups.
Current state (measured 2026-07-21)
.tsxfiles; 137 contain physical-direction Tailwind classes(
ml-,pr-,left-,text-left,rounded-l,border-l,space-x-, …)ms-/me-/ps-/pe-/start-/end-/text-start/rounded-s) andrtl:/ltr:variants are available.storybook/preview.tsxwith a sharedapplyGlobalTheme()— the right placeto also set
dirdir/direction infrastructure exists anywhere yetGuiding principle
Logical properties first,
rtl:variants last. Make componentsdirection-agnostic so RTL "just works" from
dir="rtl". Reservertl:overrides for true exceptions (icon flips, transforms). Avoid doubling the
class surface with per-direction overrides.
Phase 0 — Infrastructure (one PR, do first) — ✅ shipped in #356
directionStorybook global (ltr/rtl/auto) next to theme/density/localeautoderives direction from thelocaleglobal (rtl forar,he,fa,ur)applyGlobalTheme()to setdirondocument.documentElement(works on docs-only MDX pages, same pattern as dark mode)العربية (sample)) to the locale toolbar for real RTL textuseDirection()hook +isRtlLocale/RTL_LOCALESfor JS-level direction logic (keyboard arrows, popover placement, drag math). Default: read from closestdirattribute — zero setup for consumers who set ``scripts/rtl-scan.mjsratchet, likeccme-scan.mjs) that fails on new physical-direction classes insrc/components— prevents regression while migration is in flightPhase 1 — Mechanical codemod (bulk of the 137 files)
Scripted, reviewable find/replace over
classNamestrings, run per componentbatch (small PRs — see PR series status above):
ml-*/mr-*ms-*/me-*pl-*/pr-*ps-*/pe-*left-*/right-*start-*/end-*text-left/text-righttext-start/text-endrounded-l*/rounded-r*/ cornersrounded-s*/rounded-e*/rounded-ss…border-l*/border-r*border-s*/border-e*space-x-*flex gap-*; elsertl:space-x-reversedivide-x-*rtl:divide-x-reversescripts/rtl-codemod.mjs, shipped in feat(rtl): RTL infrastructure — scan guard, codemod, useDirection, Storybook direction toolbar #356); it also flags dynamic class construction it can't safely rewrite (cond ? 'ml-2' : …,cn()calls, class maps) → produces the Phase 2 worklistPhase 2 — Manual/semantic pass (needs judgment, per component)
rtl:-scale-x-100); checkmarks, media buttons, logos do NOT flipuseDirection())translate-x-*,slide-in-from-left, Switch thumb travel, Sheet/Sidebar slide directionstop-right→ logical "top-end")dir="ltr"enableRtl), DataVis/charts, RichTextEditor/Kerebron, ESheet renderer, YChartPhase 3 — Rollout order
rtl:mirrors)Phase 4 — Verification & guardrails
dirvalues (2× matrix only for direction-sensitive components)First milestone
Phase 0 + codemod on ~10 primitives, verified with the new toolbar switch.
Proves the full pipeline (switch → logical classes → visual check → lint guard)
before scaling to the remaining ~125 files.