philiprehberger/swift-design-system
Token-based design system engine with theme switching, component registry, and JSON export
Requirements
- Swift >= 6.0
- macOS 13+ / iOS 16+ / tvOS 16+ / watchOS 9+
Installation
Add to your Package.swift:
dependencies: [
.package(url: "https://github.com/philiprehberger/swift-design-system.git", from: "0.2.0")
]Then add "DesignSystem" to your target dependencies:
.target(name: "YourTarget", dependencies: [
.product(name: "DesignSystem", package: "swift-design-system")
])Usage
import DesignSystem
let ds = DesignSystem.shared
ds.themes.register(Theme(
name: "light",
colors: ["primary": ColorToken(hex: "#0066FF")!],
spacing: ["md": SpacingToken(16)],
typography: ["body": TypographyToken(fontSize: 16, fontWeight: .regular)]
))
ds.color("primary")?.hex // "#0066FF"Themes
Define and switch between named themes:
let light = Theme(
name: "light",
colors: ["bg": ColorToken(hex: "#FFFFFF")!, "text": ColorToken(hex: "#111111")!],
spacing: ["sm": SpacingToken(8), "md": SpacingToken(16), "lg": SpacingToken(32)],
typography: ["heading": TypographyToken(fontSize: 24, fontWeight: .bold, lineHeight: 32)]
)
let dark = Theme(
name: "dark",
colors: ["bg": ColorToken(hex: "#111111")!, "text": ColorToken(hex: "#FFFFFF")!],
spacing: light.spacing,
typography: light.typography
)
ds.themes.register(light)
ds.themes.register(dark)
ds.themes.switchTo("dark")
ds.color("bg")?.hex // "#111111"Theme Observers
React to theme changes:
ds.themes.onChange { theme in
print("Switched to \(theme.name)")
}Component Registry
Register reusable component style configurations:
ds.components.register(ComponentStyle(
name: "button.primary",
properties: ["backgroundColor": "#0066FF", "cornerRadius": "8", "textColor": "#FFFFFF"]
))
let style = ds.components.style(named: "button.primary")
style?.properties["backgroundColor"] // "#0066FF"Design Tokens
// Colors with hex parsing
let color = ColorToken(hex: "#FF5733")!
color.red // 1.0
color.hex // "#FF5733"
// Spacing
let spacing = SpacingToken(16)
// Typography
let heading = TypographyToken(fontSize: 24, fontWeight: .bold, lineHeight: 32, letterSpacing: -0.5)JSON Export / Import (Figma Bridge)
Export tokens for handoff to designers, or import from Figma:
// Export
let json = try ds.exportActiveTheme()
let jsonString = String(data: json, encoding: .utf8)!
// Import
let imported = ds.importTheme(from: jsonData)
ds.themes.switchTo(imported!.name)Shadows & Borders
let theme = Theme(
name: "material",
shadows: ["card": ShadowToken(color: ColorToken(hex: "#000000")!, radius: 8, yOffset: 4, opacity: 0.2)],
borders: ["input": BorderToken(width: 1, color: ColorToken(hex: "#CCCCCC")!)]
)
theme.shadow("card")?.radius // 8
theme.border("input")?.width // 1Theme Inheritance
let base = Theme(name: "base", colors: ["bg": ColorToken(hex: "#FFFFFF")!, "text": ColorToken(hex: "#000000")!])
let dark = base.extending(Theme(name: "dark", colors: ["bg": ColorToken(hex: "#111111")!]))
dark.color("bg")?.hex // "#111111" (overridden)
dark.color("text")?.hex // "#000000" (inherited)Token Validation
let issues = TokenValidator.validate(
theme,
requiredColors: ["primary", "background"],
requiredSpacing: ["sm", "md", "lg"]
)
for issue in issues {
print("[\(issue.severity)] \(issue.message)")
}Theme Merging
Extend a base theme with overrides:
let brand = base.merging(Theme(
name: "brand",
colors: ["primary": ColorToken(hex: "#FF6600")!]
))
// Inherits all base tokens, overrides "primary"API
DesignSystem
| Method | Description | |--------|-------------| | .shared | Singleton instance | | .themes | Access the ThemeManager | | .components | Access the ComponentRegistry | | .color(:) | Get color from active theme | | .spacing(:) | Get spacing from active theme | | .typography(_:) | Get typography from active theme | | .exportActiveTheme() | Export active theme as JSON | | .importTheme(from:) | Import theme from JSON data |
Theme
| Method | Description | |--------|-------------| | .color(:) | Look up a named color token | | .spacing(:) | Look up a named spacing token | | .typography(:) | Look up a named typography token | | .merging(:) | Merge with another theme | | .shadow(:) | Look up a shadow token | | .border(:) | Look up a border token | | .extending(_:) | Create child theme with inherited tokens |
ShadowToken
| Property | Description | |----------|-------------| | .color | Shadow color | | .radius | Blur radius | | .xOffset / .yOffset | Shadow offset | | .opacity | Shadow opacity (0-1) |
BorderToken
| Property | Description | |----------|-------------| | .width | Border width in points | | .color | Border color | | .style | Line style (solid, dashed, dotted) |
TokenValidator
| Method | Description | |--------|-------------| | .validate(_:requiredColors:requiredSpacing:requiredTypography:) | Lint a theme against requirements |
ThemeManager
| Method | Description | |--------|-------------| | .register(:) | Register a theme | | .switchTo(:) | Switch active theme | | .activeTheme | Current theme | | .availableThemes | List theme names | | .onChange(_:) | Observe theme changes |
ComponentRegistry
| Method | Description | |--------|-------------| | .register(:) | Register a component style | | .style(named:) | Retrieve by name | | .allStyles | List style names | | .remove(:) | Remove a style |
TokenExporter
| Method | Description | |--------|-------------| | .exportJSON(:) | Export theme to JSON data | | .importJSON(:) | Import theme from JSON data | | .exportTheme(_:) | Export to dictionary | | .importTheme(from:) | Import from dictionary |
Development
swift build
swift testSupport
If you find this project useful:
β Star the repo
π Report issues
π‘ Suggest features
β€οΈ Sponsor development
π» GitHub Profile
π LinkedIn Profile
License
Package Metadata
Repository: philiprehberger/swift-design-system
Default branch: main
README: README.md