Skip to content
ColorTokensKitby Penguin Design Ventures
Contents
Function

.triad

Secondary and tertiary colors for tags and categories, at the same contrast as your brand.

Updated View as Markdown

Use secondary and tertiary colors across your interface with .triad: your color and the two hues a third of the way around the wheel. All three keep your color's lightness, so every tag has the same contrast.

swift
let secondary = brand.triad[1]   // your brand's family, turned 120°
let tertiary = brand.triad[2]    // and turned 240°

HStack {
    Text("Beach")
        .padding(8)
        .foregroundStyle(brand.foregroundPrimary)   // _1000 in light mode, _50 in dark
        .background(brand.backgroundTertiary)       // _200 in light mode, _700 in dark
    Text("City")
        .padding(8)
        .foregroundStyle(secondary.foregroundPrimary)
        .background(secondary.backgroundTertiary)
    Text("Food")
        .padding(8)
        .foregroundStyle(tertiary.foregroundPrimary)
        .background(tertiary.backgroundTertiary)
}
light mode
BeachCityFood

Pick your color below, then set "See as" to "Lightness only": all three turn one gray.

Try it / Harmonies
Color

Every OKLCH hue at this color's lightness

0°90°180°270°360°
light mode
#63a7fdproBlue._400
#ed8295proRuby._400
#8ab24dproLime._400
dark mode
#63a7fdproBlue._400
#ed8295proRuby._400
#8ab24dproLime._400
swift
let color = Color.proBlue._400.toColor()
color.triad

Naming your secondary and tertiary colors#

Name the two new families once, next to your brand, and every token works on them in both modes:

swift
extension Color {
    static let brand = ProTheme(hex: "#00B386")
    static let brandSecondary = brand.triad[1]
    static let brandTertiary = brand.triad[2]
}

Three backgrounds from one token#

The Color version keeps a token's job, so the triad of a background is three backgrounds. They share its lightness, so one text color reads on all three:

swift
let tags = ["Beach", "City", "Food"]
let backgrounds = brand.backgroundSecondary.triad
// _100 in light mode, _800 in dark, at 167°, 287° and 47°

HStack {
    ForEach(tags.indices, id: \.self) { index in
        Text(tags[index])
            .padding()
            .foregroundStyle(brand.foregroundPrimary)
            .background(backgrounds[index])
    }
}
light mode
BeachCityFood

How it behaves#

The colors come back in a fixed order: yours, then 120° and 240° around the wheel. Each is turned with rotateHue(by:), so a palette color lands on the same stop at its new hue. The built-in families sit every 10°, so a built-in family's triad is built-in too: Color.proBlue.triad is blue, ruby and lime.

On a ProTheme, you get whole families at the stop you started from. Gray has no hue, so its triad is three grays.

Only hue tells the three apart, so they match in grayscale, and some people with a color vision deficiency can't tell every pair apart. When color carries meaning, add a label or an icon, as using tokens in charts shows.

API#

swift
public extension Color {
    var triad: [Color] { get }
}

public extension ProTheme {
    var triad: [ProTheme] { get }
}

It takes no parameters and returns three colors or families: yours, then the hues 120° and 240° away. It's the same as harmony(.triad). Color theory shows the triad across a whole screen.

Sources#

See also

  • Color theory Pair stops that always read, and pick secondary hues that match your brand, in both modes.
  • .complement Give a price or badge an accent that stands apart from your brand, at the same contrast.
  • .square Four hues spaced evenly around the wheel for chart series and categories, at one contrast.
  • .harmony(_:) Build the harmony someone picks at runtime, from a picker or a setting, in one call.
  • Setting up themes Recolor a view, a screen or your whole app from one value, with the same contrast in every hue.