Add fourteen skills from Dimillian/Skills, integrate the merged Snowflake and WordPress updates into the maintainer sync, and refresh registry metadata, attributions, walkthrough notes, and the 8.9.0 release notes while keeping validation warnings within budget.
1.7 KiB
1.7 KiB
Theming and dynamic type
Intent
Provide a clean, scalable theming approach that keeps view code semantic and consistent.
Core patterns
- Use a single
Themeobject as the source of truth (colors, fonts, spacing). - Inject theme at the app root and read it via
@Environment(Theme.self)in views. - Prefer semantic colors (
primaryBackground,secondaryBackground,label,tint) instead of raw colors. - Keep user-facing theme controls in a dedicated settings screen.
- Apply Dynamic Type scaling through custom fonts or
.font(.scaled...).
Example: Theme object
@MainActor
@Observable
final class Theme {
var tintColor: Color = .blue
var primaryBackground: Color = .white
var secondaryBackground: Color = .gray.opacity(0.1)
var labelColor: Color = .primary
var fontSizeScale: Double = 1.0
}
Example: inject at app root
@main
struct MyApp: App {
@State private var theme = Theme()
var body: some Scene {
WindowGroup {
AppView()
.environment(theme)
}
}
}
Example: view usage
struct ProfileView: View {
@Environment(Theme.self) private var theme
var body: some View {
VStack {
Text("Profile")
.foregroundStyle(theme.labelColor)
}
.background(theme.primaryBackground)
}
}
Design choices to keep
- Keep theme values semantic and minimal; avoid duplicating system colors.
- Store user-selected theme values in persistent storage if needed.
- Ensure contrast between text and backgrounds.
Pitfalls
- Avoid sprinkling raw
Colorvalues in views; it breaks consistency. - Do not tie theme to a single view’s local state.
- Avoid using
@Environment(\\.colorScheme)as the only theme control; it should complement your theme.