Skip to content

fix(commons/color): Match browser behavior for out-of-gamut oklch colors#4908

Merged
straker merged 1 commit intodequelabs:developfrom
happo:out-of-gamut
Oct 13, 2025
Merged

fix(commons/color): Match browser behavior for out-of-gamut oklch colors#4908
straker merged 1 commit intodequelabs:developfrom
happo:out-of-gamut

Conversation

@trotzig
Copy link
Copy Markdown
Contributor

@trotzig trotzig commented Oct 11, 2025

While working on color-contrast accessibility in our own app we noticed that the contrasts calculated by axe-core were different from the ones we calculated ourselves, despite using the same colors that axe gave us in the data (fgColor, bgColor) for the color-contrast check results. The difference came from how out-of-gamut colors were handled.

As an example, for color oklch(25% 0.75, 345) we got a color parsed to (roughly)
r: 0.73
g: -0.44
b: 0.4

The negative green value here threw getContrast off. When troubleshooting we found that the red, green and blue values were the same ones the browser would give us for the same oklch input (186, 0, 103).

To fix this, we can make use of the toGamut function of colorjs.io and ask for it to clip any out-of-gamut colors. This seems to be what browsers do as well, so this fix should make things align better with what developers see when working in a browser.

Related issue for color.js: color-js/color.js#301

While working on color-contrast accessibility in our own app we noticed
that the contrasts calculated by axe-core were different from the ones
we calculated ourselves, despite using the same colors that axe gave us
in the data (fgColor, bgColor) for the color-contrast check results. The
difference came from how out-of-gamut colors were handled.

As an example, for color oklch(25% 0.75, 345) we got a color parsed to
(roughly)
r: 0.73
g: -0.44
b: 0.4

The negative green value here threw getContrast off. When
troubleshooting we found that the `red`, `green` and `blue` values were
the same ones the browser would give us for the same oklch input (186,
0, 103).

To fix this, we can make use of the `toGamut` function of colorjs.io and
ask for it to clip any out-of-gamut colors. This seems to be what
browsers do as well, so this fix should make things align better with
what developers see when working in a browser.

Related issue for color.js: color-js/color.js#301
@trotzig trotzig requested a review from a team as a code owner October 11, 2025 01:39
@CLAassistant
Copy link
Copy Markdown

CLAassistant commented Oct 11, 2025

CLA assistant check
All committers have signed the CLA.

Copy link
Copy Markdown
Contributor

@straker straker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the pr and doing the investigation in to what was going on! Confirmed the color before and after clipping in Chrome, Firefox, and Safari, and it does indeed show that browsers are clipping the out of gamut value.

Reviewed for security.

@straker straker changed the title fix(commons/color) Match browser behavior for out-of-gamut oklch colors fix(commons/color): Match browser behavior for out-of-gamut oklch colors Oct 13, 2025
@straker straker merged commit 5036be8 into dequelabs:develop Oct 13, 2025
21 of 23 checks passed
Garbee pushed a commit that referenced this pull request Oct 31, 2025
…ors (#4908)

While working on color-contrast accessibility in our own app we noticed
that the contrasts calculated by axe-core were different from the ones
we calculated ourselves, despite using the same colors that axe gave us
in the data (fgColor, bgColor) for the color-contrast check results. The
difference came from how out-of-gamut colors were handled.

As an example, for color oklch(25% 0.75, 345) we got a color parsed to
(roughly)
r: 0.73
g: -0.44
b: 0.4

The negative green value here threw getContrast off. When
troubleshooting we found that the `red`, `green` and `blue` values were
the same ones the browser would give us for the same oklch input (186,
0, 103).

To fix this, we can make use of the `toGamut` function of colorjs.io and
ask for it to clip any out-of-gamut colors. This seems to be what
browsers do as well, so this fix should make things align better with
what developers see when working in a browser.

Related issue for color.js:
color-js/color.js#301
This was referenced Jan 5, 2026
WilcoFiers added a commit that referenced this pull request Jan 6, 2026
###
[4.11.1](v4.11.0...v4.11.1)
(2026-01-06)

### Bug Fixes

- allow shadow roots in axe.run contexts
([#4952](#4952))
([d4aee16](d4aee16)),
closes [#4941](#4941)
- color contrast fails for oklch and oklab with none
([#4959](#4959))
([8f249fd](8f249fd))
- **color-contrast:** do not incomplete on textarea
([#4968](#4968))
([d271788](d271788)),
closes [#4947](#4947)
- **commons/color:** Match browser behavior for out-of-gamut oklch
colors ([#4908](#4908))
([5036be8](5036be8))
- don't runs rules that select `html` on nested `html` elements
([#4969](#4969))
([1e9a5c3](1e9a5c3))
- replaced luminance threshold constant 0.03928 with 0.04045
([#4934](#4934))
([316967d](316967d)),
closes [#4933](#4933)
- **rgaa:** adjust mapping of aria-hidden-\* and valid-lang
([#4935](#4935))
([77571f2](77571f2))
- **valid-lang:** update valid-langs for newer language codes
([#4966](#4966))
([c3f5446](c3f5446)),
closes [#4963](#4963)

This PR was opened by a robot 🤖 🎉
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.

3 participants