Skip to content

Button

The primary interactive element in Stratum. Four variants enforce the Color Scarcity Principle — one accent hue, three ways to use it based on emphasis, plus a destructive variant for dangerous actions.

When to use: Any user-initiated action. Choose the variant based on how much visual weight the action deserves.

When not to use: Navigation between screens (use TopNavBar back button or tab bar instead). Display-only labels (use Badge or Text).

import { Button } from '@/components';
<Button variant="primary" onPress={handleSubmit}>
Save Changes
</Button>
PropTypeDefaultDescription
variant'primary' | 'secondary' | 'ghost' | 'destructive''primary'Visual weight and color treatment
size'sm' | 'md' | 'lg''md'Height and padding
shape'default' | 'pill'theme.buttonShapeBorder radius — omit to follow the theme
isElevatedbooleanfalseAdds theme.shadow.sm for a floating effect
isLoadingbooleanfalseReplaces label with spinner; blocks interaction
isDisabledbooleanfalseReduces opacity; blocks interaction
onPress() => voidCalled on tap
childrenReactNodeButton label (required)
testIDstringFor testing
// Primary — the single most important action on a screen. One per screen.
<Button variant="primary" onPress={submit}>Save</Button>
// Secondary — supporting action alongside a primary. Uses outlined accent.
<Button variant="secondary" onPress={cancel}>Cancel</Button>
// Ghost — low-emphasis action in dense UI. No fill, no border.
<Button variant="ghost" onPress={skip}>Skip for now</Button>
// Destructive — irreversible delete / remove actions only.
<Button variant="destructive" onPress={deleteAccount}>Delete Account</Button>
<Button size="sm" onPress={fn}>Small</Button> {/* Inline actions, dense UI */}
<Button size="md" onPress={fn}>Medium</Button> {/* Default — most use cases */}
<Button size="lg" onPress={fn}>Large</Button> {/* Hero CTAs, form submit */}
<Button isLoading onPress={fn}>Saving…</Button>
<Button isDisabled onPress={fn}>Unavailable</Button>

Do: One primary button per screen. Secondary sits alongside it.

<View style={{ flexDirection: 'row', gap: 8 }}>
<Button variant="secondary" onPress={cancel}>Cancel</Button>
<Button variant="primary" onPress={save}>Save Changes</Button>
</View>

Do: Reserve destructive for irreversible actions.

<Button variant="destructive" onPress={deleteAccount}>Delete Account</Button>

Don’t: Two primary buttons — visual hierarchy collapses.

// ✗
<Button variant="primary" onPress={a}>Cancel</Button>
<Button variant="primary" onPress={b}>Save</Button>

Don’t: Use destructive for reversible actions like logout.

// ✗ Logout is reversible — use ghost or secondary
<Button variant="destructive" onPress={logout}>Log Out</Button>
ThemeShapeBorderShadowBackground
SlatePillSoftBlack accent
ObsidianSharp (0 radius)2–4pxOffsetBrand accent
QuartzPillGlowElectric blue accent
  • Role: button
  • Disabled state removes focus and touch feedback
  • Loading state announces the label text; the spinner is decorative