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:
- Define tokens exclusively in
AppTheme– Never declare numeric literals in view files likeSources/PalmierPro/Timeline/TimelineView.swift. - Reference via the enum namespace – Always use
AppTheme.Spacing.mdinstead of local constants. - Prefer theme modifiers – Use
.panelHeaderBar()over manual background and border configuration. - 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
AppThemeenum located atSources/PalmierPro/UI/AppTheme.swiftto create a single source of truth. - Organize tokens by semantic type using nested enums like
Spacing,FontSize, andBackgroundto prevent cross-contamination of values. - Extend SwiftUI's
Viewprotocol with convenience modifiers such aspanelHeaderBar()andshadow(_:)to streamline theme application. - Support cross-platform consistency by providing both
NSColorandColorcomputed 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →