> ## Documentation Index
> Fetch the complete documentation index at: https://docs.swiftaiboilerplate.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Design System Module

> Production design tokens and UI components with 5 built-in themes. Token-based system ensures visual consistency.

<Info>
  **Full technical details** in your project at `/docs/modules/DesignSystem.md`
</Info>

## What You Get

* ✅ **Token system** - Colors, spacing, typography, radius (never hardcode values)
* ✅ **5 themes** - System, Light, Dark, Aurora, Obsidian
* ✅ **Liquid Glass primitive** - `SAIGlass` with iOS 26 glass and iOS 17–25 Material fallback (new in v2.0)
* ✅ **Premium components** - Buttons, inputs, cards, bubbles
* ✅ **BrandConfig** - Single place to customize app identity
* ✅ **Accessibility** - Dynamic Type, VoiceOver, Reduce Motion
* ✅ **Gradients & motion** - Premium visual effects

**Time saved: 20-32 hours** of design system, theming infrastructure, component library, and testing.

## Liquid Glass

v2.0 introduces `SAIGlass`, a Liquid Glass primitive that uses the iOS 26 `Glass` material on iOS 26+ and falls back to SwiftUI `Material` on iOS 17–25. Same call sites, progressive enhancement at runtime.

**Location:** `Packages/DesignSystem/Sources/DesignSystem/Materials/SAIGlass.swift`

### SAIGlassStyle

```swift theme={null}
public enum SAIGlassStyle {
    case regular  // Standard glass surface (cards, toolbars, sheets)
    case clear    // Edge-to-edge hero surfaces (paywall, onboarding)
}
```

### The `.saiGlass` modifier

One-call glass treatment for any view:

```swift theme={null}
// Simple card
VStack { /* content */ }
    .saiGlass(.regular)

// In a shape, interactive
Text("Tap me")
    .padding()
    .saiGlass(.regular, in: Capsule(), interactive: true)
```

| Modifier                       | Purpose                                                                                                                |
| ------------------------------ | ---------------------------------------------------------------------------------------------------------------------- |
| `.saiGlass(_:in:interactive:)` | Apply a glass material to any view. `in:` accepts any `Shape`; `interactive:` enables tap-response morphing on iOS 26. |
| `.saiScrollEdgeGlass(_:)`      | Glass treatment at scroll edges (under nav bars, tab bars).                                                            |
| `.saiSidebarAdaptable()`       | Adaptive sidebar styling for iPad and large displays.                                                                  |
| `.saiTabBarMinimize(_:)`       | Opt into iOS 26 tab bar minimisation on scroll. iOS 17–25 no-op.                                                       |

### SAIGlassContainer

Use when multiple glass surfaces sit near each other so they sample the same background instead of stacking visually:

```swift theme={null}
SAIGlassContainer {
    RatingCard()
    TipCard()
    UpsellCard()
}
```

### SAITabBarMinimizeStyle

```swift theme={null}
TabView { ... }
    .saiTabBarMinimize(.onScrollDown)  // iOS 26 only; no-op on iOS 17–25
```

<Tip>
  **Fighting glass? Stop.** Do **not** pile `.background(DSColors.background)` or `DSColors.background.ignoresSafeArea()` onto SwiftUI containers. They block the Material SwiftUI already provides and wreck Liquid Glass on iOS 26. See the [Migration Guide](/pages/migration/v1-9-to-v2-0) for cleanup steps.
</Tip>

## BrandConfig (Customize Your App)

**Single file** to customize app identity. From `BrandConfig.swift`:

```swift theme={null}
public struct BrandConfig {
    // Change this to your app name (ships with the starter value "App Name")
    public static let appDisplayName = "App Name" // generator:brand appDisplayName

    // Accent color for highlights and CTAs
    public static let accentColor: Color = DSColors.primary

    // SF Symbol for default avatar
    public static let avatarFallbackSymbol = "person.circle.fill" // generator:brand avatarFallbackSymbol

    // Production legal URLs — leave empty until your live HTTPS pages exist.
    // Non-HTTPS / placeholder values resolve to nil (no Release fallback).
    public static let privacyPolicyURLString = ""        // generator:brand privacyPolicyURL
    public static let termsOfServiceURLString = ""       // generator:brand termsOfServiceURL
    public static let subscriptionTermsURLString = ""    // generator:brand subscriptionTermsURL

    // App icon background color (for generated icons)
    public static let appIconBackground: Color = DSColors.primary
}
```

**To rebrand:**

1. Change `appDisplayName` → Your app name
2. Update the `AccentPrimary` color set in `Resources/Assets.xcassets`
3. Change `avatarFallbackSymbol` → Your SF Symbol
4. Set the legal URL strings to your live HTTPS pages before shipping
5. Entire app updates automatically

