# Button

> Buttons trigger actions, in several hierarchies, colour families and sizes, with icon, loading and toggle states.

Source: /docs/components/button

```tsx
import { Button } from '@aksornproject/aksara-ui'
```

## Basic button

`variant` sets the hierarchy. `primary` is the default and the strongest. Use one primary action per view.

```tsx
import { Button } from '@aksornproject/aksara-ui'

export default function ButtonVariants() {
  return (
    <>
      <Button>Primary</Button>
      <Button variant="secondary-brand">Secondary</Button>
      <Button variant="tertiary-brand">Tertiary</Button>
      <Button variant="link-brand">Link</Button>
    </>
  )
}
```

## Accent variants

Variants ending in `-accent` and `tertiary-inverted` use the accent set, a quieter neutral for secondary actions.

```tsx
import { Button } from '@aksornproject/aksara-ui'

export default function ButtonAccent() {
  return (
    <>
      <Button variant="secondary-accent">Secondary accent</Button>
      <Button variant="tertiary-accent">Tertiary accent</Button>
      <Button variant="link-accent">Link accent</Button>
    </>
  )
}
```

## Colors

`color` picks the family. `brand` and `sub-brand` take the brand variants. `information`, `success`, `warning` and `destructive` take `primary`, `secondary`, `tertiary` and `link`. TypeScript rejects a variant from the wrong set.

```tsx
import { Button } from '@aksornproject/aksara-ui'

export default function ButtonColors() {
  return (
    <>
      <Button color="sub-brand">Sub-brand</Button>
      <Button color="information">Information</Button>
      <Button color="success">Success</Button>
      <Button color="warning">Warning</Button>
      <Button color="destructive">Destructive</Button>
      <Button color="destructive" variant="secondary">
        Delete
      </Button>
    </>
  )
}
```

## Sizes

Six sizes, from `xs` (28px) to `huge` (60px). `md` is the default. Link variants keep the label line height.

```tsx
import { Button } from '@aksornproject/aksara-ui'

export default function ButtonSizes() {
  return (
    <>
      <Button size="xs">Extra small</Button>
      <Button size="sm">Small</Button>
      <Button size="md">Medium</Button>
      <Button size="lg">Large</Button>
      <Button size="xl">Extra large</Button>
      <Button size="huge">Huge</Button>
    </>
  )
}
```

## Icons

`leadingIcon` and `trailingIcon` render in sized boxes and inherit the text colour. `iconOnly` makes a square button. An icon-only button needs an `aria-label`.

```tsx
import {
  IconInterfaceEssentialArrow,
  IconInterfaceEssentialSearchLoupe,
  IconInterfaceEssentialTrashDeleteBin2,
} from '@aksornproject/aksara-icons'
import { Button } from '@aksornproject/aksara-ui'

export default function ButtonIcons() {
  return (
    <>
      <Button leadingIcon={<IconInterfaceEssentialSearchLoupe />}>
        Search
      </Button>
      <Button
        variant="secondary-brand"
        trailingIcon={<IconInterfaceEssentialArrow />}
      >
        Next
      </Button>
      <Button
        iconOnly
        variant="tertiary-accent"
        leadingIcon={<IconInterfaceEssentialTrashDeleteBin2 />}
        aria-label="Delete"
      />
    </>
  )
}
```

## Loading and disabled

`loading` hides the label, keeps the width and shows three dots. The button stays focusable with `aria-disabled` and `aria-busy`, and its click, key and pointer handlers do not run. `disabled` removes it from the tab order.

```tsx
import { Button } from '@aksornproject/aksara-ui'
import { useState } from 'react'

export default function ButtonLoading() {
  const [saving, setSaving] = useState(false)

  function save() {
    setSaving(true)
    setTimeout(() => setSaving(false), 2000)
  }

  return (
    <>
      <Button loading={saving} onClick={save}>
        Save
      </Button>
      <Button disabled>Disabled</Button>
    </>
  )
}
```

## Toggle

`aria-pressed` drives the selected style, so a toggle needs no extra prop.

```tsx
import { Button } from '@aksornproject/aksara-ui'
import { useState } from 'react'

export default function ButtonToggle() {
  const [bold, setBold] = useState(false)

  return (
    <Button
      variant="tertiary-accent"
      aria-pressed={bold}
      onClick={() => setBold((value) => !value)}
    >
      Bold
    </Button>
  )
}
```

## Composition

`render` swaps the element. Pass `nativeButton={false}` when it is not a `<button>`, so Base UI adds `role="button"`, the tab stop and Enter and Space activation. `className` merges last, so a token utility you pass replaces the default one.

```tsx
import { Button } from '@aksornproject/aksara-ui'

export default function ButtonRender() {
  return (
    <Button
      nativeButton={false}
      render={<a href="#composition" />}
      variant="secondary-brand"
    >
      Link styled as a button
    </Button>
  )
}
```

## Accessibility

- Without `type`, a Button inside a form submits it. Pass `type="button"` for other actions.
- `loading` keeps the label as the accessible name.
- The focus ring shows on keyboard focus only. `forceFocusVisible` shows it while related UI, such as an open menu, holds the focus.

## API

### Button

| Prop | Type | Description |
| --- | --- | --- |
| `color` | `"brand" \| "destructive" \| "information" \| "sub-brand" \| "success" \| "warning"` |  |
| `variant` | `"link" \| "link-accent" \| "link-brand" \| "primary" \| "secondary" \| "secondary-accent" \| "secondary-brand" \| "tertiary" \| "tertiary-accent" \| "tertiary-brand" \| "tertiary-inverted"` |  |
| `leadingIcon` | `ReactNode` |  |
| `trailingIcon` | `ReactNode` |  |
| `size` | `"huge" \| "lg" \| "md" \| "sm" \| "xl" \| "xs"` |  |
| `iconOnly` | `boolean` | Renders a square button. A `leadingIcon` replaces the children; without one, the children render inside the icon box. `trailingIcon` still renders. |
| `wrapperClasses` | `{ leadingIcon?: string; trailingIcon?: string; iconOnlyChildren?: string; }` |  |
| `forceFocusVisible` | `boolean` | Shows the focus ring without keyboard focus, for example while a menu opened by this button is open. |
| `loading` | `boolean` | Hides the label, keeps the width, and shows three dots. The button is `aria-disabled` and stays focusable. Click, key, and pointer handlers do not run, including those on a `render` element. |
| `render` | `ComponentRenderFn<HTMLProps, ButtonState> \| React.ReactElement<unknown, string \| React.JSXElementConstructor<any>>` | Replaces the rendered element. See Base UI `render`. |
| `nativeButton` | `boolean` | Set to `false` when `render` returns an element other than `<button>`, such as `<a>` or `<div>`. Base UI then adds `role="button"`, the tab stop, and Enter and Space activation. See Base UI `nativeButton`. |