oklch-pickerPlaygroundGitHub

An OKLCH colour picker that takes the gamut seriously

For React, Preact, Vue, Svelte, Solid, Angular, Qwik, and for no framework at all. Zero runtime dependencies. The component is ~6.5 kB gzipped, and the colour maths alone is ~2.5 kB.

Live. Drag the plot, or any slider.

Every axis is a slider over a gamut cross-section: the filled silhouette is the range that can actually be shown, so the reachable colours are visible instead of something you discover by dragging into a region that does nothing.

Why

Most pickers work in HSV and convert. If your design tokens are already OKLCH, that round-trip is lossy and the controls do not map onto what you store. This one works in OKLCH directly:

Push chroma up on the picker above and most hues cannot sustain it. The hatching and the notice both say so.

Install

npm install @oklch-picker/react
npm install @oklch-picker/vue
npm install @oklch-picker/svelte
npm install @oklch-picker/solid
npm install @oklch-picker/angular
npm install @oklch-picker/qwik
npm install oklch-picker
npm install oklch-picker
import { useState } from "react";
import { ColourPicker } from "@oklch-picker/react";
import "@oklch-picker/core/styles.css";

export function Example() {
  const [colour, setColour] = useState("oklch(0.7 0.15 255)");
  return <ColourPicker value={colour} onChange={setColour} />;
}
<script setup>
import { ref } from "vue";
import { ColourPicker } from "@oklch-picker/vue";
import "@oklch-picker/core/styles.css";

const colour = ref("oklch(0.7 0.15 255)");
</script>

<template>
  <ColourPicker v-model="colour" />
</template>
<script>
  import { ColourPicker } from "@oklch-picker/svelte";
  import "@oklch-picker/core/styles.css";

  let colour = $state("oklch(0.7 0.15 255)");
</script>

<ColourPicker bind:value={colour} />
import { createSignal } from "solid-js";
import { ColourPicker } from "@oklch-picker/solid";
import "@oklch-picker/core/styles.css";

export function Example() {
  const [colour, setColour] = createSignal("oklch(0.7 0.15 255)");
  return <ColourPicker value={colour()} onChange={setColour} />;
}
import { Component, signal } from "@angular/core";
import { ColourPickerComponent } from "@oklch-picker/angular";
import "@oklch-picker/core/styles.css";

@Component({
  selector: "app-example",
  standalone: true,
  imports: [ColourPickerComponent],
  template: `
    <oklch-colour-picker [value]="colour()" (valueChange)="colour.set($event)" />
  `,
})
export class Example {
  readonly colour = signal("oklch(0.7 0.15 255)");
}
import { $, component$, useSignal } from "@builder.io/qwik";
import { ColourPicker } from "@oklch-picker/qwik";
import "@oklch-picker/core/styles.css";

export const Example = component$(() => {
  const colour = useSignal("oklch(0.7 0.15 255)");
  return (
    <ColourPicker
      value={colour.value}
      onChange$={$((c: string) => (colour.value = c))}
    />
  );
});
---
import "@oklch-picker/core/styles.css";
---

<oklch-picker value="oklch(0.7 0.15 255)"></oklch-picker>

<script>
  import "oklch-picker/register";
</script>
<link rel="stylesheet" href="https://esm.sh/@oklch-picker/core/styles.min.css" />

<oklch-picker value="oklch(0.7 0.15 255)"></oklch-picker>

<script type="module">
  import "https://esm.sh/oklch-picker/register";
</script>

Where next

Browser support

Preset swatches need oklch() in CSS. That means Chrome/Edge 111+, Safari 15.4+, Firefox 113+. The charts, tracks and previews are computed to hex, so they render anywhere.