macOS · SwiftUI
Use Sitka's design tokens in a macOS SwiftUI app. The same typed Swift constants that power iOS — with macOS-native color APIs, window-chrome conventions, and HIG-correct component patterns.
Download the Swift token file
Go to the Token Export page and download SitkaTokens.swift. It contains the same brand colors and spacing scale used across web and iOS.
npx style-dictionary build --config style-dictionary.config.js
# outputs dist/SitkaTokens.swiftAdd to your Xcode project
- Drag
SitkaTokens.swiftinto your Xcode project navigator. - In the Add Files dialog, ensure Copy items if needed is checked and your macOS target is selected.
- For shared iOS+macOS targets, the same token file works on both platforms — only the semantic color bridges differ.
macOS semantic color bridges
macOS uses NSColor instead of UIColor for system colors. The mapping is consistent across all Sitka component pages.
| iOS (UIColor) | macOS (NSColor) | Semantic role |
|---|---|---|
| UIColor.systemBackground | NSColor.windowBackgroundColor | Window / screen background |
| UIColor.secondarySystemBackground | NSColor.controlBackgroundColor | Card / panel surface |
| UIColor.tertiarySystemBackground | NSColor.quaternaryLabelColor.opacity(0.06) | Subtle fill |
| UIColor.label | NSColor.labelColor / .labelColor | Primary text |
| UIColor.secondaryLabel | NSColor.secondaryLabelColor | Secondary text |
| UIColor.tertiaryLabel | NSColor.tertiaryLabelColor | Tertiary text |
| UIColor.separator | NSColor.separatorColor | Borders & dividers |
import SwiftUI
import AppKit
// Drop this file into a macOS-only target alongside SitkaTokens.swift
// to get NSColor-backed semantic tokens.
extension SitkaTokens.Color {
// Adaptive surfaces — follow the system light/dark mode automatically
public static var windowBackground: Color { Color(NSColor.windowBackgroundColor) }
public static var controlBackground: Color { Color(NSColor.controlBackgroundColor) }
public static var separator: Color { Color(NSColor.separatorColor) }
// Adaptive text
public static var label: Color { Color(.labelColor) }
public static var secondaryLabel: Color { Color(.secondaryLabelColor) }
public static var tertiaryLabel: Color { Color(.tertiaryLabelColor) }
}Usage examples
Card surface
VStack(alignment: .leading, spacing: SitkaTokens.Spacing.s3) {
Text("Card title")
.font(.system(size: SitkaTokens.Typography.sizeSM, weight: .semibold))
.foregroundStyle(SitkaTokens.Color.label)
Text("Subtitle text")
.font(.system(size: SitkaTokens.Typography.sizeXS))
.foregroundStyle(SitkaTokens.Color.secondaryLabel)
}
.padding(SitkaTokens.Spacing.s5)
.background(SitkaTokens.Color.controlBackground)
.clipShape(RoundedRectangle(cornerRadius: SitkaTokens.Radius.md, style: .continuous))
.overlay(
RoundedRectangle(cornerRadius: SitkaTokens.Radius.md, style: .continuous)
.stroke(SitkaTokens.Color.separator, lineWidth: 1)
)Settings form
struct AppSettingsView: View {
@State private var notifications = true
@State private var theme = "system"
var body: some View {
Form {
Section("General") {
Toggle("Enable notifications", isOn: $notifications)
.toggleStyle(.checkbox)
Picker("Appearance", selection: $theme) {
Text("System").tag("system")
Text("Light").tag("light")
Text("Dark").tag("dark")
}
.pickerStyle(.radioGroup)
}
}
.formStyle(.grouped)
.frame(width: 380)
.padding()
}
}Keyboard shortcut
Button("New Document") { createDocument() }
.keyboardShortcut("n", modifiers: .command)
Button("Find") { openFind() }
.keyboardShortcut("f", modifiers: .command)
Button("Close") { closeWindow() }
.keyboardShortcut("w", modifiers: .command)Native toolbar
struct ContentView: View {
var body: some View {
NavigationSplitView {
List(items) { item in
NavigationLink(item.title, value: item)
}
.listStyle(.sidebar)
} detail: {
DetailView()
}
.toolbar {
ToolbarItem(placement: .primaryAction) {
Button(action: addItem) {
Label("Add", systemImage: "plus")
}
}
}
.navigationTitle("Library")
}
}macOS-specific patterns
| Pattern | iOS approach | macOS approach |
|---|---|---|
| Modal / sheet | ZStack overlay fullscreen | .sheet(isPresented:) on window |
| Tooltips | Custom overlay on long press | .help() modifier (native hover) |
| Form layout | Section {} in Form | .formStyle(.grouped) |
| Toggles | Toggle with .tint | .toggleStyle(.checkbox) |
| Radio buttons | Custom button group | Picker with .pickerStyle(.radioGroup) |
| Sidebar nav | TabView / NavigationStack | NavigationSplitView + .listStyle(.sidebar) |
| Table | Custom VStack/HStack rows | SwiftUI Table view (sortable, multi-select) |
| Shortcuts | N/A (touch-first) | .keyboardShortcut() on any Button |
Environment propagation for sheets
On macOS, sheets presented from a WindowGroup do not inherit the parent view's environment. Every .sheet() modifier must explicitly re-inject all required environment objects — including ThemeManager — or theming will silently break inside sheets.
// Correct — ThemeManager re-injected into the sheet
.sheet(isPresented: $showSettings) {
SettingsView()
.environmentObject(themeManager)
.environmentObject(appSettings)
}See the complete wiring guide for ThemeManager, adaptive tokens, and effectiveAccent: Wire Sitka's ThemeManager in SwiftUI →
Requirements
Swift
5.9+
macOS
13+
Xcode
15+
Back to all libraries.