/ component

Color Function

Edit the modern CSS color functions — color-mix(), light-dark(), and relative color — with compile-time validation that dispatches on the leading function name. color-mix(in srgb shorter hue, …) → never (hue method on a rectangular space); oklch(from #f00 r g b) → never (rgb channels in an oklch relative color); rgb(255 0 0) → never (a bare color is color-picker's job). The strict tier validates each family's grammar to never on violation.

/ basic-usage

Build a modern color function

Controlled value + onChange. Pick the interpolation colorspace, the two colors and their mix ratio — the editor emits a valid color-mix() string, and the trigger swatch shows the resolved color.

color-mix(in oklch, oklch(0.7 0.2 30) 60%, oklch(0.6 0.2 250))
/ live-preview

Each family, edited live with a result swatch

Three modes, three panels. Every control composes the typed string; the swatch beneath each renders the produced value as a computed background-color. The light-dark() swatch carries a scheme toggle so the browser resolves it both ways.

color-mix()
First50%
Second50%
color-mix(in oklch, oklch(0.75 0.18 35) 50%, oklch(0.6 0.2 255) 50%)
Preview
Preview
color-mix(in oklch, oklch(0.75 0.18 35) 50%, oklch(0.6 0.2 255) 50%)
relative color
from
oklch(from oklch(0.7 0.2 30) l c calc(h + 180))
Preview
Preview
oklch(from oklch(0.7 0.2 30) l c calc(h + 180))
light-dark()
Light color
Dark color
light-dark(#fef3c7, #1e293b)
Preview
Preview
light-dark(#fef3c7, #1e293b)
/ types

Three usage tiers

From useState-and-go to per-family grammar validation at compile time.

01 casual
string

Pass any string

useState<string>. No compile-time validation; the runtime parser handles whatever you type — including var() colors and bare keyword colors like white.

color-mix(in srgb, var(--brand), white 20%)
const [value, setValue] = useState<string>("color-mix(in srgb, var(--brand), white 20%)")
02 intellisense
ColorFunctionString

Suggestion-shaped hints

State typed as ColorFunctionString — the union of the three family shapes. It is also the onChange return type, and it narrows to one family via the mode prop. Precise validation lives in the strict tier.

light-dark(#ffffff, #1a1a1a)
const [value, setValue] = useState<ColorFunctionString>("light-dark(#ffffff, #1a1a1a)")
03 strict
ColorFunctionLiteral<S>

Per-family grammar at compile time

cssColorFn() dispatches on the function name and resolves a bad colorspace, a hue method on a rectangular space, or wrong relative-color channel keywords to never — a type error before you run the code.

color-mix(in oklch shorter hue, #ff0000, #0000ff)
cssColorFn("color-mix(in oklch shorter hue, #f00, #00f)") // ✓
// @ts-expect-error hue method on a rectangular space
cssColorFn("color-mix(in srgb shorter hue, …)")
// @ts-expect-error rgb channels in an oklch relative color
cssColorFn("oklch(from #f00 r g b)")

Note: calc() channel bodies and channel magnitudes are weak-validated — any balanced calc(…) passes. The runtime parser is equally tolerant.

/ api

API

Public surface — component props, runtime helpers, and the type exports.

§ ColorFunction / ColorFunctionPanel

<ColorFunction
  mode?: 'color-mix' | 'relative' | 'light-dark'
  value: ColorFunctionStringMap[mode] | (string & {})
  onChange: (next: ColorFunctionStringMap[mode]) => void
  className?: string
  aria-label?: string
/>

ColorFunction is popover-wrapped; ColorFunctionPanel renders the same editor inline. Both are controlled. With mode set the panel shows only that family and onChange narrows; omit it for a family selector that edits any of the three.

PropTypeDescription
mode'color-mix' | 'relative' | 'light-dark'Optional. Which family to edit. Narrows the onChange string type; when omitted the panel shows a family <select>.
valueColorFunctionStringMap[mode] | (string & {})Current CSS color-function value. Required.
onChange(next: ColorFunctionStringMap[mode]) => voidFires when the edited value changes. Emits the canonical string for the family.

§ Sub-components

<ColorMixEditor state onChange />

The color-mix editor: interpolation-space select, a hue-method select (cylindrical spaces only), two ColorPickers with mix-ratio sliders, and a swap-colors affordance. Emits a ColorMixState.

<RelativeColorEditor state onChange />

The relative-color editor: function select, a from-color ColorPicker, three channel inputs (with per-function keyword placeholders), an optional color() space ident, and an alpha input. Emits a RelativeColorState.

<LightDarkEditor state onChange />

Two ColorPickers — the light-scheme and dark-scheme colors. Emits a LightDarkState.

<ColorFunctionPreview value />

The result swatch — renders the produced string as a computed background-color, with a color-scheme toggle for light-dark().

§ Runtime helpers

cssColorFn<S extends string>(value: S & ColorFunctionLiteral<S>): S

Call-site validator. Mirrors cssBoxShadow() / cssFilter() / color().

parseColorFunction(src: string): ColorFunctionState | null

String → typed state, or null on a syntax / unknown-function / arity error. Tolerant of calc()/var() and bare keyword colors; lenient on channel magnitudes.

formatColorFunction(state: ColorFunctionState): string

Canonical re-serialization for each family (parse ∘ format is idempotent).

colorFunctionKind(src: string): 'color-mix' | 'relative' | 'light-dark' | null

Runtime mirror of KindOf — the family of a value, or null.

defaultState(mode): ColorFunctionState

Seed a fresh value per mode (each round-trips through format).

MIX_COLOR_SPACES / CYLINDRICAL_SPACES / HUE_METHODS / RELATIVE_FNS / CHANNEL_KEYWORDS

The dispatch tables that drive both the parser and the UI.

§ Types

ColorFunctionLiteral<S>
Strict validator — dispatches on the leading function name into ColorMixLiteral / LightDarkLiteral / RelativeColorLiteral; S on success, never otherwise.
ColorMixLiteral<S>
Full color-mix validation: in <space> with an optional <method> hue (cylindrical spaces), then two <color> <pct>?.
LightDarkLiteral<S>
Full light-dark validation: exactly two <color> arguments.
RelativeColorLiteral<S>
Relative-color validation with per-space channel keywords; lenient on calc() bodies and magnitudes.
ColorFunctionString / …StringMap / …Mode
Suggestion union, the mode→string map, and the mode key — the onChange return type.
KindOf / MixSpaceOf / RelativeFnOf / ColorsOf
Literal-level operators: the family, the mix colorspace, the relative function, and the raw color arguments.
ColorFunctionState
Discriminated-union state (color-mix | relative | light-dark), exported for advanced use.

§ Strict-tier scope

  • color-mix() and light-dark() are fully validated: the 14-member interpolation colorspace set, a <method> hue suffix only on the four cylindrical spaces (hsl / hwb / lch / oklch), exactly two colors with optional trailing <percentage> weights.
  • Relative color enforces per-space channel keywords (oklch → l c h, rgb → r g b, …) and the from keyword + source color + exactly three channels + optional / <alpha>.
  • Weak-validated (documented): calc() channel bodies are not parsed (any balanced calc(…) passes), channel magnitudes are not range-checked, and the color() colorspace ident is accepted leniently. var() is accepted wherever a <color> is expected.
  • A bare color literal (rgb(255 0 0) with no from) resolves to never — that is color-picker's domain, not this component's.
/ install

Drop it in

One command via the shadcn CLI.

$ pnpm dlx shadcn@latest add https://turtiesocks.github.io/ridiculous/r/color-function.json