DEV Community

Cover image for Supporting light and dark appearance in SwiftUI
Daniil Kornilov
Daniil Kornilov

Posted on Edited on

Supporting light and dark appearance in SwiftUI

Disclosure: This article was created or edited with AI assistance. Revised on 13 October 2026 to remove promotional links and unsupported outcome claims. It is educational guidance, not a verified personal case study or a measured result. Examples are illustrative; use only your own documented experience and measurements.

The sections below present options and illustrative examples. Adapt them to the requirements of your project or role; they do not establish a universal rule or promise a result.

The Basics: Color Scheme

SwiftUI provides @Environment(\.colorScheme) to detect the current mode:

struct ContentView: View {
    @Environment(\.colorScheme) var colorScheme

    var body: some View {
        Text("Hello")
            .foregroundColor(colorScheme == .dark ? .white : .black)
    }
}
Enter fullscreen mode Exit fullscreen mode

But there's a better way...

Use Semantic Colors

SwiftUI has built-in colors that adapt automatically:

Text("Adapts automatically")
    .foregroundColor(.primary)      // Black in light, white in dark
    .background(Color(.systemBackground))  // White in light, black in dark
Enter fullscreen mode Exit fullscreen mode

Key semantic colors:

  • .primary / .secondary — text colors
  • Color(.systemBackground) — main background
  • Color(.secondarySystemBackground) — grouped content

Create a Theme System

For full control, create a theme:

struct AppTheme {
    let background: Color
    let text: Color
    let accent: Color

    static let light = AppTheme(
        background: .white,
        text: .black,
        accent: .blue
    )

    static let dark = AppTheme(
        background: Color(.systemGray6),
        text: .white,
        accent: .cyan
    )
}
Enter fullscreen mode Exit fullscreen mode

Use it with environment:

struct ThemeKey: EnvironmentKey {
    static let defaultValue = AppTheme.light
}

extension EnvironmentValues {
    var theme: AppTheme {
        get { self[ThemeKey.self] }
        set { self[ThemeKey.self] = newValue }
    }
}
Enter fullscreen mode Exit fullscreen mode

Preview Both Modes

Always test both modes in previews:

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        Group {
            ContentView()
                .preferredColorScheme(.light)
            ContentView()
                .preferredColorScheme(.dark)
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Common Mistakes

  1. Hardcoding colors — Use semantic colors instead
  2. Forgetting images — Use SF Symbols or provide dark variants
  3. Not testing — Always preview both modes

Scope of the code examples

These are separate teaching snippets, not a complete tested application or a production-ready package. Match availability to your deployment target, provide omitted model/service definitions, and check errors, cancellation, accessibility and persistence before adapting them.

Top comments (0)