Skip to content
FelicityUI

Button

A pressable control that performs an action. Native on every platform; shared intent, tokens, and naming.

Installation

terminal
npx felicityui add button --platform react

Usage

example.tsx
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) or size: icon with 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.

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

PropTypeDefaultDescription
variantprimary | secondary | outline | ghost | destructive | linkprimaryVisual emphasis.
sizesm | md | lg | iconmdControl height. icon is square, for Icon Button.
disabledbooleanfalsePrevents interaction.
loadingbooleanfalseShows a pending state and prevents interaction.
pressedbooleanfalseSelected state for a segment. Sets aria-pressed on React.
selectedbooleanfalseSelected state for a segment. Native equivalent of React pressed.
segmentedbooleanfalseOn ButtonGroup: attach child buttons into one segmented control.
classNamestringAdditional classes.
labelString / @Composable () -> Unit / ViewVisible 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.2 Target Size) and preferably 44×44 on touch platforms.
  • Honor prefers-reduced-motion for 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 Component compatible. 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 Type and the system color scheme.
  • VoiceOver reads the title; icon-only buttons require an accessibilityLabel.
  • 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.

  • TalkBack uses the text or contentDescription.
  • Minimum height follows size.control.* tokens (32dp small, 40dp medium, 48dp large).
  • Material 3 may still enforce a 48dp minimum touch target around small buttons.
  • selected marks a segment. FelicityButtonGroup(segmented = true) clips and strokes as one control.