-
Notifications
You must be signed in to change notification settings - Fork 0
Theme Presets
Every DesignFoundation theme is a light/dark pair — a DFThemePreset bundles two DFTheme instances and resolves the correct variant at runtime by reading the system color scheme (@Environment(\.colorScheme)). You never branch on colorScheme yourself; you apply a preset and the framework handles the rest.
Presets encode more than color. Each one ships with a calibrated radius scale, shadow opacity, and typographic character that work together to create a coherent aesthetic. Swapping .slate for .copper changes how the entire app feels, not just its palette.
Related pages: Theming · Style-System · Swift-6-Concurrency
- Applying Presets
- Default
- Slate
- Aurora
- Copper
- Sage
- Comparison Table
- Choosing a Preset
- Building a Custom Preset
- Previewing in Xcode Canvas
- Runtime Preset Switching
- Per-Platform Preset Overrides
// Resolves light or dark variant based on the system color scheme
ContentView()
.dfThemePreset(.slate)Apply this modifier at the scene root — typically on the view returned by your WindowGroup body. All DesignFoundation components in the subtree inherit the resolved theme automatically via @Environment(\.dfTheme).
// Always light — ignores system setting
ContentView().dfTheme(.slateLight)
// Always dark — ignores system setting
ContentView().dfTheme(.slateDark)Each preset exposes named DFTheme constants following the <preset><Variant> pattern: .defaultLight / .defaultDark, .slateLight / .slateDark, .auroraLight / .auroraDark, .copperLight / .copperDark, .sageLight / .sageDark.
A preset applied to any view overrides the parent theme for that subtree only. The rest of the app is unaffected.
VStack {
MainContent() // inherits app-level .slate
PromoSection()
.dfThemePreset(.copper) // warm earth tones for this block only
}@State private var preset: DFThemePreset = .slate
var body: some View {
ContentView()
.dfThemePreset(preset)
}Personality. Default delegates to SwiftUI's semantic color system — Color.primary, Color.accentColor, Color.background. It carries no opinionated hue of its own, blending into any context and respecting the user's system accent color.
| Token | Value |
|---|---|
| Primary (light) | System accent |
| Primary (dark) | System accent |
radius.md |
8 pt |
| Shadow opacity | 8% |
Ideal for: utilities, productivity tools, developer tools, apps that must match the OS exactly, prototypes before a brand palette is decided.
ContentView().dfThemePreset(.default)Personality. A cool, authoritative presence rooted in deep navy and clear sky blue. In light mode, the deep navy primary communicates trust and expertise. In dark mode, sky blue lifts the palette into something crisp and legible. The character is measured, structured, and confident.
| Token | Value |
|---|---|
| Primary (light) |
#1C3D5A — deep navy |
| Primary (dark) |
#63B5F6 — sky blue |
radius.md |
8 pt |
| Shadow opacity | 8% |
Ideal for: finance, B2B SaaS, analytics dashboards, enterprise productivity.
struct PortfolioCard: View {
let symbol: String
let change: Double
var body: some View {
DFCard {
HStack {
VStack(alignment: .leading, spacing: 4) {
DFText(symbol, style: .headline)
DFText("Today's change", style: .caption)
}
Spacer()
DFBadge(
text: change >= 0 ? "+\(change)%" : "\(change)%",
color: change >= 0 ? .green : .red
)
}
}
.dfThemePreset(.slate)
}
}Personality. Bold violet and soft lavender — a palette built for imagination. In light mode, saturated violet pulses with creative energy. In dark mode, soft lavender is dreamy and ethereal. The radius scale is more generous than Default and Slate, rounding corners into something fluid and inviting. The lightest shadow depth of all presets lets surfaces float.
| Token | Value |
|---|---|
| Primary (light) |
#6C47FF — bold violet |
| Primary (dark) |
#A78BFA — soft lavender |
radius.md |
10 pt |
| Shadow opacity | 6% |
Ideal for: creative tools, AI-powered apps, social platforms, journaling, education.
struct IdeaCard: View {
@State private var query = ""
var body: some View {
DFCard {
VStack(spacing: 16) {
DFText("Your Creative Space", style: .headline)
DFTextField("Search ideas…", text: $query, leadingIcon: "sparkles")
DFButton("New Idea", icon: "plus") { addIdea() }
}
}
.dfThemePreset(.aurora)
}
}Personality. Terracotta and warm amber — tactile and warm. Light mode recalls handmade ceramics and sun-dried clay. Dark mode glows like embers. The tightest radius scale gives Copper a structured, handcrafted look. The strongest shadow opacity adds tangible depth to every surface.
| Token | Value |
|---|---|
| Primary (light) |
#C4623E — terracotta |
| Primary (dark) |
#F4A261 — warm amber |
radius.md |
6 pt |
| Shadow opacity | 12% |
Ideal for: food and recipe apps, hospitality, lifestyle e-commerce, wellness, travel.
struct RecipeCard: View {
let recipe: Recipe
var body: some View {
DFCard {
VStack(alignment: .leading, spacing: 8) {
DFText(recipe.name, style: .headline)
HStack {
DFIcon("clock", size: .sm)
DFText("\(recipe.minutes) min", style: .caption)
DFBadge(text: recipe.difficulty)
}
DFButton("View Recipe") { openRecipe() }
}
}
.dfThemePreset(.copper)
}
}Personality. Forest green grounds the light palette in something ancient and living; mint green in dark mode refreshes without demanding attention. The most generous radius scale curves every surface into something rounded and unhurried. The near-imperceptible shadow creates an interface that feels like ambient light, not a spotlight. Sage communicates calm, health, and intentionality.
| Token | Value |
|---|---|
| Primary (light) |
#2D6A4F — forest green |
| Primary (dark) |
#74C69D — mint green |
radius.md |
12 pt |
| Shadow opacity | 4% |
Ideal for: health and fitness, meditation, sustainability, personal finance, nature apps.
struct WellnessCheckIn: View {
@State private var mood: Double = 0.7
@State private var notes = ""
var body: some View {
DFCard {
VStack(alignment: .leading, spacing: 16) {
DFText("How are you feeling?", style: .headline)
DFSlider(value: $mood, in: 0...1, label: "Mood")
DFTextField("Any notes for today…", text: $notes, leadingIcon: "leaf")
DFButton("Save check-in") { saveCheckIn() }
}
}
.dfThemePreset(.sage)
}
}| Preset | Primary (Light) | Primary (Dark) | radius.md |
Shadow | Character |
|---|---|---|---|---|---|
| Default | System accent | System accent | 8 pt | 8% | Neutral, platform-native |
| Slate |
#1C3D5A navy |
#63B5F6 sky blue |
8 pt | 8% | Professional, authoritative |
| Aurora |
#6C47FF violet |
#A78BFA lavender |
10 pt | 6% | Creative, luminous, airy |
| Copper |
#C4623E terracotta |
#F4A261 amber |
6 pt | 12% | Warm, tactile, grounded |
| Sage |
#2D6A4F forest |
#74C69D mint |
12 pt | 4% | Calm, natural, open |
| App domain | Recommended preset | Rationale |
|---|---|---|
| Finance / banking | .slate |
Trust, authority, analytical precision |
| Developer tools |
.slate or .default
|
Structured, doesn't distract from content |
| Creative / AI apps | .aurora |
Bold, imaginative; signals innovation |
| Social / community | .aurora |
Inviting, modern, emotionally warm |
| Food / hospitality | .copper |
Earthy warmth matches culinary context |
| E-commerce (lifestyle) | .copper |
Artisanal quality feel |
| Health / fitness | .sage |
Calm, trustworthy; green carries wellness |
| Meditation / mindfulness | .sage |
Minimal shadow and open radius reinforce tranquility |
| Utilities | .default |
Matches system expectations |
| Education |
.aurora or .sage
|
Aurora for engagement; Sage for focus |
Tip: choose by emotional register: trustworthy → .slate, expressive → .aurora, warm → .copper, calm → .sage, native → .default.
import DesignFoundation
let midnightLight = DFTheme(
colors: DFColorTokens(
primary: Color(hex: "#2A3F8F"),
background: Color(hex: "#F5F6FA"),
surface: Color(hex: "#FFFFFF"),
surfaceElevated: Color(hex: "#ECEEF7"),
textPrimary: Color(hex: "#111827"),
textSecondary: Color(hex: "#6B7280"),
border: Color(hex: "#D1D5EB"),
accent: Color(hex: "#5B6FD4"),
success: Color(hex: "#16A34A"),
warning: Color(hex: "#D97706"),
error: Color(hex: "#DC2626")
),
radius: DFRadiusTokens(sm: 4, md: 9, lg: 16, full: 999),
shadow: DFShadowTokens(opacity: 0.08)
)
let midnightDark = DFTheme(
colors: DFColorTokens(
primary: Color(hex: "#7B93E8"),
background: Color(hex: "#0D1023"),
surface: Color(hex: "#151929"),
surfaceElevated: Color(hex: "#1E2440"),
textPrimary: Color(hex: "#F1F3FF"),
textSecondary: Color(hex: "#9EA8CC"),
border: Color(hex: "#2C3560"),
accent: Color(hex: "#A0AEFF"),
success: Color(hex: "#4ADE80"),
warning: Color(hex: "#FCD34D"),
error: Color(hex: "#F87171")
),
radius: DFRadiusTokens(sm: 4, md: 9, lg: 16, full: 999),
shadow: DFShadowTokens(opacity: 0.12)
)
let midnightPreset = DFThemePreset(light: midnightLight, dark: midnightDark)
// Apply like any built-in preset
ContentView().dfThemePreset(midnightPreset)Tip: Extend
DFThemePresetwith a static property to get dot-shorthand:extension DFThemePreset { static let midnight = DFThemePreset(light: midnightLight, dark: midnightDark) } ContentView().dfThemePreset(.midnight)
DFThemePreset conforms to CaseIterable, so you can render all five built-in presets in a single #Preview block:
#Preview("All Presets") {
ScrollView {
VStack(spacing: 24) {
ForEach(DFThemePreset.allCases) { preset in
VStack(alignment: .leading, spacing: 8) {
DFText(preset.name, style: .caption)
.padding(.horizontal)
MyCardComponent()
.dfThemePreset(preset)
}
}
}
.padding(.vertical)
}
}For light/dark comparison of a single preset:
#Preview("Slate — Light and Dark") {
HStack(spacing: 0) {
MyCardComponent()
.dfTheme(.slateLight)
.frame(maxWidth: .infinity)
MyCardComponent()
.dfTheme(.slateDark)
.frame(maxWidth: .infinity)
.preferredColorScheme(.dark)
}
}import DesignFoundation
import Observation
@Observable
final class AppSettings {
var themePreset: DFThemePreset = .slate
private let key = "app.themePreset"
init() {
if let saved = UserDefaults.standard.string(forKey: key),
let preset = DFThemePreset.allCases.first(where: { $0.name == saved }) {
themePreset = preset
}
}
func save() {
UserDefaults.standard.set(themePreset.name, forKey: key)
}
}
@main
struct MyApp: App {
@State private var settings = AppSettings()
var body: some Scene {
WindowGroup {
ContentView()
.environment(settings)
.dfThemePreset(settings.themePreset)
}
}
}
struct AppearanceSettingsView: View {
@Environment(AppSettings.self) private var settings
var body: some View {
DFCard {
VStack(alignment: .leading, spacing: 16) {
DFText("Appearance", style: .headline)
DFPicker(
"Theme",
selection: Bindable(settings).themePreset,
options: DFThemePreset.allCases
)
// Live preview of the selected theme
PreviewTile()
.dfThemePreset(settings.themePreset)
DFButton("Save") { settings.save() }
}
}
.padding()
}
}@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
#if os(macOS)
ContentView().dfThemePreset(.slate) // professional on desktop
#else
ContentView().dfThemePreset(.aurora) // warmer on mobile
#endif
}
}
}visionOS note:
.aurorais particularly well-suited to visionOS — its light shadows and generous radius feel at home in a spatial context..sageand.defaultalso translate well..copper's strong shadows can feel heavy in mixed reality.
See also: Theming · Style-System · Swift-6-Concurrency · Liquid-Glass