Malachi.UI.TokenGen.Color (malachi v0.22.7)

View Source

An authored OKLCH color and the conversions the token generator needs from it.

Tokens are authored in OKLCH, and the web stylesheet and Malachi.UI.Tokens carry them as written, since both are CSS a browser draws. The terminal palette, the contrast and color vision checks and the snapshot's srgb8 receive sRGB instead. This module is that conversion, OKLCH to OKLab to linear sRGB, a clip into the unit cube in linear light, and the sRGB transfer function to bytes, with Björn Ottosson's published matrices.

The clip is deliberately the simple one. A color sRGB cannot show is moved channel by channel to the nearest face of the cube, and clip_delta/1 reports how far in OKLab that moved it, so the generator can refuse an authored color that sRGB cannot approximate ($gamut in the token file) instead of hiding the error inside a gamut mapping search.

Summary

Types

Linear light sRGB, nominally 0 to 1 per channel and outside it when out of gamut.

A point in OKLab: lightness, a, b.

An sRGB color as the bytes a screen shows.

t()

An authored OKLCH color: lightness 0 to 1, chroma 0 to 0.4, hue in degrees, alpha 0 to 1.

Functions

Clamps every channel of a linear sRGB color into 0..1.

How far, in OKLab, the clip into sRGB moves this color. Zero for a color inside the gamut.

The inverse sRGB transfer function, encoded value to linear light.

The Euclidean distance between two OKLab points.

The sRGB transfer function, linear light to the encoded value.

Linear sRGB to OKLab.

OKLab to linear sRGB, unclipped.

An OKLab point as sRGB bytes, after the clip.

Parses an authored oklch(L C H) or oklch(L C H / A) string.

sRGB bytes in linear light.

sRGB bytes in OKLab.

The color in OKLab.

The color as the sRGB bytes a screen shows, after the clip.

Types

linear()

@type linear() :: {float(), float(), float()}

Linear light sRGB, nominally 0 to 1 per channel and outside it when out of gamut.

oklab()

@type oklab() :: {float(), float(), float()}

A point in OKLab: lightness, a, b.

srgb8()

@type srgb8() :: {0..255, 0..255, 0..255}

An sRGB color as the bytes a screen shows.

t()

@type t() :: %Malachi.UI.TokenGen.Color{
  alpha: float(),
  c: float(),
  h: float(),
  l: float()
}

An authored OKLCH color: lightness 0 to 1, chroma 0 to 0.4, hue in degrees, alpha 0 to 1.

Functions

clip(arg)

@spec clip(linear()) :: linear()

Clamps every channel of a linear sRGB color into 0..1.

clip_delta(color)

@spec clip_delta(t()) :: float()

How far, in OKLab, the clip into sRGB moves this color. Zero for a color inside the gamut.

decode(x)

@spec decode(float()) :: float()

The inverse sRGB transfer function, encoded value to linear light.

delta_e_ok(arg1, arg2)

@spec delta_e_ok(oklab(), oklab()) :: float()

The Euclidean distance between two OKLab points.

encode(x)

@spec encode(float()) :: float()

The sRGB transfer function, linear light to the encoded value.

linear_srgb_to_oklab(arg)

@spec linear_srgb_to_oklab(linear()) :: oklab()

Linear sRGB to OKLab.

oklab_to_linear_srgb(arg)

@spec oklab_to_linear_srgb(oklab()) :: linear()

OKLab to linear sRGB, unclipped.

oklab_to_srgb8(lab)

@spec oklab_to_srgb8(oklab()) :: srgb8()

An OKLab point as sRGB bytes, after the clip.

parse(text)

@spec parse(String.t()) :: {:ok, t()} | {:error, String.t()}

Parses an authored oklch(L C H) or oklch(L C H / A) string.

Lightness must lie in 0..1, chroma in 0..0.4, hue in 0 up to but excluding 360, and alpha in 0..1. Percentages, commas, missing components and any other color syntax are refused.

srgb8_to_linear(arg)

@spec srgb8_to_linear(srgb8()) :: linear()

sRGB bytes in linear light.

srgb8_to_oklab(rgb)

@spec srgb8_to_oklab(srgb8()) :: oklab()

sRGB bytes in OKLab.

to_oklab(color)

@spec to_oklab(t()) :: oklab()

The color in OKLab.

to_srgb8(color)

@spec to_srgb8(t()) :: srgb8()

The color as the sRGB bytes a screen shows, after the clip.