How to Enforce Design System Consistency in SwiftUI Apps: The Palmier-Pro Token Architecture

Enforce design system consistency in SwiftUI apps by centralizing every visual token—colors, spacing, typography, and effects—in a single AppTheme enum that eliminates magic numbers and guarantees automatic propagation of design changes across your entire codebase.

Maintaining visual consistency across a growing SwiftUI codebase requires architectural guardrails to prevent hard-coded values from fragmenting your interface. The Palmier-Pro macOS application solves this challenge by treating its AppTheme structure as the sole authority for all styling decisions. By routing every visual property through this centralized enum located in Sources/PalmierPro/UI/AppTheme.swift, the codebase enforces design system consistency at compile time while making global redesigns trivial.

Centralize Tokens in a Single Source of Truth

The foundation of Palmier-Pro's design system is the AppTheme enum, which serves as the single definition point for every visual property. This structure contains nested enums organized by purpose—Spacing, FontSize, Background, Border, Opacity, Radius, Shadow, and Anim—each containing strictly typed constants.

For example, AppTheme.Spacing.smMd is defined as 8 points at line 2001, while AppTheme.FontSize.smMd equals 12 points at line 2014. By prohibiting literal values like 12 or 0.5 anywhere outside this file, the architecture guarantees that changing a token in AppTheme automatically propagates to every component consuming it. This eliminates the drift that occurs when developers copy-paste values between views.

Enforce Usage Through Typed Grouping

Palmier-Pro organizes tokens by semantic purpose to improve discoverability and prevent accidental cross-use. The Spacing enum contains only layout constants, while FontSize manages typography scales, and Background defines surface colors. This typed grouping ensures that AppTheme.Spacing.lgXl cannot be mistaken for a color value, catching conceptual errors at compile time.

Real-world enforcement is visible in Sources/PalmierPro/UI/SidebarRowButton.swift, where the button's horizontal padding, font size, and corner radius all reference AppTheme constants. This pattern forces developers to use AppTheme.Spacing.smMd rather than guessing values, ensuring that sidebar components remain visually aligned with the rest of the application.

Streamline Implementation with SwiftUI Extensions

To reduce friction for developers, Palmier-Pro extends SwiftUI's View protocol with convenience modifiers that apply theme values automatically. Located at lines 28-33 of AppTheme.swift, these extensions include methods like shadow(_:) and panelHeaderBar() that internally resolve the correct token values.

This abstraction allows developers to write myView.shadow(AppTheme.Shadow.md) without memorizing specific color codes or opacity levels. The panelHeaderBar() modifier bundles background color, border treatment, and height constraints from the theme into a single call, ensuring that every panel header in Sources/PalmierPro/UI/GeneratingOverlay.swift and similar views shares identical visual properties.

Bridge Cross-Platform Visual Tokens

For macOS applications mixing SwiftUI with AppKit, Palmier-Pro exposes each token as both NSColor and SwiftUI Color types. Each color token in AppTheme provides computed properties that return the appropriate type for the current framework, enabling the same spacing and color definitions to power both legacy AppKit views and modern SwiftUI components.

This bridging ensures consistency across the entire application surface. When a token updates in AppTheme.swift, both the AppKit sidebar and the SwiftUI toolbar in Sources/PalmierPro/Toolbar/ToolbarView.swift update simultaneously, maintaining visual coherence without manual synchronization.

Eliminate Magic Numbers with Enforcement Rules

Adopting the Palmier-Pro pattern requires strict governance to prevent regression. The team enforces these through four concrete practices:

  1. Define tokens exclusively in AppTheme – Never declare numeric literals in view files like Sources/PalmierPro/Timeline/TimelineView.swift.
  2. Reference via the enum namespace – Always use AppTheme.Spacing.md instead of local constants.
  3. Prefer theme modifiers – Use .panelHeaderBar() over manual background and border configuration.
  4. Lint for violations – Run greps for numeric literals (\b\d+(\.\d+)?\b) across Swift files to catch stray values during code review.

