# Setting up themes

Recolor a view, a screen or your whole app from one value, with the same contrast in every hue.

Recolor a view, a screen or your whole app by changing one value. Draw with a family's [semantic tokens](https://colortokenskit.com/getting-started/semantic-tokens/index.md) instead of fixed colors, and the contrast stays the same in every hue, in light and dark mode.

## Theme one view

Give the view a `theme`, draw it with the theme's tokens, and pass any family:

```swift
struct TripCard: View {
    let destination: String
    let theme: ProTheme

    var body: some View {
        VStack(alignment: .leading, spacing: 4) {
            Text(destination)
                .font(.headline)
                .foregroundStyle(theme.foregroundPrimary)
            Text("3 nights from October 12")
                .font(.subheadline)
                .foregroundStyle(theme.foregroundSecondary)
        }
        .padding()
        .background(theme.backgroundPrimary)
        .border(theme.outlineSecondary)
    }
}

VStack {
    TripCard(destination: "Lisbon", theme: Color.proOrange)
    TripCard(destination: "Kyoto", theme: Color.proPink)
    TripCard(destination: "Reykjavík", theme: Color.proSky)
}
```

What this draws:

| Color                        | light mode                             | dark mode                              |
| ---------------------------- | -------------------------------------- | -------------------------------------- |
| `orange.backgroundPrimary`   | color(display-p3 0.9936 0.9528 0.9271) | #301404                                |
| `orange.outlineSecondary`    | color(display-p3 0.978 0.79 0.6716)    | #6e3612                                |
| `orange.foregroundPrimary`   | #301404                                | color(display-p3 0.9936 0.9528 0.9271) |
| `orange.foregroundSecondary` | #6e3612                                | color(display-p3 0.978 0.79 0.6716)    |
| `pink.backgroundPrimary`     | color(display-p3 0.9953 0.9483 0.9611) | #31111c                                |
| `pink.outlineSecondary`      | color(display-p3 0.9839 0.7697 0.8336) | #6f3147                                |
| `pink.foregroundPrimary`     | #31111c                                | color(display-p3 0.9953 0.9483 0.9611) |
| `pink.foregroundSecondary`   | #6f3147                                | color(display-p3 0.9839 0.7697 0.8336) |
| `sky.backgroundPrimary`      | #eaf7fe                                | color(display-p3 0.0119 0.1193 0.1786) |
| `sky.outlineSecondary`       | #9addfd                                | color(display-p3 0.0596 0.2924 0.4106) |
| `sky.foregroundPrimary`      | color(display-p3 0.0119 0.1193 0.1786) | #eaf7fe                                |
| `sky.foregroundSecondary`    | color(display-p3 0.0596 0.2924 0.4106) | #9addfd                                |

The subtitle is 8.72:1 on the card in light mode and 11.48:1 in dark, whichever hue you pass. Pick another family, or type your brand's hex:

The live page draws the Lisbon trip card with only one family's tokens, in light and dark mode. For `Color.proBlue`, the title is #0b1c32 in light mode and #eff6ff in dark mode on a background that is #eff6ff in light mode and #0b1c32 in dark mode. [Try it on the live page](https://colortokenskit.com/advanced/themes/).

A theme is one hue, so you can store it with `@AppStorage` and let people pick their own.

## Theme a screen

When a whole screen shares a theme, put it in the SwiftUI environment, and one modifier sets it for every view inside:

```swift
extension EnvironmentValues {
    @Entry var theme: ProTheme = Color.proBlue
}

struct Tag: View {
    @Environment(\.theme) private var theme
    let title: String

    var body: some View {
        Text(title)
            .font(.subheadline)
            .padding(.vertical, 4)
            .padding(.horizontal, 10)
            .foregroundStyle(theme.foregroundPrimary)
            .background(theme.backgroundTertiary)
    }
}

struct TripScreen: View {
    @Environment(\.theme) private var theme

    var body: some View {
        VStack(alignment: .leading) {
            Text("Lisbon")
                .font(.headline)
                .foregroundStyle(theme.foregroundPrimary)
            Text("3 nights from October 12")
                .font(.subheadline)
                .foregroundStyle(theme.foregroundSecondary)
            HStack {
                Tag(title: "Beach")
                Tag(title: "City")
                Tag(title: "Food")
            }
        }
        .frame(maxWidth: .infinity, alignment: .leading)
        .padding()
        .background(theme.backgroundPrimary)
    }
}

TripScreen()
    .environment(\.theme, Color.proOrange)
```

What this draws, with `Color.proOrange`:

