DEV Community

Sebastien Lato
Sebastien Lato

Posted on

SwiftUI Design Tokens & Theming System (Production-Scale)

As SwiftUI apps grow, UI consistency becomes fragile.

You start seeing:

  • slightly different padding values
  • inconsistent corner radii
  • random colors hardcoded everywhere
  • dark mode bugs
  • accessibility issues
  • painful rebranding work

The solution is design tokens β€” a single source of truth for all visual decisions.

This post shows how to build a production-grade design token & theming system in SwiftUI that scales cleanly.


🧠 What Are Design Tokens?

Design tokens are named, semantic values, not raw numbers.

Bad:

.padding(12)
.cornerRadius(16)
.foregroundColor(.blue)
Enter fullscreen mode Exit fullscreen mode

Good:

.padding(.md)
.cornerRadius(.card)
.foregroundStyle(.accent)
Enter fullscreen mode Exit fullscreen mode

Tokens express intent, not implementation.


πŸ“¦ 1. Core Token Categories

A complete system usually includes:

  • Spacing
  • Radius
  • Typography
  • Colors
  • Elevation
  • Animation
  • Opacity

We’ll define each cleanly.


πŸ“ 2. Spacing Tokens

enum Spacing {
    static let xs: CGFloat = 4
    static let sm: CGFloat = 8
    static let md: CGFloat = 16
    static let lg: CGFloat = 24
    static let xl: CGFloat = 32
}
Enter fullscreen mode Exit fullscreen mode

Usage:

.padding(Spacing.md)
Enter fullscreen mode Exit fullscreen mode

πŸ”² 3. Corner Radius Tokens

enum Radius {
    static let sm: CGFloat = 6
    static let md: CGFloat = 12
    static let lg: CGFloat = 20
    static let card: CGFloat = 16
}
Enter fullscreen mode Exit fullscreen mode

🎨 4. Color Tokens (Semantic, Not Visual)

Never do:

Color.blue
Enter fullscreen mode Exit fullscreen mode

Instead:

enum AppColor {
    static let background = Color("Background")
    static let surface = Color("Surface")
    static let accent = Color("Accent")
    static let textPrimary = Color("TextPrimary")
    static let textSecondary = Color("TextSecondary")
}
Enter fullscreen mode Exit fullscreen mode

Assets handle:

  • light/dark mode
  • accessibility contrast
  • brand changes

πŸ”€ 5. Typography Tokens

enum AppFont {
    static let title = Font.system(size: 28, weight: .bold)
    static let headline = Font.system(size: 20, weight: .semibold)
    static let body = Font.system(size: 16)
    static let caption = Font.system(size: 13)
}
Enter fullscreen mode Exit fullscreen mode

Usage:

Text("Title")
    .font(AppFont.title)
Enter fullscreen mode Exit fullscreen mode

🧊 6. Elevation & Material Tokens

enum Elevation {
    static let card = CGFloat(4)
    static let modal = CGFloat(12)
}
Enter fullscreen mode Exit fullscreen mode
.background(.ultraThinMaterial)
.shadow(radius: Elevation.card)
Enter fullscreen mode Exit fullscreen mode

πŸ•Ί 7. Animation Tokens

enum Motion {
    static let fast = Animation.easeOut(duration: 0.15)
    static let standard = Animation.easeInOut(duration: 0.25)
    static let slow = Animation.spring(response: 0.45)
}
Enter fullscreen mode Exit fullscreen mode

πŸŒ— 8. Theme Container

Create a theme model:

struct AppTheme {
    let colors: Colors
    let spacing: Spacing.Type
    let radius: Radius.Type
}
Enter fullscreen mode Exit fullscreen mode

Inject via environment:

.environment(\.theme, currentTheme)
Enter fullscreen mode Exit fullscreen mode

πŸ” 9. Supporting Multiple Themes

enum ThemeKind {
    case light
    case dark
    case highContrast
}
Enter fullscreen mode Exit fullscreen mode

Switch dynamically:

  • system appearance
  • accessibility
  • user preference
  • A/B testing

πŸ§ͺ 10. Previews Become Powerful

#Preview("Dark") {
    ContentView()
        .environment(\.theme, .dark)
}
Enter fullscreen mode Exit fullscreen mode

Design validation becomes instant.


πŸš€ Final Thoughts

A design token system gives you:

  • consistency
  • accessibility
  • faster iteration
  • safer refactors
  • easy rebranding
  • cleaner code
  • happier designers

Once you adopt tokens, you never go back.

Top comments (0)