@select-org/palette
v0.1.0
Published
Framework-agnostic OKLCH color utilities and community-palette derivation for the Select design system.
Readme
@select/palette
Framework-agnostic OKLCH color utilities and community-palette derivation for the Select design system.
This package is runtime — it converts hex inputs to OKLCH, adjusts contrast, derives semantic palettes from a community brand+background hex pair, and returns OKLCH css strings ready to apply as scoped CSS variable overrides.
Pure TypeScript. No React. No CSS. No Tailwind. The only runtime dependency is Culori.
Why this exists
Backend ships per-community theming as hex (brandHex, bgHex). The web client today applies these inline with hand-tweaked contrast adjustments. This package centralizes the derivation rules: every consumer that derives a community palette gets the same algorithm and the same opinions about contrast, surface tints, and border generation.
See ADR 0012 for the architectural rationale.
Install
Workspace-internal during Phase 1 — consumed via file: dependency:
{
"dependencies": {
"@select/palette": "file:../select-design-system/packages/palette"
}
}Usage
import {
hexToOklch,
oklchToCss,
ensureContrast,
derivePaletteFromCommunity,
} from "@select/palette";
// One-shot palette for a community surface.
const palette = derivePaletteFromCommunity({
brandHex: "#00d4dd",
bgHex: "#ffffff",
});
// Apply as scoped CSS variable overrides:
const style = {
"--color-surface": palette.surface,
"--color-action-primary": palette.actionPrimary,
"--color-text-primary": palette.textPrimary,
// ... 21 more semantic tokens
};API
Types
OklchColor—{ l, c, h, alpha? }with all components in 0–1 (or 0–360 for h).CommunityPalette— 24 semantic tokens as OKLCH css strings.
Conversion
hexToOklch(hex: string): OklchColor— accepts#rgbor#rrggbb. Throws on invalid input.oklchToHex(c: OklchColor): string— outputs#rrggbb. Gamut-clamps to sRGB.oklchToCss(c: OklchColor): string— outputsoklch(L C H)oroklch(L C H / alpha).
Transformation
lighten(c, amount)— increases L byamount, clamped at 1.darken(c, amount)— decreases L byamount, clamped at 0.mix(a, b, ratio)— interpolates L, C, alpha linearly; H along shortest path on the 0–360 circle.
Contrast
computeContrast(a, b): number— WCAG contrast ratio (1–21).ensureContrast(fg, bg, targetRatio = 4.5): OklchColor— adjusts fg lightness until contrast meets target. Defaults to WCAG AA for normal text.
Palette derivation
derivePaletteFromCommunity({ brandHex, bgHex }): CommunityPalette— the load-bearing function. Seesrc/derive.tsfor the exact rules; see Plan 0002 for rationale per slot.
Testing
pnpm -F @select/palette test