# What are tokens?

Colors picked one at a time never line up. Tokens name each job and pick its color in both modes.

Tokens are how design teams keep an app's colors consistent across every screen, theme and mode. To see why they matter, start with how most apps handle colors today.

## The problem with how most apps are built

Most apps keep their colors in one file: the brand color as a constant, a few asset catalog colors with a light and a dark value, and SwiftUI's own colors for the rest.

```swift
extension Color {
    static let brand = Color(hex: "#F27333")          // from the brand guide
    static let brandPressed = Color(hex: "#D9602A")   // a darker orange, picked by eye
    static let card = Color("Card")                   // asset catalog: #F4F2EF light, #1C1C1E dark
    static let textMuted = Color("TextMuted")         // asset catalog: #6B6B70 light, #A1A1A6 dark
}

struct TripCard: View {
    var body: some View {
        VStack(alignment: .leading, spacing: 4) {
            HStack {
                Text("Lisbon")
                    .font(.headline)
                    .foregroundStyle(.primary)        // SwiftUI's own: black, or white in dark mode
                Spacer()
                Text("€620")
                    .foregroundStyle(Color.brand)
            }
            Text("3 nights from October 12")
                .font(.subheadline)
                .foregroundStyle(Color.textMuted)
            Text("Book now")
                .padding(8)
                .foregroundStyle(.white)
                .background(Color.brand)
        }
        .padding()
        .background(Color.card)
    }
}
```

What this draws:

| Color             | light mode | dark mode |
| ----------------- | ---------- | --------- |
| `#F4F2EF/#1C1C1E` | #f4f2ef    | #1c1c1e   |
| `#000000/#FFFFFF` | #000000    | #ffffff   |
| `#F27333`         | #f27333    | #f27333   |
| `#6B6B70/#A1A1A6` | #6b6b70    | #a1a1a6   |
| `white`           | #ffffff    | #ffffff   |

This works. The card follows dark mode through the asset catalog, the brand color lives in one place, and SwiftUI fills in the rest. The problems show up as the app grows:

- **Nothing checks the colors.** White on the brand orange measures 2.89:1, and the orange price on the light card 2.58:1, both well under the 4.5:1 that WCAG AA asks for text. Every pair is a guess until someone measures it.
- **Dark mode by hand.** Each asset needs a dark value someone picks and checks, and the brand orange and its pressed shade stay the same in both modes.
- **Shades picked by eye.** `brandPressed` is a guess, and a disabled button, a hover state or a tag tint is another hex value each time.
- **Hard to scale.** A second theme or a new feature color means another set of assets and another round of checks.

Even carefully picked colors don't line up. A color picker calls these four equally light, yet as text on white one vanishes and another is nearly black:

- hsl(60 100% 50%), 1.07:1: #ffff00
- hsl(120 100% 50%), 1.37:1: #00ff00
- hsl(0 100% 50%), 4.00:1: #ff0000
- hsl(240 100% 50%), 8.59:1: #0000ff

## How tokens fix it

How a token comes to be, for `proOrange.foregroundSecondary`:

1. Hue: `proOrange` sits at 50° of 36 hues.
2. Ramp: 20 stops, from `_50` to `_1000`.
3. Stops: `_800` (#67391b) and `_200` (#f9c9ab), each a fixed color, in Display P3 hex.
4. Job: `foregroundSecondary` picks `_800` in light mode and `_200` in dark.
5. Your app: secondary text, in both modes.

Tokens fix it in two steps: every hue gets the same lightness at each stop, and each job, like secondary text, gets a name that picks the right stop in each mode. The rest of this page follows that path, from a hue to your app.

## One hue becomes 20 stops

`proOrange`, lightest to darkest: #fdf3ec, #fce6d8, #fbd8c2, #f9c9ab, #f8ba92, #f7a977, #f6985a, #e88c4f, #da8043, #c9753d, #b86b37, #a86132, #97572c, #874d26, #774320, #67391b, #573015, #492711, #3a1e0c, #2d1607.

We turn each family's hue into 20 **stops**, from near-white `_50` to near-black `_1000`. A stop is one fixed color: `Color.proOrange._600` is the same orange in both modes. All 36 hues share one lightness at each stop, so a stop you check once is checked in every hue.

## A token names a job

`proOrange`, lightest to darkest: #fdf3ec, #fce6d8, #fbd8c2, #f9c9ab, #f8ba92, #f7a977, #f6985a, #e88c4f, #da8043, #c9753d, #b86b37, #a86132, #97572c, #874d26, #774320, #67391b, #573015, #492711, #3a1e0c, #2d1607. Marked: `_200` (dark mode), `_800` (light mode).

Your views don't need "orange `_800`". They need secondary text, a card background, a border. A **token** names that job and picks a stop for each mode: `foregroundSecondary` picks `_800` in light mode and `_200` in dark, the same distance from each end of the ramp:

```swift
Text("3 nights from October 12")
    .font(.subheadline)
    .foregroundStyle(Color.proOrange.foregroundSecondary)   // _800 in light mode, _200 in dark
```

What this draws, with `Color.proOrange`:

| Color                 | light mode | dark mode                           |
| --------------------- | ---------- | ----------------------------------- |
| `foregroundSecondary` | #6e3612    | color(display-p3 0.978 0.79 0.6716) |

In `ColorTokens.swift`, the file of tokens you copy into your app, the token is those two stops and nothing more. Defining a token is where `toColor()` belongs: it turns a stop into the SwiftUI `Color` that `Color(light:dark:)` takes.

```swift
public extension ProTheme {
    var foregroundSecondary: Color {
        Color(light: _800.toColor(), dark: _200.toColor())
    }
}
```

We chose each token's stops so every text token passes WCAG AA on the page background, `backgroundPrimary`, in both modes and every hue.

## A token for every part of a view

We give you 20 tokens that cover a whole screen: foreground for text and icons, background for screens and cards, surface for see-through layers, outline for borders, and inverted versions for areas in the opposite appearance. Here's one of each:

```swift
let theme = Color.proOrange

VStack(alignment: .leading, spacing: 8) {
    Text("Lisbon")
        .font(.headline)
        .foregroundStyle(theme.foregroundPrimary)            // foreground: text and icons
    Text("Flight included")
        .font(.subheadline)
        .padding(4)
        .foregroundStyle(theme.foregroundSecondary)
        .background(theme.surfacePrimary)                    // surface: see-through layers
        .border(theme.outlineSecondary)                      // outline: borders
    Text("Book now")
        .padding(8)
        .foregroundStyle(theme.invertedForegroundPrimary)    // inverted: the opposite appearance
        .background(theme.invertedBackgroundPrimary)
}
.padding()
.frame(maxWidth: .infinity, alignment: .leading)
.background(theme.backgroundPrimary)                         // background: screens and cards
```

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)    |
| `surfacePrimary`            | color(display-p3 0.978 0.79 0.6716 / 0.5) | rgb(144 73 26 / 0.5)                   |
| `outlineSecondary`          | color(display-p3 0.978 0.79 0.6716)       | #6e3612                                |
| `invertedForegroundPrimary` | color(display-p3 0.9936 0.9528 0.9271)    | #301404                                |
| `invertedBackgroundPrimary` | #4e250a                                   | color(display-p3 0.9936 0.9528 0.9271) |

