Skip to content
ColorTokensKitby Penguin Design Ventures
Contents
Function

.toLAB()

Change a color's lightness alone in CIELab, the lightness that decides contrast.

Updated View as Markdown

Change a color's lightness and nothing else with toLAB(): CIELab L* from 0 to 100, plus a (green to red) and b (blue to yellow). It takes nothing and returns a LABColor:

swift
Color(hex: "#00B386").toLAB()   // LABColor(l: 64.93, a: -49.37, b: 12.31)

Changing lightness alone#

adjustLightness(by:) adds to L* and keeps a and b, so the color gets lighter or darker without turning another hue:

swift
let lab = Color(hex: "#00B386").toLAB()
let lighter = lab.adjustLightness(by: 10)   // LABColor(l: 74.93, a: -49.37, b: 12.31)
let fill = lighter.toLCH().toColor()        // #3CCFA0
#00b386
lab
#3ccfa0
lighter

For a palette color, lighten(by:) is usually the better choice: it moves whole stops, so the result is a palette color too.

Replacing a value#

with(l:a:b:alpha:) returns a copy with the values you pass and keeps the rest, as in lab.with(alpha: 0.5).

How it behaves#

adjustLightness(by:) keeps L* within 0 to 100. Conversions use the D65 white point and pass through extended sRGB without clipping. Inside sRGB, a runs from about −86 to 98 and b from about −108 to 94; Display P3 colors reach further. A token gives its light-mode value on iOS 17, macOS 14 and later.

LABColor has no toColor(), so go through another type, as in lab.toLCH().toColor(). From a LABColor you can go to toRGB(), toXYZ() and toLCH().

API#

swift
public extension Color {
    func toLAB() -> LABColor
}

public struct LABColor: Hashable, Sendable {
    public let l: CGFloat       // 0 to 100, CIELab L*
    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)

    public func with(
        l: CGFloat? = nil, a: CGFloat? = nil, b: CGFloat? = nil, alpha: CGFloat? = nil
    ) -> LABColor
    public func adjustLightness(by amount: CGFloat) -> LABColor
    public func lerp(_ other: LABColor, t: CGFloat) -> LABColor

    public func toRGB() -> RGBColor
    public func toXYZ() -> XYZColor
    public func toLCH() -> LCHColor
}
ParameterTypeWhat it does
amountCGFloatL* to add. Negative values darken. The result stays within 0 to 100.

Sources#

  • Color+ColorSpaces.swift, which defines toLAB() on Color.
  • The LAB folder, which defines LABColor, with(l:a:b:alpha:) and adjustLightness(by:).

See also

  • .toLCH() Read a color as CIELab lightness, chroma and hue, to match LCH numbers or check contrast.
  • .lighten(by:) Add highlights and lighter steps to any color, landing on an exact palette stop.
  • .toXYZ() Get a color's CIE XYZ values, where y is the relative luminance contrast is built on.
  • How accessible is it? What the palette guarantees for contrast in every hue and mode, and what you still need to check.