Inputs
Color Picker
A colour picker that thinks in OKLCH, because this library does.
import { ColorPicker } from 'omaris' Examples
Basic
The plane is lightness up and chroma across, each row scaled to the most colour that lightness can hold — so it is full of colour and everything in it is a colour sRGB can show. plane="absolute" scales every row alike instead, which draws the gamut's real silhouette against transparency. value is plain CSS text in format.
<script lang="ts"> import { ColorPicker, Text } from 'omaris'; let value = $state('oklch(0.6 0.18 260)');</script><div class="flex w-full flex-col items-center gap-3"> <ColorPicker bind:value /> <Text variant="label-md" tone="muted" font="mono">{value}</Text></div> Format and swatches
format decides how value is written — parsing always accepts hex, rgb(), oklch() and oklab() whatever it says. alpha carries opacity in the value, and swatches puts presets under the picker.
<script lang="ts"> import { ColorPicker, SegmentedButton, Text } from 'omaris'; const BRAND = ['#1c6fd6', '#0f9d58', '#e0a800', '#c0392b', '#6c3fd1', '#111827']; let format = $state<'hex' | 'rgb' | 'oklch'>('hex'); let value = $state('#1c6fd6');</script><div class="flex w-full flex-col items-center gap-3"> <SegmentedButton label="Format" size="sm" value={format} onchange={(next) => (format = next as typeof format)} items={[ { value: 'hex', label: 'hex' }, { value: 'rgb', label: 'rgb' }, { value: 'oklch', label: 'oklch' } ]} /> <ColorPicker bind:value {format} alpha swatches={BRAND} /> <Text variant="label-md" tone="muted" font="mono">{value}</Text></div> Compact and sizes
compact trims the picker to the plane, the sliders and the swatches — no text field, no number boxes — for one that has to sit in a settings panel. channels={false} hides the L / C / H boxes on its own, and classes reaches every part — here the root and the swatches.
<script lang="ts"> import { ColorPicker, Text } from 'omaris'; let accent = $state('oklch(0.7 0.16 150)'); let small = $state('oklch(0.55 0.22 25)');</script><div class="flex w-full flex-wrap items-start justify-center gap-8"> <div class="flex flex-col gap-2"> <Text variant="label-sm" tone="muted">compact</Text> <ColorPicker bind:value={accent} compact channels={false} eyedropper={false} swatches={['oklch(0.7 0.16 150)', 'oklch(0.6 0.18 260)', 'oklch(0.75 0.16 70)']} classes={{ root: 'rounded-shape-lg bg-surface-container-low p-3', swatch: 'size-7 rounded-shape-sm' }} class="w-64 max-w-full" /> </div> <div class="flex flex-col gap-2"> <Text variant="label-sm" tone="muted">size="sm"</Text> <ColorPicker bind:value={small} size="sm" label="Accent colour" /> </div></div> Plane scale
plane scales the x-axis. relative (the default) gives each row the most chroma that lightness can hold, so the box is full of colour and every point in it is pickable. absolute gives every row the same chroma and leaves the rest transparent — the gamut's true silhouette, and mostly checkerboard you cannot drag to.
<script lang="ts"> import { ColorPicker, Text } from 'omaris'; let value = $state('oklch(0.6 0.18 260)');</script><div class="flex w-full flex-wrap items-start justify-center gap-8"> <div class="flex flex-col gap-2"> <Text variant="label-sm" tone="muted">plane="relative"</Text> <ColorPicker bind:value plane="relative" compact size="sm" /> </div> <div class="flex flex-col gap-2"> <Text variant="label-sm" tone="muted">plane="absolute"</Text> <ColorPicker bind:value plane="absolute" compact size="sm" /> </div></div> Eyedropper
The eyedropper samples a pixel from anywhere on the screen — another tab, another window, the desktop — not just from this page. It is the same capability the picker's own button uses, exported as sampleScreenColor() so it can sit beside a plain field instead. canSampleScreen() says whether to draw the control at all; Firefox and Safari have no sampler.
Outside sRGB — shown at chroma 0.185.
<script lang="ts"> import { onMount } from 'svelte'; import { Button, ColorPicker, Text, canSampleScreen, sampleScreenColor } from 'omaris'; let value = $state('oklch(0.72 0.19 45)'); // Read once the page is mounted: the sampler is a browser capability, so // on the server the answer is always no and would never be revisited. let supported = $state(false); onMount(() => { supported = canSampleScreen(); }); async function sample() { const hex = await sampleScreenColor(); if (hex) value = hex; }</script><div class="flex w-full flex-col items-center gap-4"> <ColorPicker bind:value compact /> <div class="flex items-center gap-2"> <span class="size-8 shrink-0 rounded-full border border-border" style:background-color={value} ></span> <Button variant="tonal" size="sm" disabled={!supported} onclick={sample}> Pick from screen </Button> </div> <Text variant="label-md" tone="muted" font="mono"> {supported ? value : 'no screen sampler in this browser'} </Text></div> When to use it
Use it for
- A colour that becomes a token: a brand accent, a project colour, a chart series.
format="oklch"writes whatapp.cssreads. - A tint with opacity, like an overlay or a highlight. Pass
alpha; the colour is drawn over a checkerboard. - A picker inside a settings panel.
compactkeeps the plane, the sliders and the swatches and drops the text and number fields. - A legacy field that needs
#rrggbb. Useformat="hex". Parsing accepts hex,rgb(),oklch()andoklab()whatever the format. - Matching a colour on screen but outside the app, like a logo or a screenshot. The eyedropper samples any pixel the browser can see;
sampleScreenColor()puts that on a button of your own.
Not for
- A choice between a few named colours, like six tag colours → a row of Chips or a Radio group drawn as swatches.
swatcheshere still opens the whole plane. - Switching the app's palette, contrast and radius → Theme Settings.
- An emoji or icon for a channel → Emoji Picker.
- A gradient or a palette of several stops. It picks one colour.
Do
- Leave
plane="relative".absolutedraws the gamut's true shape, most of which cannot be picked. It is for teaching, not choosing. - Pass
swatcheswith the brand's colours, so the common pick is one tap. - Let an out-of-gamut value stay as it is. A saved
oklch()past sRGB shows a hint and pins the puck to the edge instead of clipping silently. - Set
channels={false}where the text field is enough. The L / C / H boxes are for someone typing a colour. - Use
size="md"or larger where a finger does the picking. Everything grows on a coarse pointer;size="sm"is a mouse size.
Don't
- Store
hexwhen the source of truth is anoklchtoken. The round trip to sRGB lowers chroma. - Put it in a popover with no width. The plane takes the container's width.
- Hide
eyedropperbecause some browsers lack it. It hides itself where there is nothing to sample with. - Call
sampleScreenColor()from anything but a click. Browsers only open the sampler inside a user gesture; a call from an effect resolvesnull.
Quick reference
size smmd(default)lg
API
ColorPicker
A colour picker that thinks in OKLCH, because this library does.
Lightness runs up the y-axis and chroma across the x-axis. How far across is plane:
- relative (the default) scales the row to the most colour that lightness can hold, so the rectangle is full of colour, the right edge is the most saturated this hue gets here, and every point you can drag to is a colour sRGB can show. Moving lightness or hue takes the chroma with it, which is why the puck never falls out of the gamut on its own. - absolute scales every row to the same chroma and leaves the rest transparent, drawing the gamut's real silhouette — the lopsided leaf that grows and shrinks as you turn the hue. True to the geometry, and mostly empty: at any one lightness sRGB holds a fraction of the widest chroma it holds anywhere, so most of the box is checkerboard you cannot pick from.
Either way nothing is clipped behind your back: a colour outside sRGB — typed in, or bound from elsewhere — says so, and asking for hex maps it back by lowering chroma only, so your hue and lightness survive.
value is a plain CSS string in whatever format you asked for, so it drops straight into a token, a form field or a style attribute.
import { ColorPicker } from 'omaris' <ColorPicker bind:value format="oklch" swatches={brand} /> Parts
Every element this renders is reachable from outside. Pass Tailwind for one part as classes={{ part: '…' }}; class covers the root and is merged last, so it always wins.
root- No description in the source yet.
area- The lightness × chroma plane.
canvas- No description in the source yet.
areaThumb- The puck on the plane.
controls- Preview + sliders.
preview- The current colour, over a checkerboard.
previewFill- No description in the source yet.
sliders- No description in the source yet.
slider- One slider row.
alphaTrack- The alpha track's checkerboard sits under its gradient.
track- No description in the source yet.
thumb- No description in the source yet.
fields- Numeric and text inputs.
field- No description in the source yet.
label- No description in the source yet.
input- No description in the source yet.
eyedropper- The "sample a pixel" button, where the browser has one.
swatches- No description in the source yet.
swatch- No description in the source yet.
hint- Says when the colour has been mapped back into sRGB.
Props
value bindableDefaults to 'oklch(0.6 0.18 260)'
string The colour, as CSS text in format. Bindable.
format Defaults to 'oklch'
ColorFormat How value is written. Parsing accepts hex, rgb(), oklch() and oklab() whatever this says.
size Defaults to 'md'
ColorPickerSize smmdlg
alpha Defaults to false
boolean Show the alpha slider and carry alpha in value.
swatches string[] Preset colours under the picker. Any CSS colour string.
plane Defaults to 'relative'
PlaneScale How the plane's x-axis is scaled — relative fills it with the colour this lightness can hold, absolute draws the gamut's own silhouette against transparency.
channels Defaults to true
boolean Show the L / C / H number boxes beside the text field.
eyedropper Defaults to true
boolean Offer the browser's screen sampler, where there is one.
compact Defaults to false
boolean disabled Defaults to false
boolean label Defaults to 'Colour'
string Accessible name for the plane.
onchange (value: string, color: Oklch) => void class string classes ColorPickerClasses