Skip to content
quartiledocsv0.1
GitHub

Button

Preview

Buttons for actions, with icon buttons, attached groups and a split button that opens related actions.

Example

<Button variant="primary" loading={saving} onClick={save}>Save view</Button>
<Button variant="signal">Apply filter</Button>
<Button>Export</Button>
<Button variant="ghost">Cancel</Button>
<Button variant="destructive">Delete</Button>

<IconButton icon={<IconDownload />} label="Download" />

<ButtonGroup aria-label="View">
  {['Chart', 'Table', 'Both'].map((v) => (
    <Button key={v} aria-pressed={view === v} onClick={() => setView(v)}>{v}</Button>
  ))}
</ButtonGroup>

<SplitButton
  variant="signal"
  label="Export PDF"
  menuLabel="More export formats"
  onClick={exportPdf}
  menu={[
    { label: 'Export CSV', icon: <IconFile />, meta: '.csv', onSelect: exportCsv },
    { label: 'Share link', icon: <IconShare />, onSelect: share },
  ]}
/>

Usage

Use primary (ink) for the one main action in a view and signal for actions that change what the data shows, such as applying a filter. secondary is the default. Sizes are 28, 36 and 44 px at comfortable density.

loading shows a spinner, keeps the width and blocks clicks. SplitButton disables its chevron while loading too. Buttons spread native button attributes, and default to type="button".

Props

Button

PropTypeDefaultDescription
variant'primary' | 'signal' | 'secondary' | 'ghost' | 'destructive'ButtonVariant—primary (ink) is the one primary action per view. signal is for actions that change what the data shows: apply, select, share a view.
size'sm' | 'md' | 'lg'ButtonSize—sm 28 · md 36 · lg 44 px at comfortable density. Inside a ButtonGroup, defaults to the group's size.
loadingbooleanfalseShows a spinner, keeps the width, and blocks clicks.
iconReactNode—Icon before the label.
iconEndReactNode—Icon after the label.

Also accepts the props of ButtonHTMLAttributes<HTMLButtonElement>, spread onto the root element.

IconButton

PropTypeDefaultDescription
iconrequiredReactNode—Icon before the label.
labelrequiredstring—Required: becomes the accessible name (aria-label).
loadingboolean—Shows a spinner, keeps the width, and blocks clicks.
variant'primary' | 'signal' | 'secondary' | 'ghost' | 'destructive'ButtonVariant—primary (ink) is the one primary action per view. signal is for actions that change what the data shows: apply, select, share a view.
size'sm' | 'md' | 'lg'ButtonSize—sm 28 · md 36 · lg 44 px at comfortable density. Inside a ButtonGroup, defaults to the group's size.

Also accepts the props of Omit<ButtonProps, 'children' | 'iconEnd' | 'aria-label'>, spread onto the root element.

ButtonGroup

PropTypeDefaultDescription
size'sm' | 'md' | 'lg'ButtonSize'md'Size for every button in the group (a button's own size still wins).
variant'primary' | 'signal' | 'secondary' | 'ghost' | 'destructive'ButtonVariant—Default variant for the buttons in the group.
attachedbooleantrueAttached (default): buttons share borders and only the outer corners are rounded. A button with aria-pressed="true" renders as the selected one, so the group works as a toggle set.

Also accepts the props of HTMLAttributes<HTMLDivElement>, spread onto the root element.

SplitButton

PropTypeDefaultDescription
labelrequiredReactNode—Label of the primary action.
onClick(event: MouseEvent<HTMLButtonElement>) => void——
menurequiredSplitButtonItem[] | ReactNode—Menu items (rendered as a role="menu" list) or any custom content for the popover.
variant'primary' | 'signal' | 'secondary' | 'ghost' | 'destructive'ButtonVariant'secondary'—
size'sm' | 'md' | 'lg'ButtonSize'md'—
iconReactNode——
loadingboolean——
disabledboolean——
menuLabelstring'More options'Accessible name of the chevron trigger.
placement'bottom-start' | 'bottom' | 'bottom-end' | 'top-start' | 'top' | 'top-end' | 'right-start' | 'left-start'Placement'bottom-end'—
classNamestring——

SplitButtonItem

PropTypeDefaultDescription
labelrequiredReactNode——
onSelect() => void——
iconReactNode——
metaReactNode—Right-aligned mono hint, e.g. a file type or shortcut.
disabledboolean——

Accessibility

  • Native button elements with a visible focus ring.
  • IconButton requires label, which becomes its aria-label.
  • In a ButtonGroup used as a toggle, set aria-pressed on each button; the pressed one is styled from it.
  • SplitButton's chevron is a separate button named by menuLabel, opening a role="menu" list.
Report an issueBuilt Oct 21, 2026