Button
Triggers an action on the current screen.
import { Button } from "@/components/ui/button"
<Button>Save changes</Button>Examples
Example source
var P = window.PUI, h = React.createElement;
ReactDOM.createRoot(document.getElementById('preview')).render(h('div', null,
h('div', { className: 'pui-row' },
h(P.Button, null, 'Save changes'),
h(P.Button, { variant: 'secondary' }, 'Duplicate'),
h(P.Button, { variant: 'outline' }, 'Export CSV'),
h(P.Button, { variant: 'ghost' }, 'Cancel'),
h(P.Button, { variant: 'destructive' }, 'Delete project'),
h(P.Button, { variant: 'link' }, 'View history')),
h('div', { className: 'pui-row', style: { marginTop: 12 } },
h(P.Button, { size: 'sm' }, 'Small'),
h(P.Button, { size: 'lg' }, 'Large'),
h(P.Button, { size: 'icon', variant: 'outline', 'aria-label': 'Add' },
h(P.Icon, { name: 'plus' })),
h(P.Button, { disabled: true }, 'Disabled'))));
Usage
What you provide
children: a verb that says what happens ("Save changes", "Delete project"). Three words at most.variant:default|secondary|outline|ghost|destructive|link.size:xs(24px) |sm(32px) |default(36px) |lg(40px); icon-only:icon-xs|icon-sm|icon|icon-lg(requiresaria-label).asChildto render an<a>/<Link>that looks like a button.
When to use each variant
default(primary): the main action. One per visible area (card, dialog, toolbar).secondary: supporting actions next to the main one.outline: neutral actions in toolbars, filters, export.ghost: low-emphasis actions in lists and menus; "Cancel" in dialogs.destructive: irreversible actions only. Always confirm withAlertDialog.link: navigation inside text.
Do / don't
- Do: put a 16px Heroicons Micro icon (
@heroicons/react/16/solid) before the label;gap-2is built in. - Do: in footers, secondary on the left, primary on the right.
- Don't: add loose color classes (
bg-teal-600); change the look only throughvariant. - Don't: place two
defaultbuttons side by side.
Accessibility
- Built as a
<button>. If it runs an action on the same screen it is a button, even when it looks like a link; if it navigates, use a link (asChildwith<a>). - Hover, pressed and keyboard focus each give visible feedback; focus shows a solid 2px ring.
EnterandSpaceactivate it; the person can perceive that the action ran (a toast, a state change, a new screen).- A disabled button does not take keyboard focus and does nothing. Say nearby what is needed to enable it; don't disable a submit button as the only way to signal errors.
- The accessible name states the goal without ambiguity, also when several buttons share a screen. Icon-only buttons need
aria-label. - A button that opens a menu or hidden content exposes its state with
aria-expanded(andaria-controls).
Adapted from Magoo 1.6.0 by Marcelo Sales: Button (botao). Magoo is educational guidance; test the behavior in your implementation.
API
Install with:
npx @aline-aroucha/pui add button
<Button>
<Icon />
Label
</Button>
Button
| Name | Type | Default |
|---|---|---|
variant | "default" | "secondary" | "outline" | "ghost" | "destructive" | "link" | "default" |
Emphasis. Use one default button per area. | ||
size | "xs" | "sm" | "default" | "lg" | "icon-xs" | "icon-sm" | "icon" | "icon-lg" | "default" |
Height from 24px to 40px; icon sizes are square and need aria-label. | ||
asChild | boolean | false |
| Renders its child (e.g. a link) with the button style. | ||
disabled | boolean | false |
| Removes the button from the tab order and blocks the action. | ||
type | "button" | "submit" | "reset" | "submit" in forms |
| Native button type. | ||
onClick | (event: MouseEvent) => void | — |
| Runs on click, Enter and Space. | ||
childrenRequired | ReactNode | — |
| An optional icon, then a verb that says what happens. | ||