caliper uidocs

Direction

Free

Sets reading direction for everything inside it. Spacing, borders, affixes and the selected edge follow on their own; icons that point somewhere flip when marked.

Installs components/ui/direction.tsxDepends on none beyond ReactMotion none in component source
pnpm dlx shadcn@latest add https://caliperui.site/r/direction.json
ملخص الفوترةمدفوعة
$USD
فاتورة أكتوبر
أُرسلت في ١ أكتوبر

When to use #

Use it for

  • Arabic, Hebrew, Persian and Urdu interfaces.
  • A preview that shows a layout in both directions.

Skip it when

  • Flipping numbers, logos or media controls; they keep their direction.

Decisions #

Why it is built this way, so you know what to keep when you change your copy.

Behaviour

Logical properties, not mirrored styles
Parts use inline-start and inline-end and :dir() rules, so one stylesheet serves both directions. Mark a chevron or back arrow with data-mirror.

Usage

Overlays read the direction
Dialogs render in a portal outside the wrapper, so they take it from useDirection. The alert dialog does this for you.

Installation #

terminal
pnpm dlx shadcn@latest add https://caliperui.site/r/direction.json

Adds the file to components/ui/, each with its own stylesheet, and installs their packages. No account needed. The first part also adds Caliper UI’s tokens; import them once as in Installation.

Usage #

This is the code behind the preview above. It is built from Direction and useDirection.

example.tsx
import { ChevronRight, FileText } from "lucide-react"
import { useState } from "react"
import { Badge } from "@/components/ui/badge"
import { Checkbox } from "@/components/ui/checkbox"
import { Direction, type TextDirection } from "@/components/ui/direction"
import { Input } from "@/components/ui/input"
import { Item, ItemContent, ItemDescription, ItemMedia, ItemTitle } from "@/components/ui/item"
import { Label } from "@/components/ui/label"
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle"

export function DirectionDemo() {
  const [dir, setDir] = useState<string[]>(["rtl"]);
  const rtl = dir[0] === "rtl";
  return (
    <div className="grid w-full max-w-sm gap-4">
      <ToggleGroup type="single" label="Direction" value={dir} onValueChange={setDir} className="justify-self-start">
        <ToggleGroupItem value="ltr" size="sm">LTR</ToggleGroupItem>
        <ToggleGroupItem value="rtl" size="sm">RTL</ToggleGroupItem>
      </ToggleGroup>
      <div className="grid gap-4 rounded-surface border border-border bg-elevated p-4">
        <Direction dir={(rtl ? "rtl" : "ltr") satisfies TextDirection}>
          <div className="flex items-center justify-between gap-3 font-medium">
            <span>{rtl ? "ملخص الفوترة" : "Billing summary"}</span>
            <Badge variant="success">{rtl ? "مدفوعة" : "Paid"}</Badge>
          </div>
          <Input aria-label={rtl ? "المبلغ" : "Amount"} defaultValue="4,820.00" leading="$" trailing="USD" />
          <div className="flex items-center">
            <Checkbox id="direction-receipt" defaultChecked />
            <Label htmlFor="direction-receipt">{rtl ? "أرسل لي الإيصال" : "Email me the receipt"}</Label>
          </div>
          <Item asChild>
            <a href="#direction">
              <ItemMedia><FileText aria-hidden="true" /></ItemMedia>
              <ItemContent>
                <ItemTitle>{rtl ? "فاتورة أكتوبر" : "October invoice"}</ItemTitle>
                <ItemDescription>{rtl ? "أُرسلت في ١ أكتوبر" : "Sent 1 Oct"}</ItemDescription>
              </ItemContent>
              <ChevronRight data-mirror="" aria-hidden="true" className="size-4 text-muted-foreground" />
            </a>
          </Item>
        </Direction>
      </div>
    </div>
  );
}

API reference #

Direction

component

Also accepts the native props of its root element and a ref.

PropTypeDefault
dir *TextDirection—

useDirection

component

No props of its own; it re-exports a primitive and takes that primitive’s props.

Accessibility #

It is built on native elements, so roles and focus come from the browser.

No custom key handling; Tab and the native activation keys do the work.

Motion #

It declares no animation in its component source. Any transition comes from its stylesheet, which uses the duration tokens that collapse to instant under prefers-reduced-motion.