Colour for MoonBit: CSS syntax, colour spaces, gamut mapping, terminal colours and colour arithmetic.
let brand = @moonrgba.Color::decode("#0891b2")
let white = @moonrgba.Color::white()
assert_eq(brand.to_oklch().encode(), "oklch(0.60891 0.11094 221.72312)")
assert_eq(
brand.mix(white, space=Oklch).encode(),
"oklch(0.80446 0.05547 221.72312)",
)
assert_eq(brand.contrast(white), 3.6820433648230324)
assert_eq(brand.to_ansi256().code(), 31)let css = text => @moonrgba.Color::decode(text).encode()
assert_eq(css("#123"), "#123")
assert_eq(css("rgb(10 20 30 / 50%)"), "rgb(10 20 30 / 0.5)")
assert_eq(css("lab(50% 50% -20%)"), "lab(50 62.5 -25)")
assert_eq(css("color(display-p3 .5 .2 .1)"), "color(display-p3 0.5 0.2 0.1)")
assert_eq(css("currentcolor"), "currentcolor")let refused : @moonrgba.Refused = try {
ignore(@moonrgba.Color::decode("rgb(1 2)"))
abort("unreachable")
} catch {
error => error
}
assert_eq("\{refused}", "at 7: got ), want a number")| Syntax | Read | Write | Written by encode() |
|---|---|---|---|
| #rgb, #rgba, #rrggbb, #rrggbbaa | yes | yes | sRGB that is a whole byte per channel |
| rgb() rgba(), comma arguments | yes | — | the form belongs to an earlier grammar |
| rgb() rgba(), space arguments | yes | yes | sRGB that is not a whole byte per channel |
| hsl() hsla() | yes | yes | a colour written in HSL |
| hwb() | yes | yes | a colour written in HWB |
| lab(), lch() | yes | yes | a colour written in Lab or LCh |
| oklab(), oklch() | yes | yes | a colour written in Oklab or OkLCh |
| color(...) | yes | yes | a colour in one of the eight spaces it names |
| the 148 named colours | yes | with Format::Named | |
| transparent | yes | as #0000 | |
| currentcolor | yes | yes |
let hsl = @moonrgba.Color::hsl(210.0, 0.5, 0.4)
assert_eq(hsl.to_oklch().encode(), "oklch(0.49931 0.09866 250.43306)")
assert_eq(hsl.to_srgb().rgb8(), (51, 102, 153))| Space | Components | Reached through |
|---|---|---|
| Srgb | red, green, blue 0–1 | the hub for the HSL family |
| SrgbLinear | red, green, blue 0–1 | D65 XYZ |
| Hsl, Hsv, Hwb | hue in degrees, then two fractions | sRGB |
| DisplayP3, DisplayP3Linear, Rec2020, A98Rgb | red, green, blue 0–1 | D65 XYZ, own matrix and curve |
| ProPhoto | red, green, blue 0–1 | D50 XYZ, then Bradford to D65 |
| Lab, Lch | lightness 0–100, then a and b or chroma and hue | D50 XYZ |
| Oklab, Oklch | lightness 0–1, then a and b or chroma and hue | D65 XYZ through LMS |
| XyzD50, XyzD65 | X, Y, Z, Y = 1 for white | Bradford between the two |
| YCbCr | luma 0–1, chroma −0.5–0.5 | sRGB, with BT.601, BT.709 or BT.2020 coefficients |
| Current | — | the unresolved currentcolor keyword |
let wide = @moonrgba.Color::oklch(0.7, 0.4, 30.0)
assert_eq(wide.in_gamut(space=Srgb), false)
assert_eq(wide.to_srgb().encode(), "color(srgb 1.31965 -0.44982 -0.31009)")
assert_eq(wide.clip(space=Srgb).encode(), "#f00")
let fitted = wide.to_gamut(space=Srgb)
assert_eq(fitted.encode(), "rgb(255 88.00945 67.46665)")
assert_eq(fitted.to_oklch().encode(), "oklch(0.6831 0.2062 30.18163)")let red = @moonrgba.Color::rgb(255, 0, 0)
let blue = @moonrgba.Color::rgb(0, 0, 255)
assert_eq(red.mix(blue, space=Srgb).encode(), "rgb(127.5 0 127.5)")
assert_eq(
red.mix(blue, space=Oklab, weight=0.3).encode(),
"oklab(0.5048 0.04474 -0.18032)",
)
let rose = @moonrgba.Color::hsl(350.0, 1.0, 0.5)
let orange = @moonrgba.Color::hsl(10.0, 1.0, 0.5)
assert_eq(rose.mix(orange, space=Hsl, hue=Longer).hue(), 180.0)let ink = @moonrgba.Color::black()
let paper = @moonrgba.Color::white()
assert_eq(ink.contrast(paper), 21.0)
assert_eq(ink.apca(paper), 106.04067321268862)
assert_eq(paper.apca(ink), -107.88473318309848)| Measure | Space | Note |
|---|---|---|
| E76 | Lab | the straight-line distance |
| E94 | Lab | graphic-arts weights; the first colour is the reference |
| E2000 | Lab | the current CIE recommendation |
| Ok | Oklab | no weights needed; a just-noticeable difference is 0.02 |
let brand = @moonrgba.Color::decode("#0891b2")
assert_eq(brand.to_ansi16(), Cyan)
assert_eq(brand.to_ansi256().code(), 31)
assert_eq(brand.rgb8(), (8, 145, 178))
assert_eq(@moonrgba.Ansi256::of(196).color().encode(), "#f00")pub(all) struct Color {
space : Space
x : Double
y : Double
z : Double
alpha : Double
}assert_eq("\{@moonrgba.Color::decode("#0891b2")}", "#0891b2")| Call | What it does |
|---|---|
| Color::decode(text) | read a CSS colour; raises Refused |
| Color::decode_opt(text) | the same, None instead of raising |
| Color::encode(format?) | write CSS, in the shortest exact spelling |
| Color::of(space, x, y, z, alpha?) | a colour from components already in a space |
| Color::srgb, rgb, hsl, hwb, lab, lch, oklab, oklch, xyz_d65, xyz_d50, display_p3, rec2020, a98_rgb, prophoto, ycbcr | a colour in that space |
| Color::of_name, Color::name | the 148 names, both ways |
| Color::to_srgb, to_hsl, to_lab, to_oklch and the rest, to_space | the same colour in another space |
| Color::in_gamut, clip, to_gamut | decide what to do with a colour a display cannot show |
| Color::mix, lighten, darken, saturate, desaturate, fade | combining and nudging |
| Color::contrast, apca, luminance | how readable a pair is |
| Color::delta_e | how far apart two colours are |
| Color::to_ansi16, to_ansi256, rgb8 | into a terminal's palette |
| Color::to_cmyk, Cmyk::to_color | into print, and back |
| Color::components, hue, chroma, lightness, saturation, with_components, with_alpha, resolve | looking inside, and putting it back together |
| Space::code, Space::of, is_polar, is_predefined, has_gamut | what a space is |
| space | x | y | z |
|---|---|---|---|
| Srgb, SrgbLinear, DisplayP3, Rec2020, A98Rgb, ProPhoto | red | green | blue |
| Hsl | hue, degrees | saturation | lightness |
| Hsv | hue, degrees | saturation | value |
| Hwb | hue, degrees | whiteness | blackness |
| Lab | lightness, 0–100 | a | b |
| Lch | lightness, 0–100 | chroma | hue, degrees |
| Oklab | lightness, 0–1 | a | b |
| Oklch | lightness, 0–1 | chroma | hue, degrees |
| XyzD50, XyzD65 | X | Y | Z |
| YCbCr | luma | blue difference | red difference |
| Current | — | — | — |
Install
Download zipColour for MoonBit: CSS syntax, colour spaces, gamut mapping, terminal colours and colour arithmetic.