Color Picker
A colour picker that thinks in OKLCH, because this library does.
Walkthrough
1. 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> 2. 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> 3. 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> 4. 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> 5. 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>