@sorrell/color
v2.0.1
Published
Functional types and utilities for working with colors.
Readme
@sorrell/color
Immutable, structural RGB color types for effect:
RgbColor (8-bit channels), RgbaColor (8-bit channels plus alpha), and
LinearColor (normalized [0, 1] decimal channels, also with alpha). Every
constructor clamps and truncates its input, so out-of-range or fractional
channel values never throw or wrap. Mutators follow Effect's data-first/
data-last Function.dual convention, and every color type parses and formats
the same set of CSS and ANSI representations. Contrast computes WCAG
relative luminance and contrast ratio, and can adjust a color's lightness to
guarantee a minimum contrast against a background.
RgbColor
import { RgbColor } from "@sorrell/color";
const Red = RgbColor.RgbColor(255, 0, 0);
const Clamped = RgbColor.RgbColor(-10, 300, 127.9); // { R: 0, G: 255, B: 127 }
const Lighter = RgbColor.Lighten(Red, 0.25);
const Darker = RgbColor.Darken(0.25)(Red);
RgbColor.Format.Hex(Red); // "#ff0000"
RgbColor.Format.Hsl(Red); // "hsl(0, 100%, 50%)"Channels accept number, bigint, or an Effect BigDecimal, and are clamped
to {0..255} and truncated toward zero. RgbColor.From parses Hex, Rgb,
Hsl, Hsv, Hwb, Keyword, Ansi16, Ansi256, Tuple, and Record
representations; the string parsers return an Effect Option, since the
input may not match. RgbColor.Format renders every one of those
representations in the other direction. SetRed, SetGreen, and SetBlue
update one channel; Assign (aliased Patch) updates several at once from a
Partial<RgbColor>.
RgbaColor
RgbaColor mirrors RgbColor exactly, with an additional 8-bit A channel:
import { RgbaColor, RgbColor } from "@sorrell/color";
const HalfRed = RgbaColor.RgbaColor(255, 0, 0, 128);
RgbaColor.SetAlpha(HalfRed, 64);
RgbaColor.To.RgbColor(HalfRed); // discards alpha
RgbaColor.From.RgbColor(RgbColor.RgbColor(0, 0, 0)); // alpha defaults to 255Every From string parser (Hex, Rgb, Hsl, …) produces a fully opaque
color, since none of those representations carry an alpha component in this
package. RgbaColor.From.LinearColor and RgbaColor.To.LinearColor convert
against LinearColor, scaling every channel, including alpha, between
{0..255} and [0, 1].
LinearColor
import { BigDecimal } from "effect";
import { LinearColor } from "@sorrell/color";
const Opaque = LinearColor.LinearColor(1, 0, 0.5); // A defaults to 1
const HalfTransparent = LinearColor.LinearColor(1, 0, 0.5, 0.5);
LinearColor.Format.Tuple(Opaque).map(BigDecimal.toNumberUnsafe); // [1, 0, 0.5, 1]Channels, including alpha, are Effect BigDecimal values clamped to
[0, 1]. Unlike RgbaColor, every LinearColor constructor's Alpha
argument is optional and defaults to 1 (fully opaque); From.Tuple and
From.Record extend the same default when a tuple or record omits A.
Lighten and Darken preserve the original alpha.
Contrast
import { Contrast, RgbColor } from "@sorrell/color";
const Background = RgbColor.RgbColor(255, 255, 255);
const Foreground = RgbColor.RgbColor(220, 220, 220);
Contrast.ContrastRatio(Foreground, Background); // ~1.3
const Accessible = Contrast.EnsureContrast(Foreground, Background, 4.5);
Contrast.ContrastRatio(Accessible, Background); // >= 4.5RelativeLuminance and ContrastRatio implement the WCAG 2 formulas.
EnsureContrast darkens or lightens Self along its HSL lightness —
whichever direction has more contrast headroom against Background — using
a bounded binary search, and returns Self unchanged when it already meets
MinimumRatio. Contrast operates on RgbColor; convert an RgbaColor or
LinearColor first when checking contrast on one of those.
Entrypoints
@sorrell/color—RgbColor,RgbaColor,LinearColor, andContrast, each re-exported as a namespace.@sorrell/color/RgbColor@sorrell/color/RgbaColor@sorrell/color/LinearColor@sorrell/color/Contrast
