Button (Basic)
Native button that triggers an action. Supports text-only, icon+text, and icon-only labeling patterns.
Selection
The criteria an agent checks before retrieving this component.
Use when
- Use when the user triggers an immediate action (e.g., "Save", "Continue", "Dismiss").
Try a different component when
- Do not use when the control navigates to a new URL (use
link.basic). - Do not use when the control represents an on/off pressed state (use
button.toggle). - Do not use when the control opens a menu (use
menu.basic).
Must Haves
Non-negotiable structure. Every generated instance must satisfy these rules.
- Use a native
<button>for built-in semantics and keyboard behavior.- A custom implementation with
role="button"is appropriate only when a native button cannot be used. - If
role="button"is used instead of a native<button>, addtabindex="0"and keyboard support for Enter and Space, ensuring Space prevents page scrolling while activating the control.
- A custom implementation with
- The button has an accessible name that describes its purpose or action.
- When the button has visible text, the visible text serves as the accessible name.
- When additional context is needed beyond the visible text, add it via
aria-label,aria-labelledby, or offscreen text. The visible text appears at the start of the accessible name. - For icon-only buttons, provide an accessible name using
aria-labeloraria-labelledby. - Icons within buttons must be decorative (
aria-hidden="true"). - If the action is unavailable, disable the button using the native
disabledattribute. (It becomes unfocusable and non-interactive.) - Ensure a visible focus state (e.g., a 2px solid outline offset by 1-2px) around the button.
Donts
Avoid these accessibility and UX barriers.
- Do not build a button out of a
<div>or<span>withrole="button"unless you absolutely must; native<button>is the baseline. - Do not create icon-only buttons without an accessible name (no unlabeled icons).
- Do not use
aria-labelthat conflicts with (or is wildly different from) the visible label text. Accessible names should at least begin with the visible label. - Do not hide focus outlines without providing a strong custom focus style.
Customizable
Alternatives and options that give the AI agent some room to move.
- No accessibility-relevant variations beyond the Must Haves above.
Golden Pattern
The tested reference implementation. Agents start from this shape and adapt to the developer’s codebase and context.
export function ButtonBasicDemo() {
return (
<div>
{/* Text-only */}
<button type="button" onClick={() => alert("Saved")}>
Save
</button>
{/* Icon + text */}
<button type="button" onClick={() => alert("Downloaded")}>
<span aria-hidden="true">[icon]</span> Download
</button>
{/* Icon-only (must have accessible name) */}
<button
type="button"
aria-label="Open settings"
onClick={() => alert("Settings")}
>
<span aria-hidden="true">[icon]</span>
</button>
{/* Disabled */}
<button type="button" disabled onClick={() => alert("Won't fire")}>
Disabled
</button>
</div>
);
}
Acceptance Checks
The component’s test spec — an optional body of checks for verification.
- Tab to the button: a visible focus indicator is present.
- Press Space or Enter: the button activates.
- Text-only button: screen reader announces the visible label.
- Icon+text button: screen reader announces the text label (icon is not redundantly announced).
- Icon-only button: screen reader announces the
aria-label(e.g., "Open settings"). - Disabled button:
- Cannot be activated by click/keyboard.
- Is not focusable when
disabledis set.