Button
Triggers an action or event. Use buttons for the most important actions on a page.
Import
import { Button } from '@aspect/react';
Variants
Use variant to communicate intent.
- Primary — the main action on a page. Use once per section.
- Secondary — alternative actions. Can appear alongside primary.
- Ghost — low-emphasis actions like "Cancel" or inline actions.
- Destructive — irreversible actions like "Delete".
<Button variant="primary">Save</Button>
<Button variant="secondary">Cancel</Button>
<Button variant="ghost">Skip</Button>
<Button variant="destructive">Delete</Button>
Sizes
<Button size="sm">Small</Button>
<Button size="md">Medium</Button>
<Button size="lg">Large</Button>
<Button size="icon" aria-label="Settings">⚙</Button>
Loading state
When loading is true, the button shows a spinner, sets aria-busy, and disables interaction.
<Button loading>Saving...</Button>
Polymorphic rendering
Use asChild to render the button styles on a different element, like a Next.js Link:
<Button asChild>
<a href="/dashboard">Go to dashboard</a>
</Button>
Props
| Prop | Type | Default | Description |
| ---------- | ------------------------------------------------------ | ----------- | -------------------------------------- |
| variant | 'primary' \| 'secondary' \| 'ghost' \| 'destructive' | 'primary' | Visual style |
| size | 'sm' \| 'md' \| 'lg' \| 'icon' | 'md' | Size preset |
| loading | boolean | false | Shows spinner, disables interaction |
| disabled | boolean | false | Disables the button |
| asChild | boolean | false | Render as child element via Radix Slot |
| type | 'button' \| 'submit' \| 'reset' | 'button' | HTML button type |
CSS class
For non-React usage, apply the BEM classes directly:
<button class="mizu-button mizu-button--primary mizu-button--md">Save</button>
The button uses component-scoped custom properties internally (--_bg, --_bg-hover, --_color, --_border). Each variant modifier overrides only the properties it needs — pseudo-classes (:hover, :active) are defined once on the base class.
Guidelines
Do:
- Use primary for the single most important action in a section
- Include a visible label — icon-only buttons must have
aria-label - Default to
type="button"to prevent accidental form submission
Don't:
- Don't use more than one primary button per section
- Don't disable buttons without explaining why (use a tooltip or help text)
- Don't use destructive variant for reversible actions
Accessibility
- Renders as a native
<button>element with proper keyboard handling (Enter, Space) typedefaults to"button"(not the browser default"submit")- Loading state sets
aria-busy="true"and disables the element - Icon-only buttons (
size="icon") requirearia-label - Focus ring uses 2px outline with
outline-offset: 2px - Tested with vitest-axe — paired
Inaccessible/Accessiblestories in Storybook