Experience System
Components

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-system

In 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.

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.

PropTypeDefault
variantdefault | outline | secondary | ghost | destructive | linkdefault
sizedefault | xs | sm | lg | icon | icon-xs | icon-sm | icon-lgdefault
asChildbooleanfalse
classNamestring

Also accepts standard button attributes (type, disabled, aria-*, …).

Data attributeValues
data-slotbutton
data-variantmirrors variant
data-sizemirrors size

ButtonProps

TypeScript interface for Button props (variants plus native button attributes).

Accessibility

  • Focus styles use focus-visible so keyboard users get a clear ring; disabled buttons use disabled and reduced opacity.
  • In forms, aria-invalid on 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 asChild wraps an anchor, ensure href and link text describe the destination.

Source in the repo: packages/experience-system/src/components/Button/Button.tsx and variants.ts.