<Note>
  The `// generator:brand` markers are stable anchors for the App Generator's identity transforms — a separate product (coming soon). Keep one declaration per line and do not remove the markers. They are inert in the boilerplate itself.
</Note>

## Design Tokens (Production System)

### DSColors - Theme-Aware

Every color adapts to the active theme:

```swift theme={null}
// Real usage from auth screen
Text(BrandConfig.appDisplayName)
    .foregroundStyle(DSColors.textPrimary)

Button("Sign In")
    .foregroundStyle(DSColors.accentPrimary)  // Coral in Aurora, blue in default

VStack {}
    .background(DSColors.background)         // Adapts to all 5 themes
```

**Token categories:**

* Text: `textPrimary`, `textSecondary`
* Surfaces: `background`, `surface`, `surfaceElevated`
* Accents: `accentPrimary`, `accentSecondary`
* Borders: `borderHairline`, `borderSubtle`
* Semantic: `success`, `warning`, `danger`

### DSSpacing

Consistent spacing scale:

```swift theme={null}
// Usage
.padding(DSSpacing.md)
VStack(spacing: DSSpacing.lg) { }

// Scale: xs (4pt) → sm (8pt) → md (12pt) → lg (16pt) → xl (24pt)
```

### DSTypography

Text styles with Dynamic Type:

```swift theme={null}
// Usage
Text("Title").font(DSTypography.titleL)
Text("Body").font(DSTypography.body)

// Styles: titleXL, titleL, titleM, body, caption, code
```

### DSRadius

Corner radius values:

```swift theme={null}
// Usage
.cornerRadius(DSRadius.md)

// Scale: xs (8pt) → sm (12pt) → md (16pt) → lg (24pt) → xl (32pt)
// Plus DSRadius.card (= lg, 24pt) — the default card radius
```

## UI Components

<Tabs>
  <Tab title="Buttons">
    ```swift theme={null}
    SAIButton("Primary", style: .primary) { action() }
    SAIButton("Secondary", style: .secondary) { action() }
    SAIButton("Quiet", style: .quiet, size: .sm) { action() }
    ```
  </Tab>

  <Tab title="Inputs">
    ```swift theme={null}
    SAIInputBar(text: $message, onSend: sendMessage)
    SAIInputBar(text: $message, showTokenCount: true, onSend: sendMessage)
    ```
  </Tab>

  <Tab title="Cards & Rows">
    ```swift theme={null}
    SAICard(style: .elevated) {
        // Content
    }

    SAIListRow(title: "Setting", subtitle: "Value") {
        Image(systemName: "chevron.right")  // trailing accessory
    }
    ```
  </Tab>

  <Tab title="Other">
    ```swift theme={null}
    SAIAvatar(name: "John Doe", imageURL: url)
    ChatBubble(role: .user, text: "Hello")
    DSSectionHeader(title: "Section")
    ToastCenter.shared.show(ToastMessage(title: "Success!", style: .success))
    ```
  </Tab>
</Tabs>

## Theme System

### 5 Built-in Themes

<CardGroup cols={2}>
  <Card title="System" icon="mobile">
    Follows iOS light/dark mode
  </Card>

  <Card title="Light" icon="sun">
    Always light appearance
  </Card>

  <Card title="Dark" icon="moon">
    Always dark appearance
  </Card>

  <Card title="Aurora" icon="stars">
    Premium light — warm cream with coral/peach accents
  </Card>

  <Card title="Obsidian" icon="gem">
    Premium dark — deep navy with electric cyan accents
  </Card>
</CardGroup>

### Applying Themes

From Settings, persist and apply a theme in one call (`SettingsDTO.Theme`):

```swift theme={null}
await settingsViewModel.setTheme(.aurora)  // persists + applies app-wide
```

`ThemeManager` (in the `Core` package) owns the live theme and is also reachable directly:

```swift theme={null}
@Environment(ThemeManager.self) private var themeManager
themeManager.selected = .aurora  // applies instantly, without persisting Settings
```

### How It Works

```text theme={null}
1. settingsViewModel.setTheme maps SettingsDTO.Theme → ThemeManager.Theme
2. ThemeManager persists the choice (UserDefaults) and applies the interface style
3. It posts a "ThemeDidChange" notification
4. DSColors.setTheme(_:colorScheme:) updates DSColors.activeTheme
5. Color tokens return theme-specific values and SwiftUI refreshes
```

## Customization Examples

### Create Custom Theme

