Liking cljdoc? Tell your friends :D

Why colour-coded works the way it does

These are the decisions that you couldn't guess from the API, and the reasons for them. Each namespace also cites its sources in its docstring, and in the code where it makes a decision.

Computing colours

The library computes the same colours that a browser computes. It has every colour space of CSS Color 4, its syntax, color-mix() and its gamut mapping, tested against Colour.js and more than 6,000 of the web-platform-tests.

It depends on Clojure alone. Only the image namespace calls the platform, to decode an image: ImageIO on the JVM, and the browser's decoder in ClojureScript.

A colour is a map of :space, :coords and :alpha, the shape that Colour.js uses, and every function takes CSS strings too.

The parser is in syntax, since in ClojureScript a namespace called css clashes with the function css of the root namespace. The names spell "colour", "quantise" and "serialise" in British English throughout, but CSS's own words, e.g. color-mix(), keep CSS's spelling.

mix/gradient interpolates in Oklab, like the rest of mix. A browser still draws a gradient of hex colours in sRGB, but a colour map doesn't record how it was written, so you pass :srgb to match one.

composite/over composites in sRGB by default, where mix/mix mixes in Oklab. CSS names Oklab for color-mix(), but no space for compositing. The W3C's ACT rule for the contrast of text composites translucent text in sRGB, to judge by the ratios of its examples, so that's the default.

WCAG only defines contrast for opaque colours, so contrast/ratio flattens a translucent one first: its second colour over a canvas, and then its first over the result. That makes the order of its arguments matter for a translucent colour. The canvas is white unless you pass one. WCAG assumes white where no background is specified, but it's the wrong guess on a dark page.

Bad input

A function returns nil for what it can't use rather than guessing or throwing, so a typo in a colour shows up as nil, not as black. A report counts it as a failure instead, since finding what's wrong is a report's job. The root namespace's docstring states this rule and the others that every namespace keeps to.

Themes

A theme takes its lightness from CIELAB's L*, which keeps contrast to within a few percent whatever the hue, since CIELAB adapts to the white of D50 where WCAG's luminance uses D65's. That's why a colour's contrast holds whichever colour or image you start from. A report checks every pair anyway.

tone lowers a colour's chroma in the token's own space, so that its lightness and hue stay exact, rather than with CSS's gamut mapping. tone/worst-contrast checks both that and the clipping of browsers.

A theme's accent isn't the seed itself, since a fixed lightness is what holds the contrast. theme/keep-seed is a separate step for a brand's colour, so that asking for it is a choice. It still wants the accent to contrast 3:1 with the page, which WCAG doesn't ask of a button that has a label, so a light colour such as yellow darkens in the light appearance.

A token of tone can keep the seed's own lightness within a range, e.g. for a bar that's darker under dark artwork. Its contrast then holds only as far as it does at both ends of the range, so tone/worst-contrast tries seeds from black to white for it.

choose/ranked ranks the colours of an image by the share of it near each one's hue and by chroma, where choose/seed returns the commonest that has a hue. Material ranks a wallpaper's colours the same way, but in HCT, so the weights here are judgements, tried on the artwork of about a thousand podcasts. image/seed and the themes still use choose/seed.

An image with no opaque pixels has no seed, so it has no theme. A made-up grey would hide that, so pass a colour of your own to theme/from-seed for that case.

APCA is left out, since its licence forbids changing it and using it commercially.

Colour vision

vision/simulate has no default deficiency, since there's no general case to assume. vision/least-difference checks them all.

vision clips a simulated colour to sRGB. Machado's matrices can give a saturated colour a negative cone response, which makes its ΔEOK meaningless. The tritan simulation is the paper's approximation, which doesn't cover tritanopia.

E-paper

An e-paper panel shows 16 greys, and nothing standardises how a device turns a colour into one of them. Pure red comes out as 76 of 255 in PDFium, 54 in ImageMagick and 129 in MuPDF. Engines then floor or round to 8 bits. After that, some devices round to the nearest of the 16 greys, and others keep the top 4 bits. So a colour can shift between shades from one device to the next, or dither into a stipple.

A colour has a shade when it lands on the same grey by every conversion and each of those ways of reducing it. By default epaper asks for that, at some cost in chroma, since e-readers don't document their conversions. You can pass the ones you know. They come from real PDF and image engines and from open source code. No panel has confirmed a shade yet.

epaper/palette has four colours because no more fit 3 shades apart between shades 3 and 12. The range stops there because a dark colour that turns black still shows, but a pale one nearly vanishes on white. The hues start from Paul Tol's high-contrast scheme, which he made for a monochrome printout. His own colours can each land on two shades, so only the hues carry over. Teal sits in the widest gap left, and does better than green with a red-green deficiency. The hues, the thresholds of epaper/report and the number of colours are judgements, untested on readers.

The numbers for Kaleido 3 in epaper/panels are estimates, since E Ink publishes none and I found no measurements. Gallery 3 is left out for the same reason.

Still to come

Material 3's HCT and dynamic schemes, ramps, data palettes and historic colour theory.

Can you improve this documentation?Edit on GitHub

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
←Move to previous article
→Move to next article
Ctrl+/Jump to the search field
× close