Triggers an action. Renders a <button>, or an <a> when href is set. Hue, fill and height are three separate props.

Last updated

Show code
<Button>Get started</Button>
<Button variant="outline" color="neutral">Learn more</Button>

Add

npx @ava-ui/apogee add button

Pulls in Spinner (for the loading state). Zero JS.

Usage

---
import Button from '@/components/apogee/Button/Button.astro'
---
<Button>Save</Button>
<Button variant="outline" color="neutral">Cancel</Button>
<Button color="critical">Delete</Button>
<Button href="/pricing">See pricing</Button>

API

@composable — exposed to Content Editors in AVA. Any other attribute passes through to the root <button> or <a>.

Prop Type Default Description
color 'primary' | 'neutral' | 'critical' | 'positive' | 'warning' 'primary' The hue.
variant 'solid' | 'outline' | 'ghost' | 'faded' | 'link' 'solid' The fill style; link is text-sized with no box.
size 'small' | 'medium' | 'large' | 'xlarge' 'medium' Height: 28 / 36 / 48 / 56px.
rounded boolean false Fully round ends.
raised boolean false Adds the raised shadow.
fullWidth boolean false Stretches to the container width.
iconOnly boolean false A square button for a single icon. Give it an aria-label.
href string — Renders an <a> instead of a <button>.
type 'button' | 'submit' | 'reset' 'button' Native button type (ignored with href).
loading boolean false Shows a centred spinner, hides the content and blocks interaction; the width does not change.
showTextWhileLoading boolean false Keeps the label visible next to the spinner while loading.
disabled boolean false Disables the control (aria-disabled on links).
highlighted boolean false Keeps the hover fill on: an active filter, an open menu’s trigger.
command 'show-modal' | 'close' | 'request-close' | 'show-popover' | 'hide-popover' | 'toggle-popover' — Invoker command sent to the commandfor target; works with no JS.
commandfor string — id of the dialog or popover the command targets.
class string — Extra classes, appended last.

Slots: start and end for icons; the default slot is the label.

For an inline text link in running text use Link; variant="link" is a button that only looks like one (a “Cancel” beside a solid “Save”).

The root carries data-icon-only and data-loading while those states apply, so you can style them from outside.

Variants × colors

Every variant for every color:

Show code
<Button color="primary">Primary</Button>
<Button color="neutral">Neutral</Button>
<Button color="critical">Critical</Button>
<Button color="positive">Positive</Button>
<Button color="warning">Warning</Button>

<Button variant="faded" color="primary">Primary</Button>
<Button variant="faded" color="neutral">Neutral</Button>
<Button variant="faded" color="critical">Critical</Button>
<Button variant="faded" color="positive">Positive</Button>
<Button variant="faded" color="warning">Warning</Button>

<Button variant="outline" color="primary">Primary</Button>
<Button variant="outline" color="neutral">Neutral</Button>
<Button variant="outline" color="critical">Critical</Button>
<Button variant="outline" color="positive">Positive</Button>
<Button variant="outline" color="warning">Warning</Button>

<Button variant="ghost" color="primary">Primary</Button>
<Button variant="ghost" color="neutral">Neutral</Button>
<Button variant="ghost" color="critical">Critical</Button>
<Button variant="ghost" color="positive">Positive</Button>
<Button variant="ghost" color="warning">Warning</Button>

Highlighted

Every variant with highlighted:

Show code
<Button variant="solid" highlighted>Solid</Button>
<Button variant="faded" highlighted>Faded</Button>
<Button variant="outline" highlighted>Outline</Button>
<Button variant="ghost" highlighted>Ghost</Button>

A button with the weight of a link: no box, underline on hover, every colour.

Show code
<Button variant="link">Cancel</Button>
<Button variant="link" color="neutral">Learn more</Button>
<Button variant="link" color="critical">Delete</Button>
<Button variant="link" color="positive">Approve</Button>
<Button variant="link" color="warning">Review</Button>

Sizes

Show code
<Button size="small">Small</Button>
<Button size="medium">Medium</Button>
<Button size="large">Large</Button>
<Button size="xlarge">Extra large</Button>
<Button size="small" iconOnly aria-label="Search"><Search /></Button>
<Button iconOnly aria-label="Search"><Search /></Button>
<Button size="large" iconOnly aria-label="Search"><Search /></Button>
<Button size="xlarge" iconOnly aria-label="Search"><Search /></Button>

Shape and width

Show code
<Button rounded>Rounded</Button>
<Button raised variant="outline" color="neutral">Raised</Button>
<Button rounded iconOnly aria-label="Add"><Plus /></Button>
<Button fullWidth>Full width</Button>

States

Disabled link
Show code
<Button>Save changes</Button>
<Button loading>Save changes</Button>
<Button loading showTextWhileLoading>Save changes</Button>
<Button disabled>Disabled</Button>
<Button variant="outline" color="neutral" disabled>Disabled</Button>
<Button href="/docs" disabled>Disabled link</Button>
  • Hover — solid fills move to their -highlighted token; faded, outline and ghost fills get the hue’s -faded-highlighted tint.
  • Active — scales to 98.5% (not under prefers-reduced-motion).
  • Focus — a 2px outline in the focus token (--outline-color-focus, the primary border by default), offset 2px; color="critical" focuses in the critical colour.
  • Loading — aria-busy="true" and data-loading; a centred Spinner fades the content out (it stays in the box and in the accessibility tree, so the width and the name hold) and the control is disabled. showTextWhileLoading keeps the label beside the spinner.
  • Disabled — the disabled fill and text tokens; a disabled link drops its href and carries aria-disabled="true".

Examples

With icons in the start and end slots:

Show code
<Button><Mail slot="start" /> Email us</Button>
<Button variant="outline" color="neutral">Next <ArrowRight slot="end" /></Button>

As a link, and the text link that is not a Button:

Show code
<Button href="/docs">Read the docs</Button>
<Link href="/docs">Read the docs</Link>

Notification count

A Badge over an icon button’s corner. Put the count in the label too, so it is announced.

Show code
<span class="relative inline-flex">
<Button variant="ghost" color="neutral" iconOnly aria-label="Notifications, 3 unread"><Bell /></Button>
<Badge rounded size="small" color="critical" variant="solid" class="pointer-events-none absolute -top-1 -end-1" aria-hidden="true">3</Badge>
</span>

Accessibility

  • A real <button> (or <a> with href), so keyboard activation, form submission and screen-reader naming are native.
  • iconOnly buttons need an aria-label; the icon alone has no name.
  • Loading sets aria-busy and disables the control, so the state is announced and a double submit is impossible.
  • A disabled link keeps its text, loses its href, and reports aria-disabled="true"; it is not focusable, matching a disabled button.
  • The active-state scale honours prefers-reduced-motion.

Tokens

Token Used for
--background-{hue} / -highlighted Solid fill and its hover
--background-{hue}-faded / -faded-highlighted Faded fill; hover of faded, outline and ghost
--foreground-{hue} Text of faded, outline and ghost
--on-{hue} Text on a solid fill
--border-{hue} Outline border; --border-primary is also the focus ring
--background-disabled / --foreground-disabled / --border-disabled Disabled state
--shadow-outline The neutral outline variant
--shadow-raised raised
--radius-sm / --radius-md Corners for small / the rest

Class merging

There is none. class is appended after the component’s own utilities, so a utility you pass wins only when it is more specific or later in the generated stylesheet. To change padding or colour, use the props; to add layout (mt-4, self-end), pass a class.

Do / Don’t

  • Do use one solid primary button per view for the main action; pair it with outline or ghost neutral buttons for the rest.
  • Do use color="critical" for destructive actions, and confirm them.
  • Don’t use a Button for navigation inside running text; use Link.
  • Don’t put more than one fullWidth button side by side.

Content

  • Verb first, sentence case, no ending period: “Save changes”, “Send invite”.
  • Two or three words. The label says what happens, not “OK” or “Submit”.