moonrgbacolour for MoonBit

CSS Color 4 · CSS Color 5 · MoonBit

Turn the wheel.
Read the colour.

moonrgba is a colour library for MoonBit. It reads the colours CSS writes, converts between seventeen spaces, brings a colour into a gamut, mixes and measures it, and finds its nearest terminal slot. Every number on this board comes from the compiled library, running in this page.

210° #2e73b8
outside sRGB sRGB
hsl(210 60% 45%)
on white 4.95:1 APCA Lc 73.9
on black 4.25:1 APCA Lc −27.8
cyan 256 31 true 46, 115, 184
60%
45%
148named colours
17colour spaces
4colour differences
2contrast measures
01

One colour, every way

Paste another value in the field above, or drag the dial, and both columns follow. A cell that reads none is a component the colour does not have — a grey has no hue, and the library says so rather than inventing a number.

as CSS

hex#2e73b8
rgbrgb(45.9 114.75 183.6)
hslhsl(210 60% 45%)
hwbhwb(210 18% 28%)
lablab(46.61867 -3.51425 -43.38625)
lchlch(46.61867 43.52834 265.3692)
oklaboklab(0.54533 -0.04081 -0.1211)
oklchoklch(0.54533 0.12779 251.37887)
color(srgb)hsl(210 60% 45%)
color(display-p3)color(display-p3 0.25445 0.44408 0.6994)
color(rec2020)color(rec2020 0.37324 0.47107 0.71208)
color(a98-rgb)color(a98-rgb 0.29468 0.44754 0.70555)
color(prophoto-rgb)color(prophoto-rgb 0.3325 0.36771 0.62842)
color(xyz-d65)color(xyz-d65 0.15833 0.16226 0.47427)
color(xyz-d50)color(xyz-d50 0.14584 0.1573 0.35757)

components

srgb0.18 0.45 0.72
srgb-linear0.027212 0.170645 0.477
hsl210 60% 45%
hsv210 75% 72%
hwb210 18% 28%
lab46.618672 -3.514246 -43.386247
lch46.618672 43.528339 265.369204
oklab0.545329 -0.040806 -0.121104
oklch0.545329 0.127794 251.378874
xyz-d650.158331 0.162262 0.47427
xyz-d500.145839 0.157303 0.357571
display-p30.254452 0.444081 0.699402
rec20200.373241 0.471073 0.712083
a98-rgb0.294675 0.447536 0.705552
prophoto-rgb0.332501 0.367713 0.628422
cmyk75% 37.5% 0% 28%
ycbcr-7090.412092 0.165934 -0.147379
02

What it reads

The parser takes one value — what would sit on the right of color: — and reads it the way a browser would, including the argument list it belongs to.

#rgb · #rgba · #rrggbb · #rrggbbaa rgb() · rgba() hsl() · hsla() hwb() lab() · lch() oklab() · oklch() color() the 148 names transparent currentcolor

Two grammars, not one

The comma list and the space list are different grammars with different rules. The first separator decides which one is being read, and that one's rules apply to everything after it.

Seventeen spaces

sRGB and its linear twin, HSL, HSV, HWB, Lab, LCh, Oklab, OkLCh, Display P3, Rec. 2020, Adobe RGB, ProPhoto, both XYZ, CMYK and YCbCr — two hubs, no drift.

Into a gamut, by chroma

A colour a screen cannot show loses chroma in OkLCh until it fits. Clipping is the cheaper answer and the library keeps it, but it is not the default.

Mixing and measuring

color-mix with its four hue directions and premultiplied alpha, WCAG 2 and APCA contrast, and four colour differences that do not pretend to be each other.

A terminal's palette

The nearest named colour, the nearest slot of the 256, and the three bytes of true colour — chosen by OkLab distance, because the palette is uneven.

No I/O, four backends

Every function is a pure conversion. Nothing here reads a file, opens a socket or asks a display anything; the same code runs on all four targets.

03

In MoonBit

Seven calls, and the values they return. The trailing comments are the output of these exact lines, not an illustration.

let brand = @moonrgba.Color::decode("#0891b2")

brand.to_oklch().encode()                            // oklch(0.60891 0.11094 221.72312)
brand.contrast(@moonrgba.Color::white())             // 3.6820433648230324
brand.to_ansi16()                                    // cyan
brand.to_ansi256().code()                            // 31

@moonrgba.Color::decode("rebeccapurple").encode()    // #639
brand.mix(@moonrgba.Color::white(),
  space=@moonrgba.Space::Oklch).encode()             // oklch(0.80446 0.05547 221.72312)

Reading and writing are one pair. decode takes one value and raises Refused, which says where it stopped and what it wanted; decode_opt is the same reader with the refusal turned into None.

The writer takes the shortest spelling that reads back unchanged. That is why rebeccapurple comes out as #639: four characters, and the same colour. Where no short form is exact — a channel of 127.5 has no hexadecimal spelling — it writes the longer one instead of rounding the colour away.

A missing component stays missing. none is carried as a NaN, written back as none, and filled in from the other side when two colours are mixed.