Add oklab and/or oklch functions
It would be nice to have oklab and/or oklch functions available, so that (for example) lightening a blue color doesn't automatically turn it purple. In https://github.com/rktjmp/lush.nvim/issues/154#issuecomment-2481905872 @rktjmp mentioned not being able to find a good Oklab Lua library last year, but that appears to have changed in the past year. Here are several projects that I found, all of which are relatively recent and didn't exist 12 months ago (I haven't used any of them so I can't comment on their suitability as libraries):
- https://github.com/vgskye/oklua - brand new, 2 weeks old, just one commit, MIT license in files but no LICENSE.md file in repo
- https://github.com/loganswartz/polychrome.nvim - MIT license, not a library but https://github.com/loganswartz/polychrome.nvim/tree/main/lua/polychrome/color has oklab.lua and oklch.lua files which could be extracted?
- https://github.com/behreajj/AsepriteOkHsl - MIT license, also not a library but https://github.com/behreajj/AsepriteOkHsl/blob/main/ok_color.lua and https://github.com/behreajj/AsepriteOkHsl/blob/main/ok_hue_adj.lua could probably be extracted
None of these appear to be complete, just-use-this-library-and-you're-done solutions, but all three of them have enough pure-Lua functions in them that you might be able to just extract and use some of their code and get 80-90% of the way there.
An implementation of this should include the color operations, or similar to, as defined in https://github.com/rktjmp/lush.nvim/blob/main/lua/lush/vivid/hsl_like.lua,
- rotate
- saturate (+ desaturate, impl -saturate)
- lighten (+ darken, impl -lighten)
- mix
- to_rgb
When using oklch we have,
-
lfor lightness, actually measuring perceived lightness/"brightness", which is explicitly called out as "not being the same ashsl's lightness on the mdn docs, but I don't think that matters for practical use as long as 0% is black and 100% is white. -
cfor chroma, which would reasonably map to saturation amount, 0 is gray and 0.4+ is near enough to "saturated". -
h for hue, which maps conceptually 1:1 to the existing "rotate" in HSL.
oklab has less obvious mapping, and potentially might be simpler to just
convert oklab -> oklch -> oklab to perform any operations, or just use
oklch internally always.
lhas the same lightness definitionais a mix point between green and redbis a mix point between yellow and blue
It's rotate could probably be figured by some vectors for a b + theta and
saturation is I guess shifting both a, b values towards 0.
Other questions
- Is it worth supporting
oklab? To meoklchis more intuitively understandable with a lightness, chroma and hue value vs a and b mix points. - Probably it would also stand to have at least an explicit absolute
chromasetter, like thehuesetter for HSL. - Probably values should only be given as 0-100% (+ 0-360deg) to avoid the
awkward
-0.4 -> 0.4a,b,cvalues unless its super common express colors in that format.- This implies we cap 100% at (-)0.4 when the colorspaces actually support greater values, but in practice I dont think this matters.
https://bottosson.github.io/posts/gamutclipping/, https://bottosson.github.io/posts/colorpicker/
Yeah, oklch has obvious meanings for all three components (luminance, chroma, hue) whereas the A and B in oklab are pretty arbitrary. You will often want to make a color brighter or darker (change L), make it more saturated or move it towards gray (change chroma), or say "same brightness and saturation but rotate the hue by 15° to get a reddish-orange instead of red". So it's not worth implementing oklab, at least until/unless someone actually needs it. Operations in the oklch space are definitely more obvious.
I agree with expressing the numbers as 0-100%, probably as floats because you might want to, say, divide by 3 and you'd want 33.333(etc)% rather than rounding to 33%. That would mean it would be difficult to express absolute values in the ±0.4 range, but the documentation could say "If you are trying to copy a color that someone else has written in the -0.4 to 0.4 format, write it as (value / 0.4 * 100.0)" if someone really wants to do that.
But most people are probably going to want to express L and c as 0-100% values.