Do your own color math in OKLab with toOKLab(): lightness plus two axes, green to red (a) and blue to yellow (b), spaced so equal steps look equal. It takes nothing and returns an OKLabColor:
Color(hex: "#00B386").toOKLab() // OKLabColor(l: 0.680, a: -0.136, b: 0.031)Mixing in a straight line#
OKLab mixes two colors along a straight line, so the middle can be a little grayer than either end. OKLCH turns around the hue wheel instead and keeps chroma up:
let start = Color(hex: "#00B386")
let end = Color.proSky._200.toOKLCH()
start.toOKLab().lerp(end.toOKLab(), t: 0.5) // #64C9C1, a little grayer
start.toOKLCH().lerp(end, t: 0.5) // #4BCACFTo mix two tokens and keep dark mode, use blend(with:by:). .lerp(_:t:) compares every space.
Same lightness as OKLCH#
OKLab and OKLCH share their lightness, l. OKLCH writes a and b as a chroma, how far from gray, and a hue angle, so toOKLCH() turns one into the other without any change in color.
OKLab's lightness isn't CIELab L*, the lightness contrast depends on, so two colors with the same l can have different contrast on white. Why OKLCH? shows a pair.
How it behaves#
Conversions pass through extended sRGB without clipping, so a Display P3 color comes through unchanged. Inside sRGB, a runs from about −0.23 to 0.28 and b from about −0.31 to 0.2; Display P3 colors reach further. A token gives its light-mode value on iOS 17, macOS 14 and later.
From an OKLabColor you can go to toRGB(), toLAB(), toLCH(), toOKLCH() and toColor(), which gives an opaque color.
API#
public extension Color {
func toOKLab() -> OKLabColor
}
public struct OKLabColor: Hashable, Sendable {
public let l: CGFloat // 0 to 1
public let a: CGFloat // green (negative) to red (positive)
public let b: CGFloat // blue (negative) to yellow (positive)
public let alpha: CGFloat // 0 to 1
public init(l: CGFloat, a: CGFloat, b: CGFloat, alpha: CGFloat = 1)
public func lerp(_ other: OKLabColor, t: CGFloat) -> OKLabColor
public func toRGB() -> RGBColor
public func toLAB() -> LABColor
public func toLCH() -> LCHColor
public func toOKLCH() -> OKLCHColor
public func toColor() -> Color
}toOKLab() takes no parameters.
Sources#
- Color+ColorSpaces.swift, which defines
toOKLab()onColor. - The OKLab folder, which defines
OKLabColor. - Björn Ottosson, A perceptual color space for image processing (2020), which introduces OKLab.
See also
- .toOKLCH() Read a color's lightness, chroma and hue the way the palette sees them, to compare or adjust it.
- .lerp(_:t:) Find the color partway between two others, for your own blends, chart scales or animations.
- .blend(with:by:) Mix two colors into one that follows dark mode, or fade a color out cleanly.
- Why OKLCH? Every hue looks evenly matched at each stop, so you can swap families without rechecking contrast.