Calculates the contrast of two colors, and moves the lightness of a color
A color is a #rrggbb string of sRGB. contrast/2 returns the contrast ratio
of WCAG 2.2, from 1 to 21. lightness_contrast/2 returns the lightness
contrast Lc of APCA, from 0 to about 108. blend/3 returns the color of a
foreground with an opacity on a background, as a browser paints it.
adjust/4 makes a foreground lighter or darker until it has a contrast ratio
and a lightness contrast with a background. It changes only the lightness of
the color in OKLab, so the hue stays the same. Expresso.Palette uses these
functions for the themes.
Summary
Functions
Make a foreground lighter or darker until its contrast with the background is
at least minimum, and its lightness contrast is at least lc
Return the color of a foreground with an opacity from 0 to 1 on a background
Return the contrast ratio of two colors, as WCAG 2.2 defines it
Return the lightness contrast Lc of a text on a background, as APCA defines it
Return the relative luminance of a color, from 0 for black to 1 for white
Types
@type t() :: String.t()
A color as #rrggbb
Functions
Make a foreground lighter or darker until its contrast with the background is
at least minimum, and its lightness contrast is at least lc
minimum is a contrast ratio of WCAG, and lc is an absolute value of APCA.
The default lc of 0 asks only for the contrast ratio.
The function returns the color and the change of its lightness in OKLab, from
0 to 1. A color that already has the contrast comes back with the change 0.
The foreground becomes lighter on a dark background and darker on a light
background, in steps of 0.001. The function reduces the chroma only where a
color with the new lightness is outside sRGB. It returns :error when no
lightness gives the contrast.
iex> Expresso.Color.adjust("#ffffff", "#000000", 4.5)
{:ok, "#ffffff", 0.0}
Return the color of a foreground with an opacity from 0 to 1 on a background
Each channel is opacity * foreground + (1 - opacity) * background, on the
encoded values of sRGB. A browser composites an element with a filter in
this way.
iex> Expresso.Color.blend("#000000", "#ffffff", 0.5)
"#808080"
Return the contrast ratio of two colors, as WCAG 2.2 defines it
The ratio is (L1 + 0.05) / (L2 + 0.05), where L1 is the relative
luminance of the lighter color and L2 the luminance of the darker color.
The order of the arguments is not important.
iex> Expresso.Color.contrast("#000000", "#ffffff")
21.0
iex> Float.round(Expresso.Color.contrast("#777777", "#ffffff"), 2)
4.48
Return the lightness contrast Lc of a text on a background, as APCA defines it
The function uses the constants of APCA-W3 version 0.0.98G-4g. APCA gives a negative value for light text on a dark background. This function returns the absolute value, so a larger value is always more contrast. The order of the arguments is important: the first color is the text.
APCA raises each luminance to a different power for the text and for the
background, and the powers depend on which color is lighter. Thus the
same two colors give a different value when they change places, and a dark
background gives less contrast than the contrast ratio of WCAG says.
docs/architecture.md gives the formula.
iex> Float.round(Expresso.Color.lightness_contrast("#000000", "#ffffff"), 1)
106.0
iex> Float.round(Expresso.Color.lightness_contrast("#ffffff", "#000000"), 1)
107.9
iex> Float.round(Expresso.Color.lightness_contrast("#888888", "#ffffff"), 1)
63.1
Return the relative luminance of a color, from 0 for black to 1 for white