Aksara UI

Button

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

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.

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

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

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

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

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

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

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

<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

Edit on GitHub

On this page