Button
A pressable control that performs an action. Native on every platform; shared intent, tokens, and naming.
Installation
npx felicityui add button --platform reactUsage
import { Button } from "@/components/ui/button"
<Button>Button</Button>Use Button for actions. Do not use it for navigation that should be a link on the web, or for a control that only reveals UI without performing work — those belong to later overlay primitives.
When to use
- Commit a form, confirm a dialog, or start an asynchronous job.
- Icon-only toolbars: use
IconButton(React) orsize: iconwith an accessibility label on native platforms.
When not to use
- In-content navigation on the web (
<a>/Link). - A row in a list that navigates — use the platform's list/navigation primitives.
Default
Primary call to action. Uses color.action.primary.
Secondary
High-emphasis alternative on the inverse surface.
Outline
Low emphasis with a border.
Ghost
Tertiary, no fill until hover or press.
Destructive
Dangerous or irreversible action.
Size
Use the size prop to change control height.
Icon Button
Square icon-only control. Requires an accessible name. On React that is size=icon via IconButton.
Link Button
Looks like a text link. It is still a button, not a navigation link.
Segmented Button
Mutually exclusive options. Use ButtonGroup with segmented. React uses pressed; SwiftUI and Compose use selected.
Button Group
Related actions in a row. Same Button — not a separate catalog component.
Loading
Busy state. The control is not operable and announces busy to assistive tech.
Disabled
Not operable. Reduced opacity.
API Reference
Button
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | primary | secondary | outline | ghost | destructive | link | primary | Visual emphasis. |
| size | sm | md | lg | icon | md | Control height. icon is square, for Icon Button. |
| disabled | boolean | false | Prevents interaction. |
| loading | boolean | false | Shows a pending state and prevents interaction. |
| pressed | boolean | false | Selected state for a segment. Sets aria-pressed on React. |
| selected | boolean | false | Selected state for a segment. Native equivalent of React pressed. |
| segmented | boolean | false | On ButtonGroup: attach child buttons into one segmented control. |
| className | string | — | Additional classes. |
| label | String / @Composable () -> Unit / View | — | Visible label. Required unless an accessible name is provided for icon-only. |
Accessibility
WCAG 2.2 AA
- Must be exposed as a button, not a clickable
div. - Visible focus indicator using the focus ring token.
- Disabled and loading states are not in the tab order as an operable control (disabled) or announce busy (loading).
- Name is the visible text, or an accessible name for icon-only.
- Touch target at least
24×24 CSS pixels(WCAG 2.2Target Size) and preferably44×44on touch platforms. - Honor
prefers-reduced-motionfor loading indicators.
Platform notes
React
Renders a native HTML button. No overlay, no router integration. Icon-only use the size=icon variant (Icon Button).
Server Componentcompatible. No client JavaScript is required.- Prefer
type="button"inside forms unless the control submits.
SwiftUI
SwiftUI Button. Destructive maps to Role.destructive. Hits the standard iOS minimum touch target.
- Respects
Dynamic Typeand the system color scheme. VoiceOverreads the title; icon-only buttons require anaccessibilityLabel.- Requires Xcode 15+ / iOS 17+. Add generated files to the Xcode target.
- Disable with
.disabled(_:). Segment selection uses selected: true.
Compose
Composable button using FelicityUI tokens for color, shape, and height. Interaction follows Material press/ripple conventions without adopting Material color roles.
TalkBackuses the text orcontentDescription.- Minimum height follows
size.control.*tokens (32dpsmall,40dpmedium,48dplarge). - Material 3 may still enforce a
48dpminimum touch target around small buttons. - selected marks a segment.
FelicityButtonGroup(segmented = true)clips and strokes as one control.