This governance model ensures that the compiler and CI pipeline enforce design consistency. When a developer attempts to hard-code padding(.horizontal, 12), the lint check flags the violation, redirecting them toward AppTheme.Spacing.sm.

Code Examples: Themed versus Hard-Coded

The following examples demonstrate the transformation from fragile magic numbers to robust theme-based styling:

Consistent Button Implementation

struct MyPrimaryButton: View {
    let title: String
    var action: () -> Void
    
    var body: some View {
        Button(action: action) {
            Text(title)
                .font(.system(size: AppTheme.FontSize.md,
                             weight: AppTheme.FontWeight.semibold))
                .foregroundStyle(AppTheme.Text.primaryColor)
                .padding(.vertical, AppTheme.Spacing.sm)
                .padding(.horizontal, AppTheme.Spacing.lgXl)
        }
        .background(AppTheme.Background.raisedColor)
        .cornerRadius(AppTheme.Radius.md)
        .shadow(AppTheme.Shadow.sm)
    }
}

Panel Header Using Theme Extensions

struct SettingsHeader: View {
    var title: String
    
    var body: some View {
        Text(title)
            .font(.system(size: AppTheme.FontSize.title1,
                         weight: AppTheme.FontWeight.bold))
            .foregroundStyle(AppTheme.Text.secondaryColor)
            .panelHeaderBar()
    }
}

Anti-Pattern: Hard-Coded Values

// Avoid this approach
Text("Inconsistent")
    .font(.system(size: 13))
    .padding(.horizontal, 12)
    .foregroundColor(Color.white.opacity(0.8))

Refactored with Design Tokens

// Correct approach
Text("Consistent")
    .font(.system(size: AppTheme.FontSize.sm,
                 weight: AppTheme.FontWeight.regular))
    .padding(.horizontal, AppTheme.Spacing.sm)
    .foregroundStyle(AppTheme.Text.secondaryColor.opacity(AppTheme.Opacity.soft))

Summary

  • Centralize all visual tokens in a single AppTheme enum located at Sources/PalmierPro/UI/AppTheme.swift to create a single source of truth.
  • Organize tokens by semantic type using nested enums like Spacing, FontSize, and Background to prevent cross-contamination of values.
  • Extend SwiftUI's View protocol with convenience modifiers such as panelHeaderBar() and shadow(_:) to streamline theme application.
  • Support cross-platform consistency by providing both NSColor and Color computed properties for every color token.
  • Enforce the architecture through linting rules that reject numeric literals outside the theme file, ensuring compliance at build time.

Frequently Asked Questions

How does AppTheme prevent developers from accidentally using hard-coded values?

The Palmier-Pro codebase enforces a zero-tolerance policy for magic numbers combined with automated linting. By requiring all visual constants to live exclusively in Sources/PalmierPro/UI/AppTheme.swift, code reviews can quickly identify violations. Additionally, the typed grouping of tokens makes it easier to discover the correct constant than to guess a literal value, steering developers toward the sanctioned API.

Can this token system support both SwiftUI and AppKit in the same project?

Yes. Each color token in AppTheme exposes computed properties for both Color (SwiftUI) and NSColor (AppKit), allowing the same spacing and color definitions to power views in Sources/PalmierPro/Timeline/TimelineView.swift and legacy AppKit components simultaneously. This dual-type approach ensures consistency during gradual UI migrations.

What happens when a design token needs to change globally?

Because each token exists in exactly one location within AppTheme, changing a value like AppTheme.Spacing.sm from 6 pt to 8 pt instantly updates every view referencing that constant. This centralized architecture eliminates the need for find-and-replace operations across dozens of files, reducing regression risk when iterating on design.

How do View extensions contribute to design consistency?

The View extensions defined at the bottom of AppTheme.swift (lines 28-33) bundle complex styling decisions into single modifiers like .panelHeaderBar(). These extensions internally reference multiple theme tokens—background color, border width, and height—ensuring that every panel header uses identical specifications without requiring developers to remember the correct combination of values.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →