OKLCH Utils
weekendsuperhero-io-sistine/oklch-utilsUnverifiedUtility
Zero-dependency helpers for parsing, formatting, and manipulating OKLCH colors (hue wrapping, chroma clamping, ramps).
Install it
npx shadcn@latest add https://raw.githubusercontent.com/Weekendsuperhero-io/sistine/main/oklch-utils.jsonSource
1/**2 * OKLCH color primitives — parse / format an oklch string and generate symmetric ramps3 * (hue or chroma) around an existing color. Dependency-free; shares the {l, c, h} shape with4 * `GradientColor` in gradient-utils.ts.5 */67export interface OklchColor {8 /** Lightness, 0–100 (%) */9 l: number;10 /** Chroma, 0–~0.37 */11 c: number;12 /** Hue, 0–360 (degrees) */13 h: number;14 /** Optional alpha, 0–1 */15 alpha?: number;16}1718/**19 * Practical oklch chroma ceiling. Both sRGB and Display-P3 colors stay below ~0.37 (per Evil20 * Martians; confirmed by sweeping every L×hue with the gamut math below — sRGB max ≈ 0.321,21 * P3 max ≈ 0.363). Used as the binary-search bound and the chroma-ramp default range.22 */23export const MAX_CHROMA = 0.37;2425/** Wrap a hue into [0, 360). */26export function wrapHue(h: number): number {27 return ((h % 360) + 360) % 360;28}2930/** Clamp chroma into [0, MAX_CHROMA]. */31export function clampChroma(c: number): number {32 return Math.max(0, Math.min(MAX_CHROMA, c));33}3435/** A bare lightness ≤ 1 is the 0–1 form (×100); otherwise it's already 0–100. */36function normalizeLightness(l: number): number {37 return l <= 1 ? l * 100 : l;38}3940/**41 * Parse an `oklch()` string into components. Handles both lightness conventions used in this42 * repo — `oklch(72% 0.05 255)` and `oklch(0.72 0.05 255)` — plus an optional `/ alpha`.43 * Returns null if the string isn't a parseable oklch().44 */45export function parseOklch(input: string): OklchColor | null {46 const match = /^\s*oklch\(([^)]+)\)\s*$/i.exec(input);47 if (!match) return null;4849 const [coords, alphaPart] = match[1].split("/");50 const parts = coords.trim().split(/\s+/);51 if (parts.length < 3) return null;5253 const lRaw = parts[0];54 const l = lRaw.endsWith("%") ? Number.parseFloat(lRaw) : normalizeLightness(Number.parseFloat(lRaw));55 const c = Number.parseFloat(parts[1]);56 const h = Number.parseFloat(parts[2]);57 if (!Number.isFinite(l) || !Number.isFinite(c) || !Number.isFinite(h)) return null;5859 const color: OklchColor = {60 l,61 c,62 h,63 };64 if (alphaPart !== undefined) {65 const a = Number.parseFloat(alphaPart.trim());66 if (Number.isFinite(a)) color.alpha = a;67 }68 return color;69}7071/** Format components back into an `oklch()` string (lightness as %). */72export function formatOklch(color: OklchColor, alpha?: number): string {73 const a = alpha ?? color.alpha;74 const base = `${color.l.toFixed(1)}% ${color.c.toFixed(3)} ${color.h.toFixed(1)}`;75// … truncated
Files it writes
More from weekendsuperhero-io/sistine
All 71 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Canvas Background Utilscanvas-background-utils | weekendsuperhero-io/sistine | Utilities | Unverified | no deps | |
| Gradient Utilsgradient-utils | weekendsuperhero-io/sistine | Utilities | Unverified | no deps | |
| Hover Effectshover-effects | weekendsuperhero-io/sistine | Utilities | Unverified | 1 dep | |
| Material Systemmaterial | weekendsuperhero-io/sistine | Utilities | Unverified | no deps | |
| Theme Generatortheme-generator | weekendsuperhero-io/sistine | Utilities | Unverified | no deps |
