General · astro-ui · Button

Button

Action trigger with variants, sizes, loading states, and anchor mode.

Button is the workhorse of the library: a zero-JS action trigger that renders a <button> — or an <a> when href is set — with eleven variants, eight theme colors, seven sizes, four loading-spinner styles, and native form participation. Extra attributes forward untouched.

Features

  • 11 variants: primary, secondary, outline, ghost, destructive, link, solid, dashed, soft, plain, pure
  • 8 theme colors via `color`: primary, secondary, destructive, success, warning, info, carbon, accent
  • 7 sizes: xs, sm, md, lg, xl, 2xl, icon (icon requires ariaLabel)
  • Loading state with 4 spinner styles (ring, dots, pulse, spin) and 3 positions
  • Invalid state: error border + red focus ring + aria-invalid
  • Anchor mode via href — links keep rel/target support
  • Native form participation: form, formAction, formMethod='dialog'…
  • shape (rounded/square/circle/pill) and shadow (sm/md/lg) controls
  • leading / trailing icon slots
  • TypeScript-typed, zero JS, token-themed

Usage

---
import { Button } from "astro-ui";
---

<Button>Primary</Button>
<Button variant="outline" href="/docs">Read docs</Button>
<Button color="success">Approve</Button>
<Button loading loadingIcon="dots">Saving…</Button>

Demos

Basic

<Button>Primary</Button>

Variant

<Button>Primary</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="outline">Outline</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="destructive">Destructive</Button>
<Button variant="link">Link</Button>
<Button variant="solid">Solid</Button>
<Button variant="dashed">Dashed</Button>
<Button variant="soft">Soft</Button>
<Button variant="plain">Plain</Button>
<Button variant="pure">Pure</Button>

Color

<Button color="success">Success</Button>
<Button color="warning">Warning</Button>
<Button color="info">Info</Button>
<Button color="accent">Accent</Button>
<Button variant="outline" color="destructive">Outline danger</Button>
<Button variant="soft" color="info">Soft info</Button>

Size

<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="2xl">2XL</Button>
<Button size="icon" ariaLabel="Search">⌕</Button>

Loading

<Button loading>Submitting</Button>
<Button loading loadingPosition="center" loadingText="Saving…">Save</Button>
<Button loading loadingIcon="dots">Dots</Button>
<Button loading loadingIcon="pulse">Pulse</Button>

Invalid

<Button invalid>Invalid state</Button>
<Button variant="outline" invalid>Invalid outline</Button>
<Button variant="ghost" invalid>Invalid ghost</Button>

Shape & shadow

<Button variant="outline" shape="rounded">Round</Button>
<Button variant="outline" shape="circle" ariaLabel="Go">→</Button>
<Button variant="outline" shadow="md">Shadow md</Button>
<Button disabled>Disabled</Button>

API

Props

PropTypeDefaultDescription
variant"primary" | "secondary" | "outline" | "ghost" | "destructive" | "link" | "solid" | "dashed" | "soft" | "plain" | "pure""primary"Visual style; primary/secondary/destructive are solid shorthands.
color"primary" | "secondary" | "destructive" | "success" | "warning" | "info" | "carbon" | "accent"—Theme color for solid/soft and tinted outline-style variants; re-resolves legacy solid shorthands when set.
size"xs" | "sm" | "md" | "lg" | "xl" | "2xl" | "icon""md"icon is a square target — set ariaLabel.
type"button" | "submit" | "reset""submit"Native default so form-wrapped buttons submit (method=dialog works zero-JS). Pass button for standalone controls.
hrefstring—Renders <a> instead of <button>.
disabledbooleanfalseNative disabled (button) / aria-disabled (link).
loadingbooleanfalseDisables + shows spinner + aria-busy.
invalidbooleanfalseError border + red focus ring + aria-invalid.
loadingPosition"start" | "center" | "end""start"Spinner placement relative to content.
loadingTextstring—Replaces content when loadingPosition is center.
loadingIcon"ring" | "dots" | "pulse" | "spin""ring"Spinner visual style.
shape"rounded" | "square" | "circle" | "pill""rounded"Corner shape.
shadow"none" | "sm" | "md" | "lg""none"Box-shadow token.
fitContentbooleanfalseFit content width instead of inline-flex.
name / valuestring—Native form participation, buttons only.
form / formAction / formMethod / …string—Form override attributes, buttons only.
rel / targetstring—Links only.
classstring—Merged via cn(); Tailwind classes welcome.
idstring—Element id.
ariaLabelstring—Accessible name.

Slots

SlotDescription
defaultButton content (text, icons, etc.).
leadingRendered before the default content.
trailingRendered after the default content.

Accessibility

Implemented accessibility features — semantics, ARIA wiring, and keyboard behavior that ship with the component.

  • aria-busy announces the loading state; aria-disabled keeps disabled buttons focusable but inert
  • aria-invalid styling hook for form validation states
  • Loading spinner is aria-hidden with :focus-visible ring

FAQ

How do I handle clicks?

Attach listeners app-side (the button ships zero JS): document.getElementById('save')?.addEventListener('click', …). Forms work with zero JS via type="submit".

When should I use loadingText?

With loadingPosition="center" the content is replaced entirely — loadingText provides the replacement label, e.g. "Saving…".

Does href change semantics?

Yes — the component renders a semantic <a> with link styling and forwards rel/target. Form attributes are ignored in anchor mode.

Changelog

Version history mentioning Button.

v0.1.0 — 2026-11-11
added
  • General (7): Button, ButtonGroup, Command, Carousel, Clipboard, Kbd, Link.
  • Shared --ui-control-* scale (height, inline padding, radius) used by Button, Input, Select, Textarea, and InputGroup so controls align on a row.
changed
  • Button shape accepts rounded | square | circle | pill (default rounded).