Fix hcl/lch/oklch interpolation not reaching pure black/white exactly - #401
Merged
Merged
Conversation
Fixes gka#310. When interpolating toward (or from) an achromatic color in hcl/lch/oklch, saturation/chroma is intentionally frozen at the colored endpoint's value for intermediate steps, so shade()/tint() keep colors looking vivid instead of fading to gray. But the freeze also applied at f === 1 (or f === 0), where the result must equal the achromatic endpoint exactly. Since chroma is still meaningful at L=0 in these color spaces (unlike saturation at L=0/1 in hsl/hsv/hcg/hsi), keeping it frozen at a nonzero value there produced a slightly-off-black/white result, e.g. interpolate('#f00', '#000', 1, 'hcl') returning '#5b0000' instead of '#000000'. Stop the freeze exactly at that boundary so it falls through to the normal linear interpolation, which already resolves correctly to the target's own chroma. Intermediate steps (f between 0 and 1) are unaffected.
🦋 Changeset detectedLatest commit: c36e3d5 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #310.
interpolator/_hsx.jsintentionally freezes saturation/chroma at the colored endpoint's value while interpolating toward (or from) an achromatic color, soshade()/tint()keep colors looking vivid instead of fading to gray at intermediate steps. That freeze is correct and tested behavior forfstrictly between 0 and 1.The bug is that the freeze also applied exactly at the boundary (
f === 1interpolating toward black/white, orf === 0interpolating away from it), where the result must equal the achromatic endpoint exactly. Unlike hsl/hsv/hcg/hsi (where saturation has no visual effect at extreme lightness/value), chroma in hcl/lch/oklch is still meaningful at L=0, so freezing it at a large nonzero value there produces a slightly-off-black/white color once converted to RGB.Fix
Stop the freeze exactly at that boundary (
f < 1/f > 0guards) so it falls through to the existing linear interpolation, which already resolves correctly to the target's own chroma (0 for a fully achromatic target). Every other case is untouched.Test plan
test/mix.test.js:hcl/lch/oklchreaching pure black from a saturated color),red.shade(0.5, 'lch')to lock in that the intermediate-step "stay vivid" behavior is unchanged.'#5b0000'/'#100000'(the exact wrong values from the issue), while the shade test still passes on the reverted code — confirming the test isolates the actual bug rather than the intentional shading behavior.npx vitest run— 2522 passing (2519 existing + 3 new).npm run lint(prettier + eslint) passes, and the repo's pre-commit hook (lint + full test run) passed on commit.