Button
The main action control, in five styles and four sizes, with a danger variant and a loading state. Its directional icons mirror in Arabic on their own.
Preview
Mirrored is the same English content laid out right to left: it separates a mirroring bug from a translation one.
Installation
npx shadcn@latest add @ritla/buttonDependencies it brings: @base-ui/react, @phosphor-icons/react
Usage
import { ArrowRightIcon } from "@phosphor-icons/react/ssr";
import { Button } from "@/components/ui/button";<Button variant="outline">Cancel</Button>
<Button iconTrailing={ArrowRightIcon}>Continue</Button>
<Button isDanger>Delete project</Button>Examples
Styles
filled for the main action, one per view. tonal for a strong secondary, outline for alternatives like Cancel, ghost for low-emphasis actions, inverted-ghost on colored surfaces. isDanger turns any style red.
Sizes
lg 56px (default), md 48 for forms, sm 40 for tables and filters, xs 32 for dense UI. The tap target stays at least 48px at every size.
States
Hover, pressed and focus come from the pointer and keyboard. loading puts the spinner in the leading slot and keeps focus on the button; disabled switches it off. Press Save.
Icons
iconLeading and iconTrailing are optional. “Next” uses ArrowRightIcon in both languages and flips in Arabic to point forward; the download icon never mirrors.
Action order
The main action at the inline end, the alternative before it. In Arabic the whole row mirrors, so the main action stays at the end (the left). Destructive actions get a specific label, never “OK”.
As a link
Navigation stays a link even when it looks like a button: pass render={<a href=… />} or Next's <Link>. It takes the Button's look and stays a link for screen readers.
API reference
Button
Any other Base UI Button prop passes through (onClick, type…).
| Prop | Type | Default | Description |
|---|---|---|---|
variant | "filled" | "tonal" | "outline" | "ghost" | "inverted-ghost" | "filled" | Figma's Style property (style is taken by React). |
size | "lg" | "md" | "sm" | "xs" | "lg" | Figma's Size: 56, 48, 40 and 32px. |
isDanger | boolean | false | Figma's isDanger: red for destructive actions, with a red focus ring. |
children | ReactNode | Figma's Label: the button text. | |
iconLeading | IconComponent | Icon leading: a Phosphor or Kanz icon at the inline start. | |
iconTrailing | IconComponent | Icon trailing: an icon at the inline end. | |
loading | boolean | false | State = Loading: spinner in the leading slot, aria-busy, the button stays focusable and ignores presses. |
disabled | boolean | false | State = Disabled. |
render | ReactElement | Another element with the Button look, usually a link (<a> or <Link>). It keeps its own semantics, so it isn't announced as a button. | |
className | string | Extra classes merged with cn(), such as w-full. |
Accessibility
Keyboard
Keys are written for left-to-right. In right-to-left the arrow keys swap: the one pointing toward the reading direction moves forward.
| Key | Action |
|---|---|
| Tab | Moves focus to the button and shows the focus ring (red with isDanger). |
| EnterSpace | Activates it. While loading, focus stays and the action doesn't repeat. |
ARIA
- A native
<button>(or<a>withrender), so noroleis needed. - Loading sets
aria-busy="true"andaria-disabled="true"instead ofdisabled, so focus isn't lost and screen readers hear the state. Change the label too (“Saving”). - Icons are decorative; the label is the name. Icon-only buttons belong to the Icon Button component.
- The tap target is at least 48px even at
smandxs(WCAG 2.5.8).
Arabic and RTL notes
Mirrors
- The leading icon sits at the inline start (right in Arabic) and the trailing one at the end, with no RTL prop.
- Arrows and carets flip through
<Icon>: “Next” points left in Arabic. - Action order mirrors: the main action is at the inline end in both directions.
Does not mirror
- The loading spinner turns clockwise in both languages.
- Checkmarks, download icons and logos inside buttons never mirror.
- Numerals
- Numbers inside a label (“Delete 3 files”) follow the numerals setting through
formatNumber, not the button. - Arabic typography
- Labels use
body-16(body-14atxs) in Semibold; the button height is fixed and Arabic labels fit without clipping. - Mixed-direction text
- A Latin name inside an Arabic label (“تسجيل الدخول عبر Google”) goes in
<bdi>so punctuation doesn't jump.