swiftui-colors-modifiers
SKILL.md
SwiftUI Colors and Modifiers
Comprehensive guide to modern SwiftUI color APIs, ShapeStyle, gradients, and creating reusable ViewModifiers for iOS 26.
Prerequisites
- iOS 15+ for foregroundStyle (iOS 26 recommended)
- Xcode 26+
Modern Color APIs
foregroundStyle (Recommended)
Replaces the deprecated foregroundColor(_:):
// DEPRECATED
Text("Hello")
.foregroundColor(.blue)
// MODERN - Use foregroundStyle
Text("Hello")
.foregroundStyle(.blue)
// With gradients
Text("Gradient Text")
.foregroundStyle(
LinearGradient(
colors: [.blue, .purple],
startPoint: .leading,
endPoint: .trailing
)
)
ShapeStyle Protocol
foregroundStyle accepts any ShapeStyle:
// Colors
.foregroundStyle(.red)
.foregroundStyle(Color.blue)
// Gradients
.foregroundStyle(LinearGradient(...))
.foregroundStyle(RadialGradient(...))
.foregroundStyle(AngularGradient(...))
.foregroundStyle(MeshGradient(...))
// Materials
.foregroundStyle(.ultraThinMaterial)
.foregroundStyle(.regularMaterial)
// Hierarchical
.foregroundStyle(.primary)
.foregroundStyle(.secondary)
.foregroundStyle(.tertiary)
Hierarchical Colors
Setting Hierarchy at Root
// Set all three levels at once
ContentView()
.foregroundStyle(.red, .orange, .yellow)
// Children use hierarchical levels
struct ContentView: View {
var body: some View {
VStack {
Text("Primary") // Red
.foregroundStyle(.primary)
Text("Secondary") // Orange
.foregroundStyle(.secondary)
Text("Tertiary") // Yellow
.foregroundStyle(.tertiary)
}
}
}
Available Levels
.foregroundStyle(.primary) // Level 1 - Most prominent
.foregroundStyle(.secondary) // Level 2
.foregroundStyle(.tertiary) // Level 3
.foregroundStyle(.quaternary) // Level 4
.foregroundStyle(.quinary) // Level 5 - Least prominent
Note: Only first three can be customized via foregroundStyle(_:_:_:).
Practical Example
struct CardView: View {
let title: String
let subtitle: String
let detail: String
var body: some View {
VStack(alignment: .leading, spacing: 4) {
Text(title)
.font(.headline)
.foregroundStyle(.primary)
Text(subtitle)
.font(.subheadline)
.foregroundStyle(.secondary)
Text(detail)
.font(.caption)
.foregroundStyle(.tertiary)
}
}
}
Semantic Colors
System Colors
// Adaptive colors (change with Dark Mode)
Color.primary // Black/White
Color.secondary // Gray
Color.accentColor // App's accent color
// UI element colors
Color(uiColor: .systemBackground)
Color(uiColor: .secondarySystemBackground)
Color(uiColor: .tertiarySystemBackground)
Color(uiColor: .label)
Color(uiColor: .secondaryLabel)
Accent Color
Set in Asset Catalog or programmatically:
// In code
Button("Action") { }
.tint(.blue)
// App-wide in Assets.xcassets:
// Create "AccentColor" color set
Tint Modifier
Override accent color for a hierarchy:
// tint affects interactive elements, not all foreground
VStack {
Button("Blue") { } // Uses blue tint
Link("Website", destination: url) // Uses blue tint
Text("Plain") // NOT affected by tint
}
.tint(.blue)
tint vs foregroundStyle:
tint: Affects buttons, links, controlsforegroundStyle: Affects all foreground content
Gradients
LinearGradient
LinearGradient(
colors: [.blue, .purple],
startPoint: .topLeading,
endPoint: .bottomTrailing
)
// With stops for control
LinearGradient(
stops: [
.init(color: .red, location: 0),
.init(color: .orange, location: 0.3),
.init(color: .yellow, location: 1)
],
startPoint: .top,
endPoint: .bottom
)
// Usage
Rectangle()
.fill(
LinearGradient(
colors: [.blue, .cyan],
startPoint: .leading,
endPoint: .trailing
)
)
RadialGradient
RadialGradient(
colors: [.white, .blue],
center: .center,
startRadius: 0,
endRadius: 200
)
// With offset center
RadialGradient(
colors: [.yellow, .orange, .red],
center: .topLeading,
startRadius: 50,
endRadius: 300
)
AngularGradient
AngularGradient(
colors: [.red, .yellow, .green, .blue, .purple, .red],
center: .center
)
// Conic gradient with angle
AngularGradient(
colors: [.blue, .purple],
center: .center,
startAngle: .degrees(0),
endAngle: .degrees(180)
)
MeshGradient (iOS 18+)
Complex multi-point gradients:
MeshGradient(
width: 3,
height: 3,
points: [
// Row 0
[0.0, 0.0], [0.5, 0.0], [1.0, 0.0],
// Row 1
[0.0, 0.5], [0.5, 0.5], [1.0, 0.5],
// Row 2
[0.0, 1.0], [0.5, 1.0], [1.0, 1.0]
],
colors: [
.red, .orange, .yellow,
.green, .blue, .purple,
.pink, .cyan, .mint
]
)
// Animated mesh
struct AnimatedMesh: View {
@State private var offset: CGFloat = 0
var body: some View {
MeshGradient(
width: 3,
height: 3,
points: [
[0.0, 0.0], [0.5, 0.0], [1.0, 0.0],
[0.0, 0.5], [0.5 + offset, 0.5], [1.0, 0.5],
[0.0, 1.0], [0.5, 1.0], [1.0, 1.0]
],
colors: [
.blue, .cyan, .teal,
.purple, .indigo, .blue,
.pink, .orange, .yellow
],
smoothsColors: true
)
.onAppear {
withAnimation(.easeInOut(duration: 2).repeatForever()) {
offset = 0.2
}
}
}
}
Asset Catalog Colors
Creating Color Sets
- Open Assets.xcassets
- Right-click → New Color Set
- Configure for appearances:
- Any Appearance
- Light
- Dark
- High Contrast variants
Using Asset Colors
// By name
Color("BrandPrimary")
Color("BackgroundColor")
// With bundle
Color("CustomColor", bundle: .module)
Organizing Colors
Assets.xcassets/
├── Colors/
│ ├── Brand/
│ │ ├── BrandPrimary
│ │ ├── BrandSecondary
│ │ └── BrandAccent
│ ├── UI/
│ │ ├── BackgroundPrimary
│ │ ├── BackgroundSecondary
│ │ └── SeparatorColor
│ └── Text/
│ ├── TextPrimary
│ ├── TextSecondary
│ └── TextTertiary
Custom ShapeStyles (iOS 17+)
struct StripedStyle: ShapeStyle {
var color1: Color
var color2: Color
var stripeWidth: CGFloat
func resolve(in environment: EnvironmentValues) -> some ShapeStyle {
// Return a resolved style
LinearGradient(
stops: generateStripeStops(),
startPoint: .leading,
endPoint: .trailing
)
}
private func generateStripeStops() -> [Gradient.Stop] {
var stops: [Gradient.Stop] = []
var position: CGFloat = 0
while position < 1 {
stops.append(.init(color: color1, location: position))
stops.append(.init(color: color1, location: position + stripeWidth / 2))
stops.append(.init(color: color2, location: position + stripeWidth / 2))
stops.append(.init(color: color2, location: position + stripeWidth))
position += stripeWidth
}
return stops
}
}
// Usage
Rectangle()
.fill(StripedStyle(color1: .blue, color2: .white, stripeWidth: 0.1))
Custom ViewModifiers
Basic Modifier
struct CardStyle: ViewModifier {
func body(content: Content) -> some View {
content
.padding()
.background(.regularMaterial)
.clipShape(RoundedRectangle(cornerRadius: 12))
.shadow(radius: 4)
}
}
extension View {
func cardStyle() -> some View {
modifier(CardStyle())
}
}
// Usage
Text("Card Content")
.cardStyle()
Configurable Modifier
struct RoundedStyle: ViewModifier {
var cornerRadius: CGFloat
var backgroundColor: Color
var shadowRadius: CGFloat
func body(content: Content) -> some View {
content
.padding()
.background(backgroundColor)
.clipShape(RoundedRectangle(cornerRadius: cornerRadius))
.shadow(radius: shadowRadius)
}
}
extension View {
func rounded(
cornerRadius: CGFloat = 12,
backgroundColor: Color = .white,
shadowRadius: CGFloat = 4
) -> some View {
modifier(RoundedStyle(
cornerRadius: cornerRadius,
backgroundColor: backgroundColor,
shadowRadius: shadowRadius
))
}
}
// Usage
Text("Custom")
.rounded(cornerRadius: 20, backgroundColor: .blue)
Environment-Aware Modifier
struct AdaptiveCard: ViewModifier {
@Environment(\.colorScheme) var colorScheme
func body(content: Content) -> some View {
content
.padding()
.background(colorScheme == .dark ? Color.gray.opacity(0.2) : Color.white)
.clipShape(RoundedRectangle(cornerRadius: 12))
.shadow(
color: colorScheme == .dark ? .clear : .black.opacity(0.1),
radius: 8
)
}
}
Conditional Modifier
extension View {
@ViewBuilder
func `if`<Content: View>(
_ condition: Bool,
transform: (Self) -> Content
) -> some View {
if condition {
transform(self)
} else {
self
}
}
@ViewBuilder
func ifLet<T, Content: View>(
_ value: T?,
transform: (Self, T) -> Content
) -> some View {
if let value {
transform(self, value)
} else {
self
}
}
}
// Usage
Text("Hello")
.if(isHighlighted) { view in
view.foregroundStyle(.yellow)
}
.ifLet(user) { view, user in
view.badge(user.notificationCount)
}
iOS 26 New Modifiers
Close Button Role
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button("Dismiss", role: .close) {
dismiss()
}
// Renders as glass X button
}
}
Glass Button Styles
Button("Glass") { }
.buttonStyle(.glass)
Button("Prominent") { }
.buttonStyle(.glassProminent)
Custom Slider Ticks
Slider(value: $value, in: 0...100) {
Text("Value")
} minimumValueLabel: {
Text("0")
} maximumValueLabel: {
Text("100")
}
.sliderStyle(.ticked(count: 10)) // iOS 26
Design System Example
// DesignSystem.swift
enum DS {
enum Colors {
static let primary = Color("Primary")
static let secondary = Color("Secondary")
static let background = Color("Background")
static let surface = Color("Surface")
static let error = Color("Error")
static let success = Color("Success")
}
enum Spacing {
static let xs: CGFloat = 4
static let sm: CGFloat = 8
static let md: CGFloat = 16
static let lg: CGFloat = 24
static let xl: CGFloat = 32
}
enum CornerRadius {
static let sm: CGFloat = 4
static let md: CGFloat = 8
static let lg: CGFloat = 16
static let xl: CGFloat = 24
}
}
// Modifiers using design system
struct DSCard: ViewModifier {
func body(content: Content) -> some View {
content
.padding(DS.Spacing.md)
.background(DS.Colors.surface)
.clipShape(RoundedRectangle(cornerRadius: DS.CornerRadius.lg))
}
}
extension View {
func dsCard() -> some View {
modifier(DSCard())
}
}
Best Practices
- Use foregroundStyle - Not deprecated foregroundColor
- Leverage Hierarchical Colors - .primary, .secondary, .tertiary
- Asset Catalog for Themes - Organize colors properly
- DRY with Modifiers - Create reusable ViewModifiers
- Compose Modifiers - Build complex styles from simple ones
- Environment Awareness - Respect colorScheme and accessibility
Official Resources
Weekly Installs
6
Repository
bluewaves-creat…s-skillsGitHub Stars
1
First Seen
Jan 26, 2026
Security Audits
Installed on
opencode6
claude-code5
codex5
gemini-cli5
continue4
qwen-code4