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. Prefer the Button Group catalog item for separators, text addons, and orientation.

Loading

Busy state. The control is not operable and announces busy to assistive tech.

Disabled

Not operable. Reduced opacity.

API Reference

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.
classNamestring—Additional classes.
labelString / @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.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.