# .toLCH()

Read a color as CIELab lightness, chroma and hue, to match LCH numbers or check contrast.

Match LCH numbers from a design tool, or read the lightness that decides contrast, with `toLCH()`. It takes nothing and returns an `LCHColor`: CIELab lightness from 0 to 100, chroma, and hue in degrees:

```swift
Color(hex: "#00B386").toLCH()   // LCHColor(l: 64.93, c: 50.9, h: 166)
```

## Making an LCHColor

```swift
LCHColor(l: 65, c: 51, h: 166)          // #00B386
LCHColor(hex: "#00B386")
LCHColor(lchString: "lch(65% 51 166)")
```

Write `lchString` with a percent sign on the lightness. A string it can't read gives a default color, #DF97AC, instead of an error, and lightness and chroma are kept within 0 to 100 and 0 to 128.

## Lightness that decides contrast

`l` is CIELab L\*, which follows luminance, the thing WCAG contrast measures. The palette gives every hue the same L\* at each stop, so a stop has the same contrast in every theme:

```swift
brand._600.toLCH().l            // about 48.8
Color.proBlue._600.toLCH().l    // about 48.8, the same
```

[How the ramps are built](https://colortokenskit.com/under-the-hood/how-ramps-are-built/index.md) lists the L\* of every stop.

## Numbers from other tools

`LCHColor` uses the D65 white point. CSS Color 4's `lch()` uses D50, so LCH numbers from a tool built on it give a slightly different color here. For numbers that match exactly, use [OKLCH](https://colortokenskit.com/api/to-oklch/index.md): `OKLCHColor` matches `oklch()` to within rounding.

## How it behaves

Conversions pass through extended sRGB without clipping, so a Display P3 color comes through unchanged, with more chroma than sRGB can hold. Hues are stored wrapped to 0 to 360 and rounded to 0.01°. A token gives its light-mode value on iOS 17, macOS 14 and later, and [`toColor()`](https://colortokenskit.com/api/to-color/index.md) and `getColor(l:c:h:alpha:)` give an opaque color.

`_50` to `_1000`, `allStops` and `getColor(at:)` read the ramp at the color's OKLCH hue, and `LCHColor.getPrimaryColor(forHue:isGrayscale:)` gives a hue's `_450`. From an `LCHColor` you can go to `toRGB()`, `toXYZ()`, `toLAB()` and `toColor()`.

## API

```swift
public extension Color {
    func toLCH() -> LCHColor
}

public struct LCHColor: Hashable, Sendable {
    public let l: CGFloat       // 0 to 100, CIELab L*
    public let c: CGFloat       // 0 to 134 inside sRGB
    public let h: CGFloat       // 0 to 360
    public let alpha: CGFloat   // 0 to 1

    public init(l: CGFloat = 0, c: CGFloat = 0, h: CGFloat = 0, alpha: CGFloat = 1)
    public init(color: Color)
    public init(hex: String)
    public init(lchString: String)

    public func getColor(
        l: CGFloat? = nil, c: CGFloat? = nil, h: CGFloat? = nil, alpha: CGFloat? = nil
    ) -> Color
    public func getColor(at index: Int) -> LCHColor
    public var allStops: [LCHColor] { get }
    public var _50: LCHColor { get }   // and every stop to _1000
    public static func getPrimaryColor(forHue hue: Double, isGrayscale: Bool = false) -> LCHColor
    public func lerp(_ other: LCHColor, t: CGFloat) -> LCHColor

    public func toRGB() -> RGBColor
    public func toXYZ() -> XYZColor
    public func toLAB() -> LABColor
    public func toColor() -> Color
}
```

`toLCH()` takes no parameters. `ProTheme` has it too, as in `brand._600.toLCH()`. For a quick line to log, [`.getLCHString()`](https://colortokenskit.com/api/get-lch-string/index.md) gives the same numbers rounded down.

## Sources

- [Color+ColorSpaces.swift](https://github.com/metasidd/ColorTokensKit-Swift/blob/main/Sources/ColorTokensKit/Platform/SwiftUI/Color+ColorSpaces.swift), which defines `toLCH()` on `Color`.
- The [LCH](https://github.com/metasidd/ColorTokensKit-Swift/tree/main/Sources/ColorTokensKit/ColorSpace/LCH) folder, which defines `LCHColor`, its string parser and its stops.
- W3C, [CSS Color Module Level 4](https://www.w3.org/TR/css-color-4/), which defines `lch()` with the D50 white point.

## See also

- [.toLAB()](https://colortokenskit.com/api/to-lab/index.md): Change a color's lightness alone in CIELab, the lightness that decides contrast.
- [.getLCHString()](https://colortokenskit.com/api/get-lch-string/index.md): Log a color's lightness, chroma and hue in one short line, to check a color at a glance.
- [.toOKLCH()](https://colortokenskit.com/api/to-oklch/index.md): Read a color's lightness, chroma and hue the way the palette sees them, to compare or adjust it.
- [How the ramps are built](https://colortokenskit.com/under-the-hood/how-ramps-are-built/index.md): The lightness and chroma behind every stop, so any hue you pick gives the same contrast.

---

From ColorTokensKit, by Penguin Design Ventures: https://colortokenskit.com/api/to-lch/ (updated September 26, 2026).
