Chip vs Button
Both components can look identical at small sizes and in ghost or outline variants. The difference is not visual — it is semantic. Choosing the wrong one produces the wrong ARIA contract and incorrect screen reader announcements.
The decision rule
Is this an action the user performs, or a label/tag/toggle representing a value?
- Action (save, delete, submit, navigate, open a modal) → use Button
- Label or tag (category, status badge, read-only value) → use Chip (non-interactive)
- Filter toggle or multi-select token (is this category active?) → use Chip (interactive or inside ChipGroup)
Button
Use Button when the element triggers an action. It always carries action intent — something happens when you activate it.
<Button label="Save Changes" />
<Button label="Delete" theme="danger" leftIconName="system/trash" />
<Button label="Open settings" variant="ghost" onClick={openSettings} />
ARIA pattern: Native <button> element. Screen reader announces: "Save Changes, button".
Good use cases:
- Form submit / save / cancel
- Delete, archive, publish
- Open a modal, drawer, or popover
- Navigation links (
hrefrenders as<a>with Button styling)
Chip
Use Chip when the element represents a value, category, or status — not an action.
Non-interactive (label / badge)
<Chip theme="success">Published</Chip>
<Chip theme="warning">Pending Review</Chip>
<Chip theme="secondary">Design</Chip>
No role, no tabIndex. Pure display — the value is present or it isn't.
Interactive (filter toggle)
<Chip interactive onClick={toggle} selected={active}>
Design
</Chip>
ARIA pattern: Gets role="button" and aria-pressed. Screen reader announces: "Design, button, pressed" / "not pressed". The question answered is "is this category currently on or off?" — not "what action does this trigger?"
Filter group (ChipGroup)
<ChipGroup value={activeFilter} onValueChange={setFilter}>
<Chip value="all">All</Chip>
<Chip value="active">Active</Chip>
<Chip value="done">Done</Chip>
</ChipGroup>
ChipGroup manages selection state, keyboard navigation, and aria-pressed across the set. Each chip represents membership in a category.
Side-by-side comparison
| Aspect | Button | Chip |
|---|---|---|
| Intent | Action — something happens | State — this value is present/selected |
| ARIA pattern | Native <button> |
<span role="button" aria-pressed> (when interactive) |
| Size scale | sm / md / lg (control heights) |
xs / sm / md (compact, inline) |
| Loading state | Yes | No |
| Group management | No | Yes — ChipGroup |
| Screen reader | "Save, button" | "Design, button, not pressed" |
Why the visual overlap exists
Chips and Buttons share the same visual vocabulary — border radius, padding scale, icon placement — because the appearance of a small pressable element should be consistent regardless of its semantic role. A ghost-styled element could be either a low-emphasis action (Button) or a filter token (Chip).
The visual similarity is intentional. The semantic difference is what must be decided correctly.
Common mistakes
Using a small Button as a filter pill. If you have "All / Active / Completed" toggles above a list, those are filters — use Chip inside ChipGroup. Using Button here misrepresents the state (no aria-pressed), loses the group's keyboard navigation, and announces "button" when "button, pressed/not pressed" is correct.
Using an interactive Chip for a primary CTA. "Save Changes" is an action — always use Button. A Chip CTA gets aria-pressed semantics, which implies a toggle, not an action. Screen readers will announce it as pressed/not pressed rather than a simple action trigger.
Choosing based on visual size. Button size="sm" and Chip size="md" can look nearly identical. The choice is never about which looks right — it is always about whether the element triggers an action or represents a value.