RTL
Opt in once and the components you vendor use logical direction classes, so one build serves dir="ltr" and dir="rtl" alike.
Get started
Set the flag when you initialise, then add components as usual:
gsxui init --rtl
gsxui add button card
Components vendored before the flag was set are rewritten in place, keeping your edits:
gsxui migrate rtl
Set dir="rtl" on <html>, or on any subtree, from the locale. An rtl: true project renders both directions; the document's dir decides.
The flag is one-way: there is no reverse migration, so opt in before you customise vendored files.
<html lang="ar" dir="rtl">
...
</html>Translating the strings components write themselves is covered on Internationalization.
How it works
- Logical classes, at install time. With
"rtl": trueingsxui.json,gsxui addrewrites physical classes in what it vendors:ml-*toms-*,left-*tostart-*,text-lefttotext-start, and the rest of shadcn's table.translate-x-*andspace-x-*gainrtl:companions. - Directional icons. Chevrons and arrows that encode a left/right meaning carry
rtl:rotate-180. - Direction-aware floating positioning. Popover, dropdown-menu, select, tooltip and the rest of the floating family resolve placement in JS at position time.
- Mirrored keyboard semantics. Arrow keys in menus, tabs, carousel, calendar and the other roving-focus components mirror by meaning per WAI-ARIA.
- The demos on this page render from the transformed components, the same output
gsxui addproduces with the flag set.
Not transformed:
- Sheet and Sidebar
side="left"/side="right"and Drawerdirection="left"/direction="right"stay physical, matching shadcn'sdata-sidecontract; their interiors mirror. input-otp's digit group stays pinneddir="ltr".- Behaviour JS carries no classes and is untouched.
- Sized logical slide utilities (
slide-in-from-start-2) match only elements that themselves carrydirin the current tw-animate-css build; gsxui emits none.
Try it out
An Arabic sign-in card: Card, Label, Input, NativeSelect and Button, rendered from the transformed components, wrapped in dir="rtl".
// Package rtl holds the site's example gsx components demonstrating gsxui
// components under dir="rtl" — not tied to a single ui/ component the way
// the other site/examples packages are.
package rtl
import "github.com/gsxhq/gsxui/site/uirtl"
// Login is an Arabic sign-in card rendered under dir="rtl": every part —
// Card, Label, Input, NativeSelect, Button — adapts because the demo
// renders the transformed site/uirtl package, the same output gsxui add
// produces for a project with "rtl": true. This is the /docs/rtl page's
// "Try it out" demo.
component Login() {
<div dir="rtl" lang="ar" class="max-w-sm">
<uirtl.Card>
<uirtl.CardHeader>
<uirtl.CardTitle>تسجيل الدخول</uirtl.CardTitle>
<uirtl.CardDescription>أدخل بيانات حسابك للمتابعة</uirtl.CardDescription>
</uirtl.CardHeader>
<uirtl.CardContent>
<form class="flex flex-col gap-4">
<div class="flex flex-col gap-2">
<uirtl.Label for="rtl-login-email">البريد الإلكتروني</uirtl.Label>
<uirtl.Input
id="rtl-login-email"
type="email"
name="email"
placeholder="[email protected]"
autocomplete="email"
required
/>
</div>
<div class="flex flex-col gap-2">
<uirtl.Label for="rtl-login-password">كلمة المرور</uirtl.Label>
<uirtl.Input
id="rtl-login-password"
type="password"
name="password"
autocomplete="current-password"
required
/>
</div>
<div class="flex flex-col gap-2">
<uirtl.Label for="rtl-login-language">اللغة</uirtl.Label>
<uirtl.NativeSelect id="rtl-login-language" name="language">
<uirtl.NativeSelectOption value="ar" selected={true}>العربية</uirtl.NativeSelectOption>
<uirtl.NativeSelectOption value="en">English</uirtl.NativeSelectOption>
</uirtl.NativeSelect>
</div>
<uirtl.Button type="submit" class="w-full">دخول</uirtl.Button>
</form>
</uirtl.CardContent>
<uirtl.CardFooter>
<p class="text-sm text-muted-foreground">ليس لديك حساب؟ إنشاء حساب</p>
</uirtl.CardFooter>
</uirtl.Card>
</div>
}
The source imports uirtl, this site's transformed copy of ui. In your project these are ui.Card, ui.NativeSelect and so on.
More RTL variants live on their own component pages: Calendar, Pagination, and Sidebar each register an "RTL" example alongside their other demos.
Fonts
The site's own Latin type is Geist. For Arabic content, pair it with Noto Sans Arabic (a UI sans) or Noto Naskh Arabic (better for longer prose). Load the Arabic font as a fallback rather than replacing Geist outright, so Latin text — brand names, code — keeps rendering in Geist inside an RTL document.