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": true in gsxui.json, gsxui add rewrites physical classes in what it vendors: ml-* to ms-*, left-* to start-*, text-left to text-start, and the rest of shadcn's table. translate-x-* and space-x-* gain rtl: 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 add produces with the flag set.

Not transformed:

  • Sheet and Sidebar side="left"/side="right" and Drawer direction="left"/direction="right" stay physical, matching shadcn's data-side contract; their interiors mirror.
  • input-otp's digit group stays pinned dir="ltr".
  • Behaviour JS carries no classes and is untouched.
  • Sized logical slide utilities (slide-in-from-start-2) match only elements that themselves carry dir in 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.