Button

A customizable button component with bouncy press animations, multiple variants, loading states, and icons.

Import

import PrettyUI

Basic Usage

PButton("Click Me") {
    print("Button tapped!")
}

Variants

PButton supports 6 different visual variants:

// Primary - Main call-to-action
PButton("Primary").variant(.primary)

// Secondary - Less prominent actions
PButton("Secondary").variant(.secondary)

// Destructive - Dangerous actions (delete, remove)
PButton("Delete").variant(.destructive)

// Outline - Bordered, transparent background
PButton("Outline").variant(.outline)

// Ghost - No background, minimal styling
PButton("Ghost").variant(.ghost)

// Link - Text-only, link-style
PButton("Learn More").variant(.link)
VariantBackgroundBorderBest For
.primaryPrimary colorNoneMain CTAs
.secondaryMuted colorNoneSecondary actions
.destructiveDestructive colorNoneDelete, cancel
.outlineCard colorBorderAlternative actions
.ghostTransparentNoneSubtle actions
.linkTransparentNoneNavigation, inline links

Sizes

Three sizes are available:

PButton("Small").size(.sm)   // Height: 36pt
PButton("Medium").size(.md)  // Height: 44pt
PButton("Large").size(.lg)   // Height: 52pt (default)
SizeHeightFont SizePadding
.sm36pt15pt16pt horizontal
.md44pt17pt24pt horizontal
.lg52pt19pt24pt horizontal

Full Width

Make a button span the full width of its container:

PButton("Full Width Button")
    .variant(.primary)
    .fullWidth()

Icons

Add SF Symbols icons to your buttons:

// Leading icon (default)
PButton("Create Wallet")
    .icon("plus.circle.fill")

// Trailing icon
PButton("Continue")
    .icon("arrow.right")
    .iconPosition(.trailing)

Loading State

Show loading state with various spinner styles:

// Basic loading
PButton("Save")
    .loading(isLoading)

// With custom loading text
PButton("Submit")
    .loading(isLoading, text: "Submitting...")

// Different spinner positions
PButton("Processing")
    .loading(isLoading)
    .loadingPosition(.leading)   // Before text
    .loadingPosition(.trailing)  // After text
    .loadingPosition(.replace)   // Replace text entirely

Spinner Styles

Customize the loading spinner:

PButton("Syncing")
    .loading(true)
    .spinnerStyle(.circular)  // Default rotating circle
    .spinnerStyle(.dots)      // Bouncing dots
    .spinnerStyle(.minimal)   // Simple arc
    .spinnerStyle(.orbit)     // Orbiting dot
    .spinnerStyle(.track)     // Circle with track

Hide Text During Loading

PButton("Save")
    .loading(true)
    .hideTextWhenLoading()  // Shows only spinner

Disabled State

PButton("Cannot Click")
    .disabled()

Custom Colors

Override the default colors:

// Using Color values
PButton("Custom Colors")
    .background(Color.purple)
    .foreground(Color.white)

// Using ColorTokens (theme-aware)
PButton("Theme Colors")
    .background(.success)
    .foreground(.successForeground)

// Both at once
PButton("Styled")
    .colors(background: Color.orange, foreground: Color.white)

Corner Radius

Control the button's corner radius:

PButton("Pill Shape").radius(.full)    // Default for Sky theme
PButton("Rounded").radius(.lg)
PButton("Subtle").radius(.md)
PButton("Sharp").radius(.none)

Haptic Feedback

By default, PButton provides haptic feedback on iOS. You can disable it:

PButton("No Haptics")
    .haptics(false)

Custom Sizing

Set explicit dimensions:

PButton("Fixed Width")
    .width(200)

PButton("Fixed Height")
    .height(60)

PButton("Fixed Size")
    .frame(width: 150, height: 50)

Font Weight

Customize the font weight:

PButton("Bold").fontWeight(.bold)
PButton("Light").fontWeight(.light)

Combining Modifiers

Chain multiple modifiers for complex buttons:

PButton("Create Wallet") {
    handleCreateWallet()
}
.variant(.primary)
.icon("plus.circle.fill")
.size(.lg)
.fullWidth()
.loading(isCreating)
.loadingPosition(.trailing)
.spinnerStyle(.dots)

Real-World Examples

Onboarding Buttons

VStack(spacing: 12) {
    PButton("Create a New Wallet") {
        // Handle create
    }
    .variant(.primary)
    .icon("plus")
    .fullWidth()
    
    PButton("Add an Existing Wallet") {
        // Handle import
    }
    .variant(.outline)
    .fullWidth()
}

Form Submit Button

PButton(isLoading ? "Saving..." : "Save Changes") {
    saveForm()
}
.variant(.primary)
.loading(isLoading)
.loadingPosition(.leading)
.disabled(!formIsValid)
.fullWidth()

Destructive Action

PButton("Delete Account") {
    showDeleteConfirmation = true
}
.variant(.destructive)
.icon("trash")

API Reference

Initializer

public init(_ title: String, action: @escaping () -> Void = {})

Modifiers

ModifierTypeDescription
.variant(_:)PButtonVariantVisual style
.size(_:)PButtonSizeButton size
.radius(_:)RadiusSizeCorner radius
.fullWidth(_:)BoolExpand to full width
.loading(_:)BoolShow loading state
.loading(_:text:)Bool, StringLoading with custom text
.loadingPosition(_:)LoadingPositionSpinner position
.hideTextWhenLoading(_:)BoolHide text during loading
.spinnerStyle(_:)PSpinnerStyleLoading spinner style
.spinnerSize(_:)PSpinnerSizeSpinner size
.disabled()Disable button
.icon(_:)StringSF Symbol name
.iconPosition(_:)IconPositionIcon placement
.haptics(_:)BoolEnable/disable haptics
.background(_:)Color or ColorTokenBackground color
.foreground(_:)Color or ColorTokenText/icon color
.colors(background:foreground:)Color/ColorTokenBoth colors
.width(_:)CGFloatFixed width
.height(_:)CGFloatFixed height
.frame(width:height:)CGFloat?Fixed dimensions
.fontWeight(_:)Font.WeightText weight

Enums

PButtonVariant

  • .primary — Main call-to-action
  • .secondary — Secondary actions
  • .destructive — Dangerous actions
  • .outline — Bordered style
  • .ghost — Minimal style
  • .link — Link style

PButtonSize

  • .sm — Small (36pt)
  • .md — Medium (44pt)
  • .lg — Large (52pt)
  • .icon — Icon-only (40pt)

LoadingPosition

  • .leading — Before text
  • .trailing — After text
  • .replace — Replace text

IconPosition

  • .leading — Before text
  • .trailing — After text

Accessibility

PButton automatically:

  • Supports VoiceOver with the button title as the accessibility label
  • Respects accessibilityReduceMotion for animations
  • Shows disabled state visually with reduced opacity