Skip to content

feat(rtl): RTL infrastructure — scan guard, codemod, useDirection, Storybook direction toolbar - #356

Merged
garrity-miepub merged 2 commits into
mainfrom
feat/rtl-infra
Aug 6, 2026
Merged

feat(rtl): RTL infrastructure — scan guard, codemod, useDirection, Storybook direction toolbar#356
garrity-miepub merged 2 commits into
mainfrom
feat/rtl-infra

Conversation

@garrity-miepub

Copy link
Copy Markdown
Collaborator

Part 1 of the RTL series for #319, superseding #320 (which is being split into small targeted PRs).

What's here (infrastructure only — zero component changes)

  • scripts/rtl-scan.mjs — ratchet guard against new physical-direction classes, wired into CI (pnpm rtl:scan). Baseline generated at current main: 520 matches / 126 files; each component PR tightens it via pnpm rtl:scan:update.
  • scripts/rtl-codemod.mjs — physical→logical Tailwind class rewriter (ml-*ms-*, left-*start-*, rounded-l-*rounded-s-*, …) used to produce the follow-up component PRs. Dry-run by default; --write applies.
  • useDirection hook + isRtlLocale/RTL_LOCALES — observe the effective direction of the document or an element (MutationObserver-backed), exported from the hooks index.
  • cn() tailwind-merge conflicts — physical⇄logical pairs (e.g. ml-* vs ms-*) now conflict, so consumer className="ml-2" overrides keep working against migrated components. miewebUITwMergeConflicts exported for consumers with their own twMerge config.
  • Storybook direction toolbar — auto/ltr/rtl global (auto derives from locale; ar/he/fa/ur → rtl), Arabic sample locale, dir/lang synced on <html> including docs-only pages.

Follow-up PRs (each ~4–6 components, each tightens the baseline)

  1. ✅ this PR — infrastructure
  2. Primitives (Badge, Input, Label, Text, Switch)
  3. Layout + nav (AppHeader, Card, Sheet, Sidebar, Table)
  4. Messaging + AI
  5. DocumentScanner + media
  6. Billing/invoices
  7. Services/employer
  8. Scheduling + clinical
  9. Shell/misc

Tailwind preset safelist entries intentionally ship with their component PRs, not here.

Validation

  • pnpm typecheckpnpm lintpnpm format
  • 14/14 unit tests (useDirection, cn) ✅
  • pnpm rtl:scan passes at the fresh baseline ✅

…, Storybook direction toolbar

- rtl-scan.mjs ratchet (CI-enforced, baseline at current main: 520 matches / 126 files)
- rtl-codemod.mjs physical→logical class rewriter
- useDirection hook + isRtlLocale (exported from hooks index)
- cn() tailwind-merge conflicts so physical/logical pairs override correctly
- Storybook direction toolbar (auto/ltr/rtl) + Arabic sample locale

Part 1 of the RTL series (#319), superseding #320.
Copilot AI lite review requested due to automatic review settings August 5, 2026 23:11
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 5, 2026

Copy link
Copy Markdown

Deploying ui with  Cloudflare Pages  Cloudflare Pages

Latest commit: d44fe9a
Status: ✅  Deploy successful!
Preview URL: https://ec39fac7.ui-6d0.pages.dev
Branch Preview URL: https://feat-rtl-infra.ui-6d0.pages.dev

View logs

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds RTL (right-to-left) infrastructure to the UI component library: guardrails to prevent new physical-direction Tailwind utilities, a codemod to migrate classes, a runtime direction hook for JS behaviors, and Storybook globals to preview RTL.

Changes:

  • Add an RTL scan “ratchet” guard (pnpm rtl:scan) with a committed baseline and CI wiring.
  • Introduce an RTL codemod script for physical→logical Tailwind class rewrites (dry-run by default).
  • Add useDirection + locale helpers, and update cn() to treat physical/logical directional utilities as conflicting; add Storybook direction toolbar and sync dir/lang on <html>.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/utils/index.ts Re-export miewebUITwMergeConflicts alongside cn for consumers.
src/utils/cn.ts Switch to extendTailwindMerge and add physical⇄logical conflict groups; export conflicts config.
src/utils/cn.test.ts Add unit tests covering directional conflict behavior and baseline cn() merging.
src/hooks/useDirection.ts Add useDirection hook plus RTL_LOCALES and isRtlLocale() helper.
src/hooks/useDirection.test.ts Add tests for locale detection and DOM/ref-based direction resolution & updates.
src/hooks/index.ts Export useDirection API from the hooks barrel.
scripts/rtl-scan.mjs Add CI guard script to detect new physical-direction Tailwind utilities in components.
scripts/rtl-codemod.mjs Add codemod to rewrite physical→logical utilities and flag manual-review cases.
scripts/rtl-baseline.json Add initial baseline counts for the RTL scan ratchet.
package.json Add rtl:scan and rtl:scan:update scripts.
.storybook/preview.tsx Add direction global (auto/ltr/rtl), derive auto direction from locale, and sync dir/lang on <html>.
.github/workflows/ci.yml Run RTL scan guard in CI.

Comment thread scripts/rtl-codemod.mjs
…ag rtl:*-reverse remedies

The optional suffix group could match empty, so the rtl:space-x-reverse
remedy itself matched as a spurious bare 'space-x' manual site that
isHandledReverse (which tests /^space-x-/) never exempted. Adopt
rtl-scan.mjs's alternation: space-x- requires a real suffix, divide-x a
trailing boundary.
Copilot AI review requested due to automatic review settings August 5, 2026 23:32

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated no new comments.

Suppressed comments (2)

scripts/rtl-scan.mjs:35

  • The scan regex places the optional negative sign before variant prefixes, so it will NOT match common Tailwind forms like md:-ml-2 / hover:-mr-4 (negative comes after variants). That means the RTL guard can miss newly introduced negative physical-direction utilities whenever they’re combined with variants.
  '(^|[\\s\'"`{:])-?(?:[a-z-]+:)*(' +

scripts/rtl-codemod.mjs:90

  • The codemod’s detection regex also puts the optional negative sign before variant prefixes, so it won’t rewrite classes like md:-ml-2 (negative comes after md:). This will leave some physical-direction utilities un-migrated even though the script claims to preserve variants + negatives.
  '(^|[\\s\'"`{:])(-?(?:[a-z-]+:)*)(' +

@garrity-miepub
garrity-miepub merged commit 03ef1e3 into main Aug 6, 2026
11 checks passed
@garrity-miepub
garrity-miepub deleted the feat/rtl-infra branch August 6, 2026 00:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants