Skip to content
ColorTokensKitby Penguin Design Ventures
Contents
Function

.rotateHue(by:)

Make an accent or a whole second theme from your brand color, at the same lightness.

Updated View as Markdown

Make an accent, or a whole second theme, from your brand color with rotateHue(by:). It turns the hue around the OKLCH color wheel and keeps the lightness, so contrast doesn't change. On a ProTheme it returns a theme at the new hue, with every token; on a Color it returns one color:

swift
let accent = brand.rotateHue(by: .degrees(-120))   // your brand's theme, turned back 120°

HStack {
    Text("Book now")
        .padding(12)
        .foregroundStyle(brand.foregroundPrimary)
        .background(brand.backgroundTertiary)
    Text("€480")
        .padding(12)
        .foregroundStyle(accent.foregroundPrimary)
        .background(accent.backgroundTertiary)
}
light mode
Book now€480

If your brand color changes, the accent follows at the same angle. Turn the hue below: a palette color lands on the same stop of the new hue, so its contrast stays the same:

Try it / rotateHue()
Color
light mode
Before
#4176b8proBlue._600
After
#696bbcproIris._600
dark mode
Before
#4176b8proBlue._600
After
#696bbcproIris._600
swift
Color.proBlue._600.toColor().rotateHue(by: .degrees(30))

One color at a time#

On a token, rotateHue(by:) turns just that color. Here a deal badge uses the hue opposite your brand:

swift
let text = brand.foregroundPrimary.rotateHue(by: .degrees(180))    // _1000 in light, _50 in dark
let fill = brand.backgroundTertiary.rotateHue(by: .degrees(180))   // _200 in light, _700 in dark

Text("Deal").padding(8).foregroundStyle(text).background(fill)
light mode
Deal

When you need several colors from one hue, turn the theme once instead, as in the first example. For the opposite hue, complement does the same turn.

Angles are OKLCH hues#

The angle turns the hue on the OKLCH wheel, where blue sits near 250° and red near 20°. An angle from a design tool's HSB wheel won't land on the same color. triad and harmony(_:) turn one color into evenly spaced sets.

How it behaves#

A palette color lands on the same stop of the new hue's ramp. On a ProTheme, every stop and token matches ProTheme.primary(forHue:) at the new hue. A color off the palette keeps its lightness and chroma, as much as Display P3 can show at the new hue.

Gray, white and black have no hue, so they come back unchanged, and so does Color.proGray. Angles past 360° wrap around, and a negative angle turns the other way. The result keeps the color's opacity.

API#

swift
public extension Color {
    func rotateHue(by angle: Angle) -> Color
}

public extension ProTheme {
    func rotateHue(by angle: Angle) -> ProTheme
}
ParameterTypeDefaultWhat other values do
angleAnglenoneA negative angle turns the other way. Angles past 360° wrap around.

On a Color, it returns a new Color that follows light and dark mode. On a ProTheme, it returns a ProTheme at the new hue.

Sources#

See also

  • Color theory Pair stops that always read, and pick secondary hues that match your brand, in both modes.
  • Using tokens in charts Give every chart series equal contrast, plus a second cue for readers who can't tell hues apart.
  • Setting up themes Recolor a view, a screen or your whole app from one value, with the same contrast in every hue.
  • ProTheme Make a theme from a hex, a hue or OKLCH numbers, and every stop and token keeps its contrast.