Button
Triggers an action or navigates when used as a link. Supports variants, sizes, and icon layouts.
Installation
The component is exported from @by/experience-system. Add the package with your package manager:
pnpm add @by/experience-systemIn this monorepo, depend on the workspace package (for example via workspace:* or your catalog) so imports resolve to packages/experience-system.
Composition
Use the following composition to build a Button:
Button
└── children (label, icons, Spinner, …)Set data-icon="inline-start" or data-icon="inline-end" on an icon (or Spinner) so padding stays balanced. With asChild, the single child receives button styles via Radix UI Slot—use a real <a> for navigation and pass href.
Usage
import { Button } from '@by/experience-system';Button is a client component ('use client'). Use it inside a Client Component or a dynamic import when using the Next.js App Router. In forms, set type="button" when the control should not submit the form (native button defaults vary by context).
<Button variant="outline" size="md">
Click me
</Button>Examples
Fill
The default variant and size suit most primary actions. The live preview at the top of this page uses the same example (button-default).
Variants
The package default is variant="outline" with color="neutral". Use fill, soft, ghost, and link for other chrome; pair variant="fill" with color="error" (or other semantic color values) for destructive or status-driven actions.
Sizes
xs, sm, md (baseline height), and lg scale height and padding for text buttons (see Icon-only for square sizes).
With icon
Set data-icon on the icon so the button adjusts padding for leading or trailing icons.
Icon-only
Use size="icon-xs", icon-sm, icon, or icon-lg for square icon buttons. Always provide an accessible name with aria-label.
As link
With asChild, the child element receives button styles—use a real <a> for navigation and pass href.
Loading
For in-progress actions, disable the control and show Spinner from @by/experience-system with data-icon="inline-start" or data-icon="inline-end" so spacing matches other icon buttons.
Shape
The default radius follows the size scale. For pill or circular affordances, pass className (for example rounded-full) on Button—the same pattern applies to icon-only buttons.
RTL
Layout uses logical spacing and icon slots; set dir="rtl" on a parent (for example html or a layout region) so inline icons and text align correctly. For project-wide RTL configuration, follow your app’s i18n and layout guidelines.
API Reference
Subsection titles name the exports from @by/experience-system. With asChild, props merge into the child via Radix UI Slot.
Button
Renders a button by default, or merges into the child when asChild is set.
| Prop | Type | Default |
|---|---|---|
variant | default | outline | secondary | ghost | destructive | link | default |
size | default | xs | sm | lg | icon | icon-xs | icon-sm | icon-lg | default |
asChild | boolean | false |
className | string | — |
Also accepts standard button attributes (type, disabled, aria-*, …).
| Data attribute | Values |
|---|---|
data-slot | button |
data-variant | mirrors variant |
data-size | mirrors size |
ButtonProps
TypeScript interface for Button props (variants plus native button attributes).
Accessibility
- Focus styles use
focus-visibleso keyboard users get a clear ring; disabled buttons usedisabledand reduced opacity. - In forms,
aria-invalidon the control surfaces error border and ring styles from the variant layer. - Icon-only buttons need a discernible name—use
aria-label(or visible text) so the purpose is clear to assistive technologies. - When
asChildwraps an anchor, ensurehrefand link text describe the destination.
Source in the repo: packages/experience-system/src/components/Button/Button.tsx and variants.ts.