Button
Action control with default, glass, liquid, matte, glow, outline, and ghost variants.
pnpm dlx @glinui/cli@latest add buttonInstallation
pnpm dlx @glinui/cli@latest add buttonPrefer the package?
Usage
Variants
light scope
| variant | neutral | accent | success | warning | danger | info |
|---|---|---|---|---|---|---|
| glinr | ||||||
| solid | ||||||
| plain | ||||||
| soft | ||||||
| outline | ||||||
| ghost | ||||||
| gradient | ||||||
| glass |
dark scope
| variant | neutral | accent | success | warning | danger | info |
|---|---|---|---|---|---|---|
| glinr | ||||||
| solid | ||||||
| plain | ||||||
| soft | ||||||
| outline | ||||||
| ghost | ||||||
| gradient | ||||||
| glass |
import { Button, SURFACE_TONES, SURFACE_VARIANTS } from "@glinui/ui"
export function Demo() { return ( <div className="grid gap-2"> {SURFACE_VARIANTS.map((variant) => ( <div key={variant} className="flex flex-wrap gap-2"> {SURFACE_TONES.map((tone) => ( <Button key={tone} variant={variant} tone={tone}> {variant} {tone} </Button> ))} </div> ))} </div> )}Glass (opt-in)
import { Button } from "@glinui/ui"
// Glass needs a colorful or photographic backdrop to read.export function Demo() { return <Button variant="glass">Glass</Button>}Sizes, icons and states
Five sizes, leading and trailing icons, loading and disabled.
import { ArrowRight, Plus } from "@phosphor-icons/react"import { Button } from "@glinui/ui"
export function ButtonStatesDemo() { return ( <div className="grid w-full max-w-2xl gap-5"> <div className="flex flex-wrap items-end gap-3"> <Button size="xs">Extra small</Button> <Button size="sm">Small</Button> <Button size="md">Medium</Button> <Button size="lg">Large</Button> <Button size="icon" variant="outline" aria-label="Add a member"> <Plus weight="bold" /> </Button> </div> <div className="flex flex-wrap items-center gap-3"> <Button variant="solid" tone="accent" leadingIcon={<Plus weight="bold" />}> New project </Button> <Button variant="outline" trailingIcon={<ArrowRight weight="bold" />} iconNudge> Continue </Button> <Button variant="solid" tone="accent" loading> Publishing </Button> <Button variant="outline" loading> Syncing </Button> <Button disabled>Unavailable</Button> <Button variant="solid" tone="danger" disabled> Delete </Button> </div> </div> )}In a layout
A page header with a quiet filter, an outline export and one primary action.
Invoices
48 invoices this quarter, 3 overdue.
import { DownloadSimple, Funnel, Plus } from "@phosphor-icons/react"import { Button, Card, CardContent } from "@glinui/ui"
export function ButtonPageHeader() { return ( <Card className="w-full max-w-3xl"> <CardContent className="flex flex-wrap items-center justify-between gap-4"> <div className="min-w-0"> <h3 className="text-lg font-semibold text-foreground">Invoices</h3> <p className="text-sm text-[var(--color-muted)]">48 invoices this quarter, 3 overdue.</p> </div> <div className="flex flex-wrap items-center gap-2"> <Button variant="ghost" leadingIcon={<Funnel weight="bold" />}> Filter </Button> <Button variant="outline" leadingIcon={<DownloadSimple weight="bold" />}> Export CSV </Button> <Button variant="solid" tone="accent" leadingIcon={<Plus weight="bold" />}> New invoice </Button> </div> </CardContent> </Card> )}Size
Variants
Toggle backgrounds (top-right) to see how each variant adapts to different surfaces.
import { Button } from "@glinui/ui"
export function ButtonVariantDemo() {
return (
<div className="flex flex-wrap items-center gap-2">
<Button>Default</Button>
<Button variant="glass">Glass</Button>
<Button variant="frosted">Frosted</Button>
<Button variant="liquid">Liquid</Button>
<Button variant="matte">Matte</Button>
<Button variant="glow">Glow</Button>
<Button variant="outline">Outline</Button>
<Button variant="ghost">Ghost</Button>
</div>
)
}With Icon
import { ArrowRight } from "@phosphor-icons/react"
import { Button } from "@glinui/ui"
export function ButtonWithIconDemo() {
return (
<Button variant="liquid" className="gap-2">
Continue
<ArrowRight className="size-4" />
</Button>
)
}Button Group
import { Button } from "@glinui/ui"
export function ButtonGroupDemo() {
return (
<div className="inline-flex items-center gap-1 rounded-full border border-border/60 bg-background/45 p-1">
<Button size="sm" className="rounded-full">Day</Button>
<Button size="sm" variant="ghost" className="rounded-full">Week</Button>
<Button size="sm" variant="ghost" className="rounded-full">Month</Button>
</div>
)
}RTL
Use `dir="rtl"` on a layout wrapper to mirror button flow for right-to-left locales.
import { Button } from "@glinui/ui"
export function ButtonRtlDemo() {
return (
<div dir="rtl" className="flex justify-center">
<div className="flex items-center gap-2">
<Button variant="glass">حفظ</Button>
<Button variant="outline">إلغاء</Button>
</div>
</div>
)
}Internationalized Labels
Keep labels outside the component and inject locale strings. Code blocks in docs stay `ltr` for readability. ```tsx import { Button } from "@glinui/ui" const copy = { en: { save: "Save", cancel: "Cancel" }, ar: { save: "حفظ", cancel: "إلغاء" } } export function SaveActions({ locale = "en" }: { locale?: "en" | "ar" }) { const t = copy[locale] return ( <div dir={locale === "ar" ? "rtl" : "ltr"} className="flex gap-2"> <Button>{t.save}</Button> <Button variant="ghost">{t.cancel}</Button> </div> ) } ```
Key and key-white
Tactile gradient keys. Pressed state moves 1px and scales to 0.98. Use iconNudge for an arrow that slides on hover.
import { Button } from "@glinui/ui"
import { ArrowRight } from "@phosphor-icons/react"
export function Demo() {
return (
<div className="flex flex-wrap gap-3">
<Button variant="key" iconNudge>Get started <ArrowRight /></Button>
<Button variant="key-white" size="sm">Docs</Button>
<Button variant="raised">Raised ring</Button>
</div>
)
}Accessibility
- Native `button` semantics by default.
- Visible 2px accent focus ring with offset on every variant (plain uses a neutral ring).
- `disabled` and `loading` block interaction, lower emphasis and set `aria-disabled` / `aria-busy`.
- Icon-only buttons need an `aria-label`.
- Supports `asChild` for semantic link rendering.
Keyboard Interactions
| Key | Action |
|---|---|
| Enter | Activate the button. |
| Space | Activate the button. |
ARIA Attributes
- `role="button"` native
- `aria-disabled` when disabled or loading
- `aria-busy` while loading
Reduced Motion
Transitions use tokenized timing and disable transform-heavy motion with `prefers-reduced-motion`.
Affected properties
transformopacitybox-shadowbackground-colorAPI Reference
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
variant | ButtonVariant | No | ambient (glinr) | Visual treatment. Omit for the ambient style default (glinr, plain under style=minimal, glass under style=glass). Legacy names default, primary, secondary, destructive, frosted and raised still work as aliases. |
tone | ButtonTone | No | neutral | Colour axis for the vocabulary variants. |
size | ButtonSize | No | md | Height and horizontal padding scale. `icon` is a square button, give it an `aria-label`. |
leadingIcon | React.ReactNode | No | - | Decorative icon before the label. |
trailingIcon | React.ReactNode | No | - | Decorative icon after the label. |
loading | boolean | No | false | Shows a spinner, sets aria-busy and blocks interaction. |
iconNudge | boolean | No | false | Nudges icons toward the trailing edge on hover and focus. |
asChild | boolean | No | false | Renders child element with button styles. |
Source
Import directly from the package or browse the source on GitHub. Click any file to view it.
Import
import { Button } from "@glinui/ui"Source Files
Dependencies
@glinui/uiGenerated API Snapshot
Beta
Generated API Snapshot
BetaAuto-extracted from TypeScript source in packages/ui/src/components/button.tsx. This section is in beta and may lag behind hand-curated docs. Regenerate with pnpm --filter @glinui/docs api:generate.
Generated: 2026-10-08T01:41:21.546Z · Full index: /docs/api-metadata
Primary Props Type
ButtonProps
ButtonProps
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
asChild | boolean | No | false | - |
iconNudge | boolean | No | - | - |
leadingIcon | React.ReactNode | No | - | Icon before the label (decorative). |
loading | boolean | No | false | Show a spinner, set `aria-busy` and block interaction. |
size | ButtonSize | No | - | - |
tone | ButtonTone | No | - | Colour axis for the vocabulary variants. |
trailingIcon | React.ReactNode | No | - | Icon after the label (decorative). |
variant | ButtonVariant | No | - | Look. Omit for the ambient default (`glinr`, or `plain` / `glass` under `style="minimal"` / `"glass"`). Legacy names (`default`, `primary`, `secondary`, `destructive`, `frosted`, `raised`) resolve to the vocabulary. |