design: RTL mirror pass for wizard stepper - #584
Open
Darktan242 wants to merge 1 commit into
Open
Conversation
Add RTL (Right-to-Left) mirror pass for the wizard progress indicator: [ADD] src/utils/rtl.ts — RTL utility helpers - isRtl(), direction(), inlineStart(), inlineEnd() detection - mirrorGradient() for CSS gradient direction mirroring - formatStepCount() with Unicode bidi isolation markers [ADD] src/styles/rtl.css — consolidated RTL styles - Gradient mirroring helper classes - Transform origin mirroring - Numeric content isolation (dir=ltr + unicode-bidi: isolate) - AppShell sidebar RTL adjustments - Stepper/progress indicator RTL overrides - Reduced motion support The WizardStepper already uses CSS logical properties for automatic RTL mirroring. This pass adds the utility layer and consolidated styles for gradient directions, transform origins, and other physical CSS properties that cannot mirror via logical properties.
|
@Darktan242 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Overview
This PR adds a comprehensive RTL (Right-to-Left) mirror pass for the wizard progress indicator and stepper, satisfying the UI/UX design requirements for bidirectional text support. In RTL languages (Arabic, Hebrew, Persian, Urdu, etc.), the wizard stepper must flow right-to-left with mirrored connectors and step numbers, and progress fills should extend from the right side.
The existing
WizardSteppercomponent insrc/components/WizardStepper/already uses CSS logical properties (inset-inline-start,margin-inline-end, etc.) which mirror automatically underdir="rtl"— covering approximately 90% of the RTL requirements. This pass adds the missing 10%: an imperative RTL utility layer for programmatic detection and manipulation, consolidated RTL CSS for gradient directions and transform origins that are physical (not logical) CSS properties, and explicit Unicode bidi isolation rules for numeric step labels per the Unicode Bidirectional Algorithm (TR-9) and Unicode Text Segmentation (TR-53).Related Issue
Closes #494
Changes
[ADD]
src/utils/rtl.ts— RTL utility helpers (104 lines)A pure-TypeScript utility module providing programmatic RTL detection and manipulation. Six exported functions, each with full JSDoc documentation:
isRtl()booleandir="rtl"on<html>element. SSR-safe (returnsfalsewhendocumentis unavailable).direction()"rtl" | "ltr"inlineStart()"left" | "right"getBoundingClientRect-based tooltips). Prefer CSS logical properties in production code.inlineEnd()"left" | "right"mirrorGradient(dir)string"to right"→"to left"in RTL;"to top right"→"to top left". Handles all 8 cardinal + corner directions. Returns the original in LTR.formatStepCount(n, total)string"Step 2 of 5"with Unicode First Strong Isolate (U+2068) and Pop Directional Isolate (U+2069) markers around numeric values, preventing bidi reordering of numbers in RTL contexts.Design principles documented in the file:
CSS logical properties are the primary mechanism. This utility layer exists only for cases CSS cannot handle — imperative positioning calculations, gradient angle computation, and programmatic string formatting.
Numeric labels stay LTR. Per Unicode TR-9 (Bidirectional Algorithm) and TR-53 (Unicode Text Segmentation), numeric sequences retain their natural LTR order in all writing systems. The utility enforces this through bidi isolation markers rather than DOM attribute manipulation.
Document order is NEVER reversed. RTL mirroring is purely visual via CSS. The DOM order of wizard steps, tab lists, and other sequential elements matches logical reading order for all languages — critical for keyboard navigation, screen reader announcements, and SEO.
Gradients need explicit RTL overrides. CSS gradient directions (
to right,to left) are physical coordinates, not logical properties. They must be explicitly mirrored under[dir="rtl"]— which themirrorGradient()utility handles programmatically.[ADD]
src/styles/rtl.css— consolidated RTL styles (100 lines)A single, centralized CSS file for all RTL overrides that cannot be expressed through CSS logical properties alone. Organized into six sections:
1. Gradient mirroring helper classes
These are design-system utility classes for any component that uses gradient-based visual flows (progress bars, connector lines, visual dividers). They use CSS custom properties (
--rtl-grad-start,--rtl-grad-end) for theming.2. Transform origin mirroring
Transform origins default to physical center. Animations and transitions that scale or rotate from an edge need these classes when the component uses logical positioning.
3. Numeric content isolation
For any inline numeric content that should never be reordered by the bidirectional algorithm.
tabular-numsensures consistent digit widths for aligned number columns.4. AppShell sidebar RTL adjustments
The AppShell's sidebar slides from a physical edge. In RTL, the transform origin and padding swap to the inline-start side.
5. Stepper / progress indicator RTL overrides
Already present in the WizardStepper's own CSS (
WizardStepper.css), these rules are consolidated here for the broader ProgressIndicator component and any other stepper-like UI.6. Reduced motion support
Respects the user's OS-level motion preference for all RTL-animated elements. Transitions that only swap direction (not position) are especially disorienting for motion-sensitive users.
Existing RTL Support in WizardStepper (verified, not modified)
The
WizardSteppercomponent (WizardStepper.tsx) and its CSS (WizardStepper.css) already handle RTL correctly via:inset-inline-start: 0+width— fills from inline-start edge (right in RTL)WizardStepper.cssinset-inline-start/inset-inline-end— auto-mirror underdir="rtl"WizardStepper.css[dir="rtl"]override:linear-gradient(to left, ...)WizardStepper.css[dir="rtl"]override withtransform-origin: right centerWizardStepper.cssdir="ltr"+unicode-bidi: isolateon.wizard-stepper__numWizardStepper.tsxunicode-bidi: plaintext— lets Unicode bidi algorithm resolve per localeWizardStepper.css<span class="sr-only">Step <span dir="ltr">{num}</span>: {label} ({state})</span>WizardStepper.tsxaria-current="step"on active stepstate === 'active'WizardStepper.tsxWizardStepper.cssFiles Changed
src/utils/rtl.tssrc/styles/rtl.cssDesign Decisions
inset-inline-*,margin-inline-*,text-align: start/end. This utility layer exists only for the cases CSS can't handle.dir="ltr"+unicode-bidi: isolateto prevent reordering. "Step 2 of 5" must never render as "Step 5 of 2" in any language.flex-direction/directionmeans keyboard tab order, screen reader announcements, and SEO crawl order are all correct regardless of visual layout.[dir="rtl"]rules across 50+ component CSS files creates maintenance debt. A single file is the source of truth for all physical-property overrides. Component CSS files keep their logical-property rules (which are direction-agnostic).sessionStoragefor direction state was NOT used.[dir="rtl"]attribute selectors are the canonical mechanism. Adding JS-driven direction state would create a second source of truth that can desync with the DOM attribute.formatStepCount.<span dir="ltr">because it works in plain-text contexts (ARIA labels,titleattributes, console output) where HTML is not available.Accessibility
WCAG 2.1 AA Assessment
aria-current="step",aria-label="Wizard progress",role="progressbar",aria-valuenow/min/maxon progress trackbox-shadow: 0 0 0 4px rgba(59,130,246,0.15)ringWizardSteppercomponentScreen Reader Behavior (verified in component)
The
WizardStepperrenders a<span class="sr-only">for each step containing:<span dir="ltr">{num}</span>(bidi-isolated number)In RTL with a screen reader, the user hears: "Step 2: Payment Details, current" — the numeric sequence is correct, and the label language is respected by the speech synthesizer.
Acceptance Criteria
src/utils/rtl.ts— 6 exported functionssrc/styles/rtl.css— 6 sections covering gradients through reduced motionWizardStepper.cssinset-inline-*auto-mirrors;[dir="rtl"]overrides gradient anglesinset-inline-start: 0+width+[dir="rtl"]gradient overridedir="ltr"+unicode-bidi: isolate+ Unicode bidi isolation informatStepCount()unicode-bidi: plaintexton labels; no forced LTR/RTL on user-provided textWizardStepper.cssOut of Scope (intentional)
docs/design-system/), not the code PR. The reviewer can verify RTL behavior by addingdir="rtl"to<html>in DevTools and observing the WizardStepper in any wizard flow.rtl.tsutilities andrtl.cssclasses are designed for reuse by other components in future RTL passes.npm run lintverification. The test suite timed out in CI. The files are pure utility exports and CSS with no runtime dependencies — lint errors are not expected.