| Color                 | light mode                             | dark mode                              |
| --------------------- | -------------------------------------- | -------------------------------------- |
| `backgroundPrimary`   | color(display-p3 0.9936 0.9528 0.9271) | #301404                                |
| `foregroundPrimary`   | #301404                                | color(display-p3 0.9936 0.9528 0.9271) |
| `foregroundSecondary` | #6e3612                                | color(display-p3 0.978 0.79 0.6716)    |
| `backgroundTertiary`  | color(display-p3 0.978 0.79 0.6716)    | #90491a                                |

`Tag` reads the nearest theme from the environment. `@Entry` needs Xcode 16; with an older Xcode, write an `EnvironmentKey`, and because `ProTheme` is `Sendable`, it can be the key's default value.

## Theme the whole app from your brand color

Make a family from your brand's hex value:

- \#00B386

```swift
extension Color {
    static let brand = ProTheme(hex: "#00B386")
}
```

Then set it once, where your app creates its window:

```swift
@main
struct TripsApp: App {
    var body: some Scene {
        WindowGroup {
            TripScreen()
                .environment(\.theme, Color.brand)
        }
    }
}
```

What this draws, with `#00B386`:

| Color                 | light mode                             | dark mode                              |
| --------------------- | -------------------------------------- | -------------------------------------- |
| `backgroundPrimary`   | #eaf8f2                                | color(display-p3 0.0176 0.1298 0.086)  |
| `foregroundPrimary`   | color(display-p3 0.0176 0.1298 0.086)  | #eaf8f2                                |
| `foregroundSecondary` | color(display-p3 0.0797 0.3134 0.2262) | #9ce2c5                                |
| `backgroundTertiary`  | #9ce2c5                                | color(display-p3 0.1149 0.4143 0.3028) |

Your brand family has all 20 stops and every token, with the same contrast as the built-in families. `Color.brand.toColor()` is still exactly #00B386, for your logo, and a gray hex gives you a gray family. [Replacing your app colors](https://colortokenskit.com/getting-started/replacing-colors/index.md) moves the rest of your colors over.

## Add a matching second theme

A harmony of your brand family is a whole family too. `complement` is the one across the hue wheel; give it to the part of your app that should stand apart:

```swift
extension Color {
    static let brandSecondary = brand.complement   // hue 347°, across the wheel from 167°
}

VStack(spacing: 0) {
    TripScreen()
    TripScreen()
        .environment(\.theme, Color.brandSecondary)
}
.environment(\.theme, Color.brand)
```

What this draws:

| Color                 | #00B386 light mode                     | #00B386 dark mode                      | ProTheme.primary(forHue: 346.99) light mode | ProTheme.primary(forHue: 346.99) dark mode |
| --------------------- | -------------------------------------- | -------------------------------------- | ------------------------------------------- | ------------------------------------------ |
| `backgroundPrimary`   | #eaf8f2                                | color(display-p3 0.0176 0.1298 0.086)  | #fff1f8                                     | #2f1222                                    |
| `foregroundPrimary`   | color(display-p3 0.0176 0.1298 0.086)  | #eaf8f2                                | #2f1222                                     | #fff1f8                                    |
| `foregroundSecondary` | color(display-p3 0.0797 0.3134 0.2262) | #9ce2c5                                | #6b3252                                     | color(display-p3 0.9671 0.7712 0.879)      |
| `backgroundTertiary`  | #9ce2c5                                | color(display-p3 0.1149 0.4143 0.3028) | color(display-p3 0.9671 0.7712 0.879)       | #8d436d                                    |

The nearest `.environment` wins, so the second screen takes the secondary theme. [Color theory](https://colortokenskit.com/advanced/color-theory/index.md#secondary-and-tertiary-colors) has more hues that go with yours.

## See also

- [ProTheme](https://colortokenskit.com/api/protheme/index.md): Make a theme from a hex, a hue or OKLCH numbers, and every stop and token keeps its contrast.
- [Ready-made themes](https://colortokenskit.com/api/named-themes/index.md): 37 themes to use as they are, from proPink to proRose, each with 20 stops and every token.
- [Managing dark mode](https://colortokenskit.com/getting-started/dark-mode/index.md): Get dark mode without extra code: tokens switch stops for you, and your own colors can too.
- [What does the library offer?](https://colortokenskit.com/basics/color-library/index.md): 36 color themes, ready-to-use tokens with dark mode, a theme for your brand, and state functions.

---

From ColorTokensKit, by Penguin Design Ventures: https://colortokenskit.com/advanced/themes/ (updated September 26, 2026).