<Steps>
  <Step title="Add Color Set">
    In `Resources/Assets.xcassets`, add a color set per token you want to override (e.g. `AccentPrimary`, `Background`). Themed values can also be returned in code (see step 3).
  </Step>

  <Step title="Add Theme Cases">
    Add `case myTheme` to both theme enums — the persisted `SettingsDTO.Theme`
    (`Packages/Storage/.../SettingsDTO.swift`) and the live `ThemeManager.Theme`
    (`Packages/Core/Sources/Core/Theme/ThemeManager.swift`). Then add the
    `.myTheme` branch to the mapping in `SettingsViewModel.setTheme(_:)`, and add a
    matching case to `DSColors.ThemePalette` + `DSColors.setTheme(_:colorScheme:)`.
  </Step>

  <Step title="Map Colors">
    In `DSColors.swift`, branch on `activeTheme`:

    ```swift theme={null}
    public static var accentPrimary: Color {
        switch activeTheme {
        case .myTheme:
            return Color("MyThemePrimary", bundle: .module)
        default:
            return named("AccentPrimary", fallback: .blue)
        }
    }
    ```
  </Step>

  <Step title="Surface in the Picker">
    The settings appearance picker iterates `SettingsDTO.Theme.allCases`, so the
    new case appears automatically once the cases above are wired up.
  </Step>
</Steps>

### Create Custom Component

```swift theme={null}
public struct MyCustomCard: View {
    let title: String
    let subtitle: String
    
    public var body: some View {
        VStack(alignment: .leading, spacing: DSSpacing.sm) {
            Text(title)
                .font(DSTypography.titleM)
                .foregroundStyle(DSColors.textPrimary)
            
            Text(subtitle)
                .font(DSTypography.body)
                .foregroundStyle(DSColors.textSecondary)
        }
        .padding(DSSpacing.md)
        .background(DSColors.surface)
        .cornerRadius(DSRadius.md)
    }
}
```

## Animations

Standard motion timings live in `SAIMotion` and respect Reduce Motion:

```swift theme={null}
// Timings
SAIMotion.quick     // 0.12s — micro-interactions
SAIMotion.standard  // 0.18s — most UI changes
SAIMotion.smooth    // 0.25s — larger transitions

// Springs
SAIMotion.spring        // response 0.3, damping 0.7
SAIMotion.gentleSpring  // subtle bounces

// Accessibility-aware (auto-fallback when Reduce Motion is on)
SAIMotion.adaptiveStandard
SAIMotion.adaptiveSpring

// Usage
.animation(SAIMotion.spring, value: isExpanded)

// Reusable view modifiers
SomeView().pressedEffect(isPressed: isPressed)  // scale + shadow, opacity-only under Reduce Motion
SomeView().shimmer(isActive: true)              // loading shimmer, disabled under Reduce Motion
```

## Accessibility

Built-in support for:

* ✅ **Dynamic Type** - All text scales automatically
* ✅ **VoiceOver** - All components labeled
* ✅ **High Contrast** - Semantic colors adapt
* ✅ **Reduced Motion** - Respects system preference
* ✅ **Minimum Touch Targets** - 44pt minimum

## Key Files

| Component    | Location                                                               |
| ------------ | ---------------------------------------------------------------------- |
| Tokens       | `Packages/DesignSystem/Sources/DesignSystem/Tokens/`                   |
| Components   | `Packages/DesignSystem/Sources/DesignSystem/Components/`               |
| Liquid Glass | `Packages/DesignSystem/Sources/DesignSystem/Materials/SAIGlass.swift`  |
| Colors       | `Packages/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets` |

## Dependencies

* **None** - DesignSystem is independent

## Used By

* **All features** - Every UI component
* **App Shell** - Main app screens
* **Feature modules** - Chat, Rating, Settings

## Best Practices

<AccordionGroup>
  <Accordion title="Using Tokens">
    * Never hardcode colors
    * Never hardcode spacing
    * Use semantic names
    * Test in all themes
  </Accordion>

  <Accordion title="Creating Components">
    * Use design tokens exclusively
    * Support all themes
    * Add accessibility labels
    * Test Dynamic Type
    * Include preview
  </Accordion>

  <Accordion title="Theming">
    * Semantic colors only
    * Support light + dark
    * Instant switching
    * Persist preference
  </Accordion>
</AccordionGroup>

## Learn More

<CardGroup cols={2}>
  <Card title="Full Documentation" icon="book" href="https://github.com/SwiftAIBoilerplatePro/SwiftAIBoilerplatePro-Distribution/blob/main/docs/modules/DesignSystem.md">
    Complete DesignSystem guide
  </Card>

  <Card title="Feature Settings" icon="gear" href="/pages/modules/feature-settings">
    Theme selection UI
  </Card>

  <Card title="Building Guide" icon="hammer" href="/pages/guides/building-your-app">
    Customize themes
  </Card>

  <Card title="Architecture" icon="diagram-project" href="/pages/architecture">
    See how modules use DesignSystem
  </Card>
</CardGroup>

## Test Coverage

The `DesignSystemTests` target (token + snapshot tests) runs as part of the
workspace suite — \~598 tests across 12 package test targets plus the app test
suites, all in one `Boilerplate.xctestplan` pass.

Tests include:

* Theme switching
* Color mapping
* Component snapshots
* Accessibility
* Dynamic Type
