Skip to content

Epic: OKLCH color picker primitives for @rafters/ui #779

@ssilvius

Description

@ssilvius

Goal

Build 4 composable vanilla-TS primitives + 1 inline gamut helper in packages/ui/src/primitives/ that together form a full OKLCH color picker. Each primitive follows the Rafters pattern (element in, cleanup out, SSR-safe, zero deps).

Architecture

See .github/color-picker-spec.md for the complete interface specification.

Primitives

Primitive File Responsibility
oklch-gamut oklch-gamut.ts Inline sRGB/P3 gamut classification (~30 lines of matrix math)
interactive interactive.ts Pointer tracking surface (mouse/touch/keyboard -> normalized 0-1 coords)
color-area color-area.ts 2D canvas: Lightness x Chroma at fixed Hue, black for out-of-gamut
hue-bar hue-bar.ts 1D canvas: 0-360 hue spectrum at given L/C
color-input color-input.ts Filtered numeric L/C/H input fields

Shared Types (types.ts additions)

  • NormalizedPoint - {left, top} both 0-1
  • MoveDelta - keyboard movement delta
  • InteractiveMode - '1d-horizontal' | '1d-vertical' | '2d'
  • OklchColor / OklchColorAlpha - color triplets

Key Design Decisions

  1. Inline gamut math -- oklch-gamut.ts provides inSrgb(l,c,h) and inP3(l,c,h) via matrix multiplication (~30 lines). No external deps.
  2. Canvas-based rendering for color-area and hue-bar (CSS gradients cannot represent OKLCH's irregular gamut boundary).
  3. interactive is color-agnostic -- same primitive tracks pointer on the 2D area and the 1D hue strip.
  4. color-swatch reused for thumb/pointer -- no separate color-pointer primitive. Caller adds positioning CSS.
  5. All primitives are copyable via rafters add -- only import from sibling primitives, never workspace packages.
  6. No Zod -- plain TS types (dep-free constraint).
  7. Gamut feedback is consumer concern -- primitives expose inSrgb/inP3 booleans, consumer shows sRGB/P3 icons with checkmark or !.

Composition

ColorPicker (consumer component)
  +-- ColorArea (2D canvas, L x C at current H)
  |     +-- Interactive (pointer tracking, 2D mode)
  |     +-- ColorSwatch (thumb at current L,C -- already built)
  |
  +-- HueBar (1D canvas, 0-360)
  |     +-- Interactive (pointer tracking, 1D mode)
  |     +-- ColorSwatch (thumb at current H -- already built)
  |
  +-- ColorInput (L, C, H filtered fields)
  +-- ColorSwatch (preview -- already built)

Sub-Issues

Superseded Issues

Issues #780-785 were closed -- they used an outdated architecture (external gamut boundaries, separate color-pointer, workspace deps).

Prerequisite

Success Criteria

This issue is complete when: All sub-issues are closed and the primitives compose into a working color picker via #798.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions