Skip to content
Devix Open Source
UI component v1.0.0 MIT Alpha

Color Picker

HEX, RGB and HSL with swatches, alpha and the EyeDropper API.

npm install @devix-labs/color-picker
Vanilla JS React Vue Svelte
Pick a colour Open

Hex, RGB, HSL, OKLCH and named colours in one field. A saturation square built as two real range inputs, one per axis, because one role="slider" cannot honestly describe two. WCAG contrast against a background you name, composited if the colour is translucent. A warning when OKLCH names a colour sRGB cannot show, instead of clipping it quietly. Eyedropper, swatches, alpha, light and dark. 6.2 kB gzip, zero dependencies, and the same behaviour in React, Vue, Svelte or a plain page.

What you get

OKLCH, properly

Ottosson's own matrices, checked against the figures he published for the sRGB primaries. The colour space Tailwind v4 and modern CSS are written in, which no popular picker supports.

It says when a colour is unreachable

oklch(70% 0.37 150) is a green no sRGB screen can produce. Every other picker clips it silently; this one shows the nearest it can and says so.

The contrast answer, in the picker

4.6 to 1 against the background you name, AA or not, with translucent colours composited first. The check that otherwise happens by hand, in another tab, too late.

A square with two honest axes

A group holding one real range input per axis, each with its own value and both read out together. Either one takes every arrow key, so it never matters which has focus.

Any notation people paste

#39f, 3a86ff, #3a86ff80, rgb(), hsl(), oklch() and the CSS named colours — typed or pasted, applied on Enter without submitting the form around it.

The hue survives the corners

Drag through white or black and back and the hue that went in comes out. The market leader has had that bug open for years.

Drop it on an input

Point it at an existing <input> and a plain form posts the colour under its own name, with input and change events for Livewire, Alpine and anything else already listening.

Yours to style

Every visual decision is a custom property, classNames on every part, theme: 'auto' that follows the page, an @layer devix stylesheet, RTL and forced colours.

Color Picker — overview

Why it exists

Six million downloads a week go to colour pickers that cannot say oklch. The most used one is React-only and has "add a hex input" and "support oklch" sitting open with votes on them; the next most used was last published in 2020 and has 229 open issues. The most popular vanilla one has had an issue titled "Accessibility" open since 2018 and tells you in its own README not to use it.

Meanwhile CSS moved. Tailwind v4 writes its palette in OKLCH, every browser supports it, and the whole point of it — lightness that matches what the eye sees — is exactly what a picker is for.

What it does differently

  • OKLCH as a first-class notation, converted with Ottosson's own matrices and checked against the figures he published, with a gamut warning when a colour is one sRGB cannot show — instead of clipping it quietly like everything else does.
  • The contrast answer, in the picker. 4.6 : 1 against the background you name, AA or not, composited first if the colour is translucent. That check otherwise happens by hand, in another tab, after the colour has been chosen.
  • A square that is two honest sliders. One role="slider" cannot describe two axes — the open complaint against the market leader. This is a group with one real range input per axis, and either one takes every arrow key.
  • The hue survives the corners. Drag into white or black and back and the hue that went in comes out. The leader loses it, and has for years.
  • One core, four wrappers, a web component and a CDN build, so React, Vue, Svelte and a plain Blade page behave identically.
  • 6.2 kB gzip, zero dependencies.

Not in 1.0

Wide-gamut output (display-p3, Rec. 2020): the maths is here but no screen we can test on could show us the difference, and a picker that claims a gamut it cannot verify is worse than one that doesn't. Palette and gradient editors are a separate product, not a bigger picker.

How it compares

Questions

Why OKLCH and not HSL?

In HSL, hsl(60 100% 50%) and hsl(240 100% 50%) claim the same lightness — one is yellow, one is navy. OKLCH's lightness matches what the eye sees, so a palette built by moving only the lightness looks like one colour in ten shades. That is why CSS added it and why Tailwind v4 writes its palette in it.

Does it work without React?

It is a DOM widget first. React, Vue, Svelte and <dx-color> are thin wrappers over the same core, and there is a CDN build for a plain page.

Can a screen reader use it?

The square is a labelled group holding one range input per axis, each announcing both numbers; hue and opacity are real range inputs; every button has a name; and the widget has a live region for copying and for the gamut warning.

How big is it?

6.2 kB gzipped for the whole widget, including the colour science, with no dependencies. The conversions and the contrast maths can also be imported on their own for a token pipeline.

Will it submit my form when I press Enter in the field?

No. Enter applies the colour and nothing else — a small thing that pickers mounted inside forms get wrong.