
Sometimes you get inspired by a presentation or an article you’ve read. For me, an inspiration started at CSS Day last year during the opening keynote by Mathias Ott. One of those side projects I kept placing at the bottom of the pile, until I finally had had enough of it. I had to make it. In this article, I’d like to present a little tool, an OKLCH color picker. A tool created by me using CSS, a bit of React and a bit of AI help.
If you are wondering about the presentation by Mathias Ott that inspired this, take a look here.
Naming my picker
I don’t have anything against Oklahoma, and I do hope naming this tool didn’t offend anyone. But if you are wondering for the rest of this article, how do I pronounce this thing - well, it’s “Oak-ul-kroh-muh.”. Naming things is hard…
Here you can see the Oklchroma tool in action
I won’t be going into code details on how I created this tool, mostly because I am in no way React-savy enough to be an educator on the matter. But if you are wondering, yes, the sliders, controls, everything except for the color magic itself, is React / Typescript. The repository is public if you want to see some bloated non-best-practice code.
The idea for an OKLCH pattern generator
The idea of this color picker is to have one color input that is then thrown into some calculations to create a color pattern. I believe a handpicked color pattern will always be far more superior to a mathematically generated one. It’s pretty neat that we can do this with CSS nowadays. The example shown in the presentation came in two-fold. First, Mathias stated that generating a pattern could be handled like this with CSS:
:root {
--primary: oklch(56.6% 0.27 274);
--primary-10: oklch(from var(--primary) 10% c h);
--primary-20: oklch(from var(--primary) 20% c h);
--primary-30: oklch(from var(--primary) 30% c h);
--primary-40: oklch(from var(--primary) 40% c h);
--primary-50: oklch(from var(--primary) 50% c h);
--primary-60: oklch(from var(--primary) 60% c h);
--primary-70: oklch(from var(--primary) 70% c h);
--primary-80: oklch(from var(--primary) 80% c h);
--primary-90: oklch(from var(--primary) 90% c h);
--primary-100: oklch(from var(--primary) 100% c h);
}
Basic explanation of this syntax: The oklch()
color function starts with an input color, which is specified right after the “from” keyword. Based on that input color, we can create a new output by adjusting the Lightness, Chroma, and Hue. In this first example, the lightness of the color changed.

But that was not the mind-blowing part. The colors generated in this first example do look a little bit flat. Usually, what we see in color patterns is that we can see some sort of curve, where, for example, the dark colors are really dark and the light ones are almost pale, while the middle is very vibrant. What happened next was really smart. By using a base value and the trigonometric sine() function, Matthias showed some sort of easing curve that would adjust the Chroma of the color.
:root {
--primary: oklch(56.6% 0.27 270);
--c-base: 0.05;
--primary-10: oklch(
from var(--primary) 10% calc(var(--c-base) + (sin(1 * pi) * c)) h
);
--primary-20: oklch(
from var(--primary) 20% calc(var(--c-base) + (sin(0.9 * pi) * c)) h
);
--primary-30: oklch(
from var(--primary) 30% calc(var(--c-base) + (sin(0.8 * pi) * c)) h
);
--primary-40: oklch(
from var(--primary) 40% calc(var(--c-base) + (sin(0.7 * pi) * c)) h
);
/* ... */
}

Note: What is Chroma?
In short, the Chroma is not a percentage, it is a value between 0
and 0.37
. This value represents the vividness of the color.
I thought this was a great idea, and I quickly started wondering how this would work with other color inputs, or how about absolutely mental base modifiers that extend the Chroma way beyond what’s normally possible? Because theoretically, the vividness of a color goes to infinity. I played around with it a few times, but never really created a tool for it at that point. But finally, after a long time, I started planning:
Must-haves
I wanted to re-create the output from the presentation, but also with a twitst. The primary objective was for people to easily copy the output.
My must-haves for this picker were the following:
- Multiple color syntaxes as input
- A way to easily copy the output CSS
- Since I want an easy output, a way to rename the variables easily would be nice
Want-to-haves
This already seemed a cool thing, but I wondered: Can I actually turn this into a tool that I would want to use when creating a quick demo? Maybe even better, a tool that someone else wants to use from time to time. So then I thought, wouldn’t it be cool to create a system where people could share the pattern they created, or how about creating a full-fledged system.
- Make people add multiple patterns
- Make it so that url changes and there is a share link for it.
- Make color inputs more fancy by color updating range sliders (Check the sRGB one as reference)
- Light and dark theme!
They weren’t in my first prototype, but as I’m very late on doing a write-up about this, I can say they have been added. To be completely fair, this required me to go beyond my usual React skills and had a bit of help from a friend (an AI friend).
This is the part where it all became a bit “icky” for me. My skills were too few, and to be fair, I have a whole bunch of other things that I really want to learn before polishing this. The code became a bit more out of my control. So I decided to leave it there before going more into that rabbit hole.
While creating this, I added another feature that could be done with CSS in the future, changing the color of text of tab based on the picked color, while at the same time have a good color contrast, there are a few hacks for this already, but I kinda want to wait until we have the real thing, which brings me to the next thing…
Future additions
There are a few additions that I want to create. For example, I love that we can crank-up the Chroma beyond 0.37, but I should at least give a little warning in those cases that results can vary.
- Add a warning when Chroma exceeds
0.37
based on the base-modifier - Add more CSS features to replace the JS I’ve written (such as the contrast checker)
- Maybe do something with CSS functions and Mixins when that comes along?
- Clean it up a bit
- Maybe….just maybe give it a custom domain?
That’s pretty much it. I wonder what you think of it? It’s not often that I create little tools like this, but with my CSS knowledge, basic React skills, and a little bit of help from an AI friend, I was able to do this quite easily. It felt like the perfect combination regarding the amount of AI that went into it, just enhancing my capabilities as a developer instead of just basic “vibe coding”. But let’s not go into that debate right now.
Happy color picking!