VayuUI

Button

An interactive element triggered by a user.

Installation

npx vayu-ui init
npx vayu-ui add button
npx vayu-ui add -t button

Usage

<Button variant="primary">Primary</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="outline">Outline</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="destructive">Destructive</Button>

<Button size="small" variant="secondary">Small</Button>
<Button size="medium" variant="secondary">Medium</Button>
<Button size="large" variant="secondary">Large</Button>

<Button variant="primary">
  <Button.Icon><Mail /></Button.Icon>
  <Button.Text>Email Login</Button.Text>
</Button>

<Button variant="outline" size="large">
  <Button.Text>Send</Button.Text>
  <Button.Icon size="large"><Send /></Button.Icon>
</Button>

<Button
  variant="primary"
  loading={Status.PENDING}
  loadingText="Sending..."
>
  Click to Load
</Button>

<Button variant="secondary">
  <Button.Icon><Bell /></Button.Icon>
  <Button.Badge value={3} variant="danger" />
</Button>

<Button variant="outline">
  <Button.Text>Messages</Button.Text>
  <Button.Badge value={12} max={9} variant="primary" position="top-right" />
</Button>

<Button variant="primary" disabled>Disabled</Button>
<Button variant="primary" fullWidth>Full Width Button</Button>

Anatomy

<Button variant="primary" size="medium">
  <Button.Icon>
    <Mail />
  </Button.Icon>
  <Button.Text>Button Label</Button.Text>
  <Button.Badge value={3} variant="danger" />
</Button>
  • Button — root element, renders a native <button>
  • Button.Icon — wraps an icon element with sized constraints
  • Button.Text — wraps button label text with truncation
  • Button.Badge — notification badge positioned relative to the button

Accessibility

  • Keyboard Navigation: Activated via Enter and Space keys
  • Focus Visible: Displays a focus-visible ring using design tokens when navigating with keyboard
  • ARIA Attributes: Sets aria-disabled, aria-busy, and aria-label as needed
  • Loading State: Announced via aria-live="polite" with descriptive loading text
  • Semantic HTML: Renders a native <button> with the correct type attribute
  • Ref Forwarding: Supports programmatic focus through ref

Screen reader behavior

  • In default state, the screen reader announces the button's text content or aria-label
  • When disabled, the button is announced as "dimmed" or "unavailable" depending on the screen reader
  • During loading (Status.PENDING), aria-busy="true" signals an in-progress operation, and the aria-live="polite" region announces the loadingText value
  • Button.Badge uses role="status" with aria-live="polite" to announce count changes (e.g., "3 notifications")
  • Button.Icon is hidden from screen readers by default (aria-hidden="true"); set the label prop to make it accessible as an image
  • When loading transitions to Status.SUCCESS or Status.REJECTED, the button returns to its default content and the screen reader re-announces the visible text

Component Folder Structure

Button/
├── Button.tsx        # Root button component with loading states
├── ButtonIcon.tsx    # Icon wrapper subcomponent
├── ButtonBadge.tsx   # Notification badge subcomponent
├── ButtonText.tsx    # Text wrapper subcomponent
├── types.ts          # Shared types, enums, and interfaces
├── index.ts          # Public API and compound component export
└── README.md         # Component-level documentation

Props

Button

PropTypeDefaultDescription
variant"primary" | "secondary" | "outline" | "ghost" | "destructive""primary"Visual style of the button
size"small" | "medium" | "large""small"Size of the button
loadingStatusStatus.IDLELoading state controlling spinner and text
fullWidthbooleanfalseWhether the button spans full container width
loadingTextstring"Loading"Text displayed during loading state
disabledbooleanfalseDisables the button
type"button" | "submit" | "reset""button"Native HTML button type
aria-labelstringAccessible label when no visible text is present

Button.Icon

PropTypeDefaultDescription
size"small" | "medium" | "large""small"Size of the icon container
childrenReactNodeThe icon element to render
labelstringAccessible label; makes the icon visible to screen readers

Button.Text

PropTypeDefaultDescription
childrenReactNodeText content of the button

Button.Badge

PropTypeDefaultDescription
valuenumber | stringBadge content — number or short label
maxnumber99Maximum number before truncating with +
variant"primary" | "danger" | "warning" | "info" | "success""danger"Visual style of the badge
position"top-right" | "top-left" | "inline-right" | "inline-left""top-right"Position relative to the button
size"small" | "medium" | "large""small"Size of the badge
showZerobooleanfalseWhether to render the badge when value is 0

Status

ValueDescription
Status.IDLEDefault state, no loading indicator
Status.PENDINGShows spinner and loading text
Status.SUCCESSOperation completed (returns to normal state)
Status.REJECTEDOperation failed (returns to normal state)

On this page