Theming

PrettyUI features a powerful, environment-based theming system that makes it easy to create consistent, beautiful interfaces across your entire app.

How Theming Works

Themes in PrettyUI are applied using a view modifier that propagates through the SwiftUI environment. Every PrettyUI component automatically picks up the current theme's colors, spacing, typography, and shadows.

ContentView()
    .prettyTheme(.sky)  // Apply theme to entire view hierarchy

Built-in Themes

PrettyUI comes with 4 professionally designed themes, each with carefully crafted light and dark mode variants.

Sky

The default theme featuring a vibrant cyan-blue primary color. Perfect for social and communication apps.

.prettyTheme(.sky)
TokenLight ModeDark Mode
Primary#1DA1F2#1DA1F2
Background#F8F9FA#0D0D0D
Card#FFFFFF#1C1C1E
Border#E8EAED#38383A

Indigo

A theme with vibrant purple/indigo accent colors. Ideal for productivity and finance apps.

.prettyTheme(.indigo)
TokenLight ModeDark Mode
Primary#6366F1#818CF8
Background#FFFFFF#0F0D1A
Card#FFFFFF#1E1B4B
Border#E5E7EB#312E81

Emerald

A theme with refreshing teal/emerald colors. Great for health and nature apps.

.prettyTheme(.emerald)
TokenLight ModeDark Mode
Primary#10B981#34D399
Background#FFFFFF#022C22
Card#FFFFFF#064E3B
Border#D1D5DB#065F46

Amber

A warm theme with orange/amber accents. Perfect for creative and entertainment apps.

.prettyTheme(.amber)
TokenLight ModeDark Mode
Primary#F59E0B#FBBF24
Background#FFFBEB#1C1917
Card#FFFFFF#292524
Border#FDE68A#92400E

Applying Themes

App-Wide Theme

Apply a theme at the root of your app to affect all views:

import SwiftUI
import PrettyUI

@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
                .prettyTheme(.sky)
        }
    }
}

Per-View Themes

You can apply different themes to specific views:

struct ContentView: View {
    var body: some View {
        HStack {
            // Sky-themed sidebar
            Sidebar()
                .prettyTheme(.sky)
            
            // Indigo-themed content
            MainContent()
                .prettyTheme(.indigo)
        }
    }
}

Dynamic Theme Switching

Change themes at runtime using state:

struct SettingsView: View {
    @AppStorage("selectedTheme") private var themeName = "sky"
    
    var currentTheme: PrettyTheme {
        switch themeName {
        case "indigo": return .indigo
        case "emerald": return .emerald
        case "amber": return .amber
        default: return .sky
        }
    }
    
    var body: some View {
        VStack {
            Picker("Theme", selection: $themeName) {
                Text("Sky").tag("sky")
                Text("Indigo").tag("indigo")
                Text("Emerald").tag("emerald")
                Text("Amber").tag("amber")
            }
            .pickerStyle(.segmented)
        }
        .prettyTheme(currentTheme)
    }
}

Creating Custom Themes

You can create your own theme by configuring color tokens, spacing, radius, shadows, and component configs.

Basic Custom Theme

extension PrettyTheme {
    static let myCustomTheme = PrettyTheme(
        colors: ColorTokens(
            primary: Color(hex: "#FF6B6B"),
            primaryForeground: Color(hex: "#FFFFFF"),
            secondary: Color(hex: "#FFE8E8"),
            secondaryForeground: Color(hex: "#9B2C2C"),
            accent: Color(hex: "#FF8787"),
            accentForeground: Color(hex: "#FFFFFF"),
            destructive: Color(hex: "#EF4444"),
            destructiveForeground: Color(hex: "#FFFFFF"),
            success: Color(hex: "#22C55E"),
            successForeground: Color(hex: "#FFFFFF"),
            warning: Color(hex: "#F59E0B"),
            warningForeground: Color(hex: "#18181B"),
            background: Color(hex: "#FFFFFF"),
            foreground: Color(hex: "#1A1A1A"),
            muted: Color(hex: "#F5F5F5"),
            mutedForeground: Color(hex: "#6B7280"),
            card: Color(hex: "#FFFFFF"),
            cardForeground: Color(hex: "#1A1A1A"),
            border: Color(hex: "#E5E5E5"),
            input: Color(hex: "#E5E5E5"),
            ring: Color(hex: "#FF6B6B")
        )
    )
}

// Usage
ContentView()
    .prettyTheme(.myCustomTheme)

Theme with Dark Mode

extension PrettyTheme {
    static let coral = PrettyTheme(
        colors: ColorTokens(
            primary: Color(hex: "#FF6B6B"),
            // ... light mode colors
        ),
        darkColors: ColorTokens(
            primary: Color(hex: "#FF8787"),
            // ... dark mode colors
        )
    )
}

Theme with Custom Component Configs

Customize how specific components look and behave:

extension PrettyTheme {
    static let rounded = PrettyTheme(
        colors: .light,
        radius: RadiusTokens(
            none: 0,
            sm: 8,
            md: 12,
            lg: 16,
            xl: 24,
            xxl: 32,
            full: 9999
        ),
        components: ComponentConfigs(
            button: ButtonConfig(
                defaultVariant: .primary,
                defaultSize: .lg,
                radius: .full,  // Pill-shaped buttons
                animationDuration: 0.25,
                showPressAnimation: true
            ),
            card: CardConfig(
                radius: .xxl,   // Extra rounded cards
                shadow: .md,
                padding: .lg,
                showBorder: false,
                borderWidth: 0
            )
        )
    )
}

Accessing Theme Values

Access the current theme in your custom views:

struct CustomView: View {
    @Environment(\.prettyTheme) private var theme
    @Environment(\.colorScheme) private var colorScheme
    
    var body: some View {
        let colors = theme.colors(for: colorScheme)
        
        Rectangle()
            .fill(colors.primary)
            .frame(width: theme.spacing.lg, height: theme.spacing.lg)
            .cornerRadius(theme.radius.md)
            .shadow(
                color: theme.shadows.md.color,
                radius: theme.shadows.md.radius,
                x: theme.shadows.md.x,
                y: theme.shadows.md.y
            )
    }
}

Best Practices

  1. Apply themes at the root — Set your theme at the app level for consistency
  2. Use semantic colors — Use primary, success, warning, destructive instead of hardcoded colors
  3. Support both color schemes — Always provide darkColors for a complete experience
  4. Test all themes — If supporting multiple themes, test each one thoroughly
  5. Keep it simple — Start with built-in themes before creating custom ones