# Button

> The fundamental action: 5 variants, 4 sizes, adaptive press depth, in-place loading spinner, and spring width morphing.

A high-craft standard button engineered with hairline borders, zero drop shadows, adaptive aspect-ratio press scale, in-place loading state that retains keyboard focus, and smooth width morphing on label changes without external animation libraries.

## Install

```bash
npx shadcn@latest add https://hairlineui.com/r/button.json
```

Needs React 19, Tailwind CSS v4 and shadcn/ui theme variables (any style, light and dark). Icons from lucide-react.

- npm: none
- Hairline UI components it needs (the CLI installs them too): `ui-interaction-motion`, `text-morph`
- Docs and live preview: https://hairlineui.com/ui/button
- Registry item (source): https://hairlineui.com/r/button.json

## Usage

```tsx
import { Button } from "@/components/ui/button";

<Button variant="primary" size="md">Save changes</Button>
<Button variant="secondary" loading={isSaving}>Deploy</Button>
```

## With an async action

Retain keyboard focus and provide smooth in-place feedback during async requests.

```tsx
"use client";
import { useState } from "react";
import { Button } from "@/components/ui/button";

export function SubmitAction() {
  const [loading, setLoading] = useState(false);

  const handleSubmit = async () => {
    setLoading(true);
    try {
      await fetch("/api/submit", { method: "POST" });
    } finally {
      setLoading(false);
    }
  };

  return (
    <Button variant="primary" loading={loading} onClick={handleSubmit}>
      Publish post
    </Button>
  );
}
```

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `variant` | `"primary" \| "secondary" \| "outline" \| "ghost" \| "destructive" \| "link"` | `"primary"` | Visual appearance of the button. |
| `size` | `"xs" \| "sm" \| "md" \| "lg" \| "icon"` | `"md"` | Height and typographic scale. |
| `loading` | `boolean` | `false` | Shows an in-place spinner while retaining keyboard focus. |
| `icon` | `ReactNode` | – | Leading icon slot. |
| `trailingIcon` | `ReactNode` | – | Trailing icon slot. |
| `disabled` | `boolean` | `false` | Disables user interaction. |

## States

- **idle**: Resting button with crisp 1px hairline border.
- **pressed**: Tactile press depth tuned to the button’s aspect ratio.
- **loading**: In-place centered spinner, aria-busy, click-swallowing while retaining keyboard focus.
- **disabled**: Subtle 50% opacity and disabled cursor.

## What it handles

Every product needs buttons, but most libraries ship generic flat elements that snap abruptly when text changes and scale clumsily when clicked. This button delivers Apple-grade tactile feedback in 0 kB of runtime animation.

- **Adaptive press scale**: Small icon buttons push deeper (0.95), wide buttons push subtly (0.985), and floating anchor triggers never scale so popover bounds remain pixel-perfect.
- **Smooth width morphing**: When children or labels change, the button measures the new natural width and glides smoothly without jumping surrounding layout.
- **In-place loading state**: Loading morphs a centered spinner into place while keeping the button focusable (aria-disabled) so keyboard users never lose their place.
- **Hairline 1px precision**: Pure 1px border tokens with zero fuzzy drop shadows and full dark mode compatibility.

## Replaces

generic shadcn button, framer-motion whileTap, abrupt layout snaps, loading focus drop

## Where it fits

- **Form submits**: Primary actions with in-place loading state that retains keyboard focus.
- **Toolbars & headers**: Secondary and ghost buttons with hairline borders and adaptive press feel.
- **Destructive dialogs**: Clean destructive actions with clear visual distinction and no blurry glow.
- **Icon triggers**: Concentric square icon buttons with deeper tactile push feedback.

## Notes

- No external animation libraries: uses CSS transitions and native layout geometry.
- Anchor triggers for Popovers and Dropdowns are guarded from press scaling to preserve positioning geometry.
- Strict 1px hairline styling with zero drop shadows.

License: MIT (https://hairlineui.com/license#components)
