Components

Button

One pill, four variants, and three sizes for every action.

iOS
Android
Web
import ButtonRoot from "swshd/Button/Root";

ButtonRoot is the action surface: it composes Touchable for behavior and paints with a fill and text role from BUTTON_TOKENS. Add ButtonLabel and ButtonIcon as children, in the order they should appear. Reach for it for any pressable action — submit, cancel, delete, navigate.

Don't use it for sidebar navigation rows — that is SidebarItem.

Each variant paints with one fill role and one ink from BUTTON_TOKENS. Hover and pressed are the fill's own states.

fill.inverse · text.inverse
The default call to action.
fill.raised · text.primary
The supporting action beside a primary.
fill.ghost · text.primary
Text-only actions like Cancel or Skip.
fill.danger · text.onDanger
Actions that delete or leave.

Three heights, each paired with a mono label variant and an icon size. Set iconOnly for a circle with a ButtonIcon child; accessibilityLabel is then required.

sm · 32 · label-sm
md · 40 · label-md · default
lg · 48 · label-lg

Loading

A spinner replaces the label without changing the width. Presses are blocked.

Disabled

Touchable fades every variant to half opacity, including ghost.

Icon only

Set iconOnly and add ButtonIcon. An accessible label is required.

Full width

Stretches to the parent instead of hugging its content.

All components

Link

With href the web host is a real anchor.

Wrap text in ButtonLabel. Write labels in sentence case, 1–4 words ("Book a demo"). Mono renders them uppercase. ButtonIcon can appear before or after the label. ButtonRoot accepts elements, fragments, wrappers, and arrays with conditional entries.

ButtonLabel inherits its foreground color from ButtonRoot. For inline formatting, set nestable and inheritLineHeight on nested Text children.

import ButtonRoot from "swshd/Button/Root";
import ButtonLabel from "swshd/Button/Label";
import ButtonIcon from "swshd/Button/Icon";
import IconPlus from "swshd/icons/IconPlus";

<ButtonRoot variant="secondary" onPress={onAdd}>
  <ButtonIcon Icon={IconPlus} />
  <ButtonLabel>Add photos</ButtonLabel>
</ButtonRoot>

Keyboard

Tab reaches the button and shows the accent focus ring. Enter and Space press a button; with href it is a link, which Enter activates.

Icon-only

Set iconOnly for an icon control and provide an accessibilityLabel. The type requires the accessible name.

Links

With href, web renders a real anchor. A disabled link is aria-disabled: it does not navigate and does not fire onPress.

ButtonRoot

PropTypeDefaultDescription
variant"primary" | "secondary" | "ghost" | "destructive""primary"Which BUTTON_TOKENS entry paints the pill.
size"sm" | "md" | "lg""md"Height, padding, label variant, and icon size, from BUTTON_SIZES.
childrenReactElement | readonly (ButtonChild | null | undefined | boolean)[]—Compose elements or recursive element arrays in display order, including fragments, wrappers, and conditional list entries. Wrap text in ButtonLabel.
iconOnlybooleanfalseMakes the root a circle. Requires accessibilityLabel and a ButtonIcon child.
loadingbooleanfalseShows a spinner in place of the content, blocks presses, and fades the pill like disabled.
disabledbooleanfalseBlocks presses and fades to 0.5 opacity.
fullWidthbooleanfalseStretch to the parent's width instead of hugging the content.
onPress() => void—Fired on press — or on activation from the keyboard.
hrefstring—Turns the button into a link. In-app paths start with /.
target"_blank" | "_self" | "_parent" | "_top" | "framename"—Anchor target (web).
relstring—Anchor rel (web).
linkUrlSchemestring—Deep-link scheme tried before href (native).
hitSlopPressableProps["hitSlop"]—Extends the touch target (native).
accessibilityLabelstring—Screen-reader name. Required for icon-only buttons.
styleStyleProp<ViewStyle>—The pill surface.
containerStyleStyleProp<ViewStyle>—The host element.
testIDstring—data-testid on web, testID on native.

ButtonLabel

PropTypeDefaultDescription
childrenReactNode—The label, rendered in the root size's mono label variant. Its foreground color always comes from ButtonRoot; there is no color override.

ButtonIcon

PropTypeDefaultDescription
IconIconComponent—Glyph from swshd/icons/*. Its size and foreground color come from ButtonRoot.
LegacySWSHD
colorVariant="Contrast" / "Primary"variant="primary"
"Card" / "Match"variant="secondary"
"Hollow"variant="ghost"
"Destructive"variant="destructive"
"Palette" / "PaletteSecondary"not carried — album-palette fills stay in the app layer
sizeVariant="Large" / "Medium" / "Small", "Skinny*", "Mini"size="lg" / "md" / "sm"
"Flex" / "FlexLarge"fullWidth
"CircleLarge"iconOnly, size="lg", with a ButtonIcon child
roundRadius, iconSize, textPropsdropped — pill only; icon and label derive from size

Intentional differences: pill only; no per-size min/max widths (pass style or fullWidth); hugs content instead of centering; web hover is a token color, not a brightness filter.