diff --git a/packages/ui/src/theme/color.test.ts b/packages/ui/src/theme/color.test.ts new file mode 100644 index 00000000..208d28e1 --- /dev/null +++ b/packages/ui/src/theme/color.test.ts @@ -0,0 +1,19 @@ +import { describe, expect, test } from "bun:test" +import { fitOklch, oklchToRgb } from "./color" + +describe("fitOklch", () => { + test("keeps an in-gamut color unchanged", () => { + const color = { l: 0.5, c: 0, h: 0 } + + expect(fitOklch(color)).toEqual(color) + }) + + test("reduces an out-of-gamut color until its RGB values are in gamut", () => { + const result = fitOklch({ l: 0.5, c: 0.5, h: 0 }) + const rgb = oklchToRgb(result) + + expect(result.c).toBeLessThan(0.5) + expect(Math.min(rgb.r, rgb.g, rgb.b)).toBeGreaterThanOrEqual(0) + expect(Math.max(rgb.r, rgb.g, rgb.b)).toBeLessThanOrEqual(1) + }) +}) \ No newline at end of file diff --git a/packages/ui/src/theme/color.ts b/packages/ui/src/theme/color.ts index 730642b1..b294e9f8 100644 --- a/packages/ui/src/theme/color.ts +++ b/packages/ui/src/theme/color.ts @@ -100,6 +100,10 @@ export function hexToOklch(hex: HexColor): OklchColor { return rgbToOklch(r, g, b) } +function isRgbInGamut(rgb: { r: number; g: number; b: number }) { + return Math.min(rgb.r, rgb.g, rgb.b) >= 0 && Math.max(rgb.r, rgb.g, rgb.b) <= 1 +} + export function fitOklch(oklch: OklchColor): OklchColor { const base = { l: clamp(oklch.l, 0, 1), @@ -117,7 +121,7 @@ export function fitOklch(oklch: OklchColor): OklchColor { c *= 0.9 const next = { ...base, c } const out = oklchToRgb(next) - if (out.r >= 0 && out.r <= 1 && out.g >= 0 && out.g <= 1 && out.b >= 0 && out.b <= 1) { + if (isRgbInGamut(out)) { return next } }