Skip to content

feat(core): selectable gamut mapping method - #209

Merged
pbroom merged 2 commits into
mainfrom
feat/core-gamut-map-method
Oct 10, 2026
Merged

pbroom merged 2 commits into
mainfrom
feat/core-gamut-map-method

Conversation

@pbroom

@pbroom pbroom commented Oct 6, 2026 •

Copy link
Copy Markdown
Owner

toSrgbGamut/toP3Gamut only reduce OKLCH chroma at fixed L and h, which
under-shoots what CSS (and colorjs.io) produce: oklch(0.7 0.35 150) maps
to C=0.193 instead of the CSS Color 4 result C=0.210.

  • Add GamutMapOptions { method?: 'chroma-reduction' | 'css' } to
    toSrgbGamut, toP3Gamut, toSrgbGamutInto and toP3GamutInto (types
    GamutMapMethod/GamutMapOptions exported). 'css' implements the CSS
    Color 4 binary search with local MINDE (deltaE OK JND 0.02, epsilon
    1e-4, clip in the target gamut) allocation-free on scratch objects; it
    matches colorjs.io toGamut({ method: 'css' }) to ~1e-15 dE_OK for both
    sRGB and P3.
  • Default stays 'chroma-reduction': the CSS method shifts L and h, while
    contrast regions, plane geometry, slider tracks and the driver's
    displayed state rely on L/h being preserved, so it is not strictly
    better for existing tests or callers. Both methods return colors that
    pass inSrgbGamut/inP3Gamut (with GAMUT_EPSILON slack) unchanged and
    snap the lightness endpoints, so membership and mapping stay
    consistent.
  • toSrgbGamut/toP3Gamut get a one-argument overload so passing them
    straight to Array.prototype.map keeps type-checking (the index
    argument is ignored at runtime).
  • driver: mapDisplayedColors(requested, { gamutMapMethod }) and
    CreateColorStateOptions.gamutMapMethod forward the method; the default
    is unchanged.
  • maxChromaAt reuses one scratch probe instead of allocating a Color per
    bisection step.
  • The gamut-region solver's search-gap handling (oklchChromaField) is
    about maxChromaAt's bisection bracket, not the mapping method, so it
    is left as is.

Part of a stacked set of PRs (#204 → #221) from the color-kit assessment. Validated per branch with pnpm build and pnpm test; lint, format, typecheck, size, artifact, preprod and agents checks pass at the top of the stack.

🤖 Generated with Claude Code

pbroom commented Oct 6, 2026 •

Copy link
Copy Markdown
Owner Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@greptile-apps

greptile-apps Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 4/5

[Medium risk] Adds selectable gamut mapping algorithm to color processing.

The PR is not ready to merge because requested-color updates can silently discard the selected mapping method.

Fix All in CursorFindings

  1. P1 Selected mapping resets on update ▶

Summary

The PR adds selectable chroma-reduction and CSS gamut mapping to core and forwards the choice through driver state creation.

  • It retains chroma reduction as the default and adds mapping tests and API documentation.
  • Subsequent state re-derivation does not retain an explicitly selected CSS method.
Diagram
%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A["createColorState(method: css)"] --> B["State: displayed with CSS; meta.method = css"]
  B --> C["useColor requested-color update"]
  C --> D["createColorState without method"]
  D --> E["State: displayed with chroma reduction; meta.method reset"]
Loading

Reviews (1) · Last reviewed commit: "feat(core): selectable gamut mapping met..."

Comment thread packages/driver/src/color-state.ts
@vercel

vercel Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
color-kit Ready Ready Preview Oct 10, 2026 7:11pm UTC

pbroom commented Oct 10, 2026 •

Copy link
Copy Markdown
Owner Author

Merge activity

  • Oct 10, 6:47 PM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Oct 10, 7:06 PM UTC: Graphite rebased this pull request as part of a merge.
  • Oct 10, 7:06 PM UTC: @pbroom merged this pull request with Graphite.

@pbroom
pbroom changed the base branch from fix/core-contrast-precision to graphite-base/209 October 10, 2026 19:03
@pbroom
pbroom changed the base branch from graphite-base/209 to main October 10, 2026 19:04
pbroom and others added 2 commits October 10, 2026 19:05
toSrgbGamut/toP3Gamut only reduce OKLCH chroma at fixed L and h, which
under-shoots what CSS (and colorjs.io) produce: oklch(0.7 0.35 150) maps
to C=0.193 instead of the CSS Color 4 result C=0.210.

- Add GamutMapOptions { method?: 'chroma-reduction' | 'css' } to
  toSrgbGamut, toP3Gamut, toSrgbGamutInto and toP3GamutInto (types
  GamutMapMethod/GamutMapOptions exported). 'css' implements the CSS
  Color 4 binary search with local MINDE (deltaE OK JND 0.02, epsilon
  1e-4, clip in the target gamut) allocation-free on scratch objects; it
  matches colorjs.io toGamut({ method: 'css' }) to ~1e-15 dE_OK for both
  sRGB and P3.
- Default stays 'chroma-reduction': the CSS method shifts L and h, while
  contrast regions, plane geometry, slider tracks and the driver's
  displayed state rely on L/h being preserved, so it is not strictly
  better for existing tests or callers. Both methods return colors that
  pass inSrgbGamut/inP3Gamut (with GAMUT_EPSILON slack) unchanged and
  snap the lightness endpoints, so membership and mapping stay
  consistent.
- toSrgbGamut/toP3Gamut get a one-argument overload so passing them
  straight to Array.prototype.map keeps type-checking (the index
  argument is ignored at runtime).
- driver: mapDisplayedColors(requested, { gamutMapMethod }) and
  CreateColorStateOptions.gamutMapMethod forward the method; the default
  is unchanged.
- maxChromaAt reuses one scratch probe instead of allocating a Color per
  bisection step.
- The gamut-region solver's search-gap handling (oklchChromaField) is
  about maxChromaAt's bisection bracket, not the mapping method, so it
  is left as is.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
useColor's setRequested and the multi-color model dropped
meta.gamutMapMethod, so updates reverted to chroma reduction.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@pbroom
pbroom force-pushed the feat/core-gamut-map-method branch from ef170b6 to 218d614 Compare October 10, 2026 19:05
@pbroom
pbroom merged commit 674257b into main Oct 10, 2026
6 of 7 checks passed
@pbroom
pbroom deleted the feat/core-gamut-map-method branch October 10, 2026 19:06

This branch was successfully deployed

1 active deployment
Preview — 218d6149 Deployed Oct 10, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant