Action Button
Action Button joins a main action to a chevron that opens a menu of other actions.
import { ActionButton } from '@aksornproject/aksara-ui'Basic action button
children label the main button, and its onClick runs the main action. The chevron opens a menu built from the Menu.Item, Menu.Separator and Menu.Group parts you pass to menu. Put destructive items last. toggleLabel names the chevron for screen readers and is required.
Clicking the main button never opens the menu, and the chevron never runs the main action. ref, type, form and the other button attributes go to the main button. The chevron is always type="button", so it cannot submit a form. className goes to the group around both.
<ActionButton
toggleLabel="More send options"
onClick={() => {}}
menu={
<>
<Menu.Item leadingIcon={<IconContentEditCalendarSchedule />}>
Schedule send
</Menu.Item>
<Menu.Item leadingIcon={<IconInterfaceEssentialCopy />}>
Save as draft
</Menu.Item>
<Menu.Separator />
<Menu.Item
destructive
leadingIcon={<IconInterfaceEssentialTrashDeleteBin2 />}
>
Discard
</Menu.Item>
</>
}
>
Send
</ActionButton>Tones
tone="brand" fills both halves with the brand colour and is the default. tone="accent" draws white halves with a grey stroke for a secondary action. While the menu is open, the chevron half takes a darker fill and the chevron points up.
<div className="flex flex-wrap items-center gap-spacing-16">
<ActionButton tone="brand" toggleLabel="Other formats" menu={menu}>
Export PDF
</ActionButton>
<ActionButton tone="accent" toggleLabel="Other formats" menu={menu}>
Export PDF
</ActionButton>
</div>Sizes
size is sm, md or lg, 36, 40 and 44px tall. The default is md. The outer corners stay 10px at every size and the inner corners are square.
<div className="flex flex-wrap items-center gap-spacing-16">
<ActionButton size="sm" toggleLabel="More save options" menu={menu}>
Save
</ActionButton>
<ActionButton size="md" toggleLabel="More save options" menu={menu}>
Save
</ActionButton>
<ActionButton size="lg" toggleLabel="More save options" menu={menu}>
Save
</ActionButton>
</div>States
state="disabled" greys both halves. Neither takes focus, runs its action or opens the menu.
state="loading" draws a placeholder for content that is not ready yet. The label keeps its width but is hidden, the chevron goes and the group sets aria-busy. Both halves are disabled. It is not a progress indicator for a click.
Either state closes an open menu. An uncontrolled menu stays closed when the state returns to default.
<div className="flex flex-wrap items-center gap-spacing-16">
<ActionButton
state="disabled"
toggleLabel="More save options"
menu={menu}
>
Save
</ActionButton>
<ActionButton state="loading" toggleLabel="More save options" menu={menu}>
Save
</ActionButton>
<ActionButton
tone="accent"
state="loading"
toggleLabel="More save options"
menu={menu}
>
Save
</ActionButton>
</div>Placement
placement is bottom-start, the default, bottom-end, top-start or top-end. It picks the side of the whole button the menu opens on and the edge of the button it lines up with. The menu sits 4px away. It flips to the other side when the viewport has no room, so leave about 148px free on the menu side.
<div className="grid grid-cols-2 gap-spacing-24 py-spacing-160">
{PLACEMENTS.map((placement) => (
<ActionButton
key={placement}
placement={placement}
toggleLabel={`${placement} options`}
menu={
<>
<Menu.Item>Schedule send</Menu.Item>
<Menu.Item>Save as draft</Menu.Item>
</>
}
>
{placement}
</ActionButton>
))}
</div>Controlled
open, defaultOpen and onOpenChange control the menu like Menu.Root. A controlled open stays yours while the button is disabled or loading. If it is still true when the state returns to default, the menu opens again.
None yet
<div className="flex items-center gap-spacing-16">
<ActionButton
open={open}
onOpenChange={setOpen}
toggleLabel="More grade options"
onClick={() => setAction('Saved')}
menu={
<>
<Menu.Item
onClick={() => setAction('Saved, opening the next student')}
>
Save and open next
</Menu.Item>
<Menu.Item onClick={() => setAction('Saved as a draft')}>
Save as draft
</Menu.Item>
</>
}
>
Save grade
</ActionButton>
<Typography variant="body-medium" className="text-color-text-secondary">
{open ? 'Menu open' : action}
</Typography>
</div>Accessibility
- The group has
role="group"and holds two native buttons, so Tab reaches the main button, then the chevron. - The chevron has
aria-label={toggleLabel},aria-haspopup="menu"andaria-expanded. The chevron icon and the divider arearia-hidden. - The Menu module handles the keyboard. Escape, a press outside and choosing an item close the menu and return focus to the chevron.
state="loading"keeps the label as the main button's accessible name.- The focus ring on either half paints over its neighbour. Opening the menu does not add a ring.
API
ActionButton
Prop
Type