Most groups have a primary, secondary and tertiary token. [Understanding semantic tokens](https://colortokenskit.com/getting-started/semantic-tokens/index.md) lists them all.

## Theme stops, theme tokens and app tokens

Every `ProTheme`, like `Color.proOrange`, has 20 stops and all 20 tokens. There's also one more set of tokens for the app as a whole:

|             | Example                               | In dark mode              |
| ----------- | ------------------------------------- | ------------------------- |
| Theme stop  | `Color.proOrange._800`                | Stays `_800`              |
| Theme token | `Color.proOrange.foregroundSecondary` | Switches to orange `_200` |
| App token   | `Color.foregroundSecondary`           | Switches to gray `_200`   |

Theme tokens let a view change theme in one line, as [setting up themes](https://colortokenskit.com/advanced/themes/index.md) shows. App tokens are the same 20 names on `Color` itself, built on gray, from `ColorTokens.swift`. Use them for the parts of your app that don't belong to a theme, such as settings:

```swift
HStack {
    Text("Price alerts")
        .foregroundStyle(Color.foregroundPrimary)
    Spacer()
    Text("On")
        .font(.subheadline)
        .foregroundStyle(Color.foregroundSecondary)
}
.padding()
.background(Color.backgroundPrimary)   // white in light mode, black in dark
```

What this draws, with `Color`:

| Color                 | light mode | dark mode |
| --------------------- | ---------- | --------- |
| `backgroundPrimary`   | #ffffff    | #000000   |
| `foregroundPrimary`   | #000001    | #ffffff   |
| `foregroundSecondary` | #2c2c2c    | #dddddd   |

A theme's page color keeps a little of its hue: `Color.proOrange.backgroundPrimary` is `_50`, a faint orange, in light mode, where the app token `Color.backgroundPrimary` is white.

## Sources

- The library's [ColorTokens.swift](https://github.com/metasidd/ColorTokensKit-Swift/blob/main/Tests/ColorTokensKitTests/Marketing/Setup/ColorTokens.swift), which defines the 20 tokens and the stops each one uses.
- The library's [UniformRamp.swift](https://github.com/metasidd/ColorTokensKit-Swift/blob/main/Sources/ColorTokensKit/Services/Ramps/UniformRamp.swift), which sets the lightness of each stop.
- W3C, [WCAG 2.2: contrast minimum](https://www.w3.org/TR/WCAG22/#contrast-minimum).

## See also

- [Why use ColorTokensKit over native colors?](https://colortokenskit.com/basics/why-colortokenskit/index.md): What you gain over SwiftUI's system colors, asset catalogs and hex values, and what it costs.
- [How did we choose and build these colors?](https://colortokenskit.com/basics/how-the-colors-were-built/index.md): Why every hue shares one lightness and one chroma at each stop, and how the 36 hues were named.
- [Using your first tokens](https://colortokenskit.com/getting-started/first-tokens/index.md): Color text, buttons, cards and lists with tokens, then theme a movie card in one line.
- [Color theory](https://colortokenskit.com/advanced/color-theory/index.md): Pair stops that always read, and pick secondary hues that match your brand, in both modes.
- [How accessible is it?](https://colortokenskit.com/basics/how-accessible/index.md): What the palette guarantees for contrast in every hue and mode, and what you still need to check.

---

From ColorTokensKit, by Penguin Design Ventures: https://colortokenskit.com/basics/what-are-tokens/ (updated September 26, 2026).
