You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[css-conditional-5][css-color] Define style() color comparison in more precise terms #13157
Unless I missed it, the way values are expected to match in style() queries is largely left undefined, with some non-normative prose around matching computed values. As a result, implementations vary dramatically in how they handle this, which is particularly noticeable in color values.
(Gecko shows all red, @emilio might know why — yes the about:config option is on)
But even where they agree, the results are nonsensical. From an author perspective all of these should be green, because they are all exactly the same color! The only one that could plausibly not match is rgb(-10 0 0) and that matches in both impls. The reasons they don't match are largely spec/implementation details and should not be leaking into usage like this.
I suspect we can still fix this as style() is only now rolling out in Gecko, and authors are hesitant to use it until it does. Chrome’s use counter shows 0.4% for style queries but only a small subset of those are going to be registered colors. Time is of the essence though.
I propose we define a rule similar to interpolation in which we first convert colors to the same color space and compare component-wise following powerless component rules too.
I propose we define a rule similar to interpolation in which we first convert colors to the same color space and compare component-wise following powerless component rules too.
That seems like a very good way forward, it is consistent with how we deal with disparate color spaces elsewhere and avoids surprises.
Unless I missed it, the way values are expected to match in style() queries is largely left undefined, with some non-normative prose around matching computed values.
I believe the intention is that this should be the exact same matching behavior as Transitions uses. (Which is indeed largely hand-waved.)
Alltho the Transitions behavior is technically observable (via transition events), I agree that a style() query is much more observable, and so figuring out how to make this clearer and more consistent is important.
converting them to a given color space which will be referred to as the comparison color space below. If one or both colors are already in the comparison color space, this conversion changes any powerless components to missing values. If no comparison color space is specified, oklab is used
(if required) re-inserting carried forward values in the converted colors
the two colors are compared, component by component, for equality. To accommodate floating point errors due to color space conversion, implementations will return true if the difference between two components is less than some small value ε
return true if all components (the color components, and the alpha component) are equal
For interoperability, defining ε should be done in the spec, at least for oklab.
It could well be that we don't need any other comparison color space than oklab but I prefer not to assume that at this stage.
I think we need to decide what we want to do for none components before we author spec text. Currently the algorithm above would return true for e.g. hsl(none 100% 50%) vs yellow. Do we want that? My intuition is no, but we should discuss.
Ok so I think none should not match anything other than none. If you want to resolve nones, you can just wrap your color in a color-mix(). Aside from that, color space conversion should be fine, and if it preserves missing components, all the better.
So if color space conversion invokes powerless-to-missing, that's fine, you'll just get false. Which color space you convert to shouldn't matter, since powerless component groups are symmetrical.
Though if we go down the route of picking an epsilon, it would need to be different not just per color space, but also per component. I wonder if a better strategy may be to compute deltaEOK (which is computationally cheap once you convert to oklab) and use a fraction of the JND. Though that doesn't help when you have powerless components, so we may need to go down that route anyway…
The CSSWG will automatically accept this resolution one week from now if no objections are raised here. Anyone can add an emoji to this comment to express support. If you do not support this resolution, please add a new comment.
Proposed Resolution: Define ε as 0.00001 for oklab
Those component values differ by more than ε, which my tool puts as the same value as the one CSSWG resolved on for Oklab comparisons.
I feel that this value should not be left as implementation defined, by the way.
Note that color equivalence, as defined in section 12, is separate from "visually indistinguishable" which is a much looser classification based on Just Noticeable Differencs (ΔE)..
Activity
SebastianZ commentedon Nov 25, 2025
The current implementation is incomplete, see https://bugzil.la/1945342.
Sebastian
svgeesus commentedon Mar 23, 2026
Firefox has improved since:
That seems like a very good way forward, it is consistent with how we deal with disparate color spaces elsewhere and avoids surprises.
Which spec would be a good home for this?
tabatkins commentedon Mar 23, 2026
I believe the intention is that this should be the exact same matching behavior as Transitions uses. (Which is indeed largely hand-waved.)
Alltho the Transitions behavior is technically observable (via transition events), I agree that a
style()query is much more observable, and so figuring out how to make this clearer and more consistent is important.svgeesus commentedon Mar 23, 2026
Strawperson for color comparisons (adapted from 12. Color Interpolation):
Comparing colors
Comparison of two values for equality takes place by executing the following steps:
oklabis usedFor interoperability, defining ε should be done in the spec, at least for
oklab.It could well be that we don't need any other comparison color space than
oklabbut I prefer not to assume that at this stage.LeaVerou commentedon Mar 23, 2026
I think we need to decide what we want to do for
nonecomponents before we author spec text. Currently the algorithm above would returntruefor e.g.hsl(none 100% 50%)vsyellow. Do we want that? My intuition is no, but we should discuss.svgeesus commentedon Mar 24, 2026
No. Although I would be fine with
oklab(none none none / none)matching everything. But the example you gave is clearly not (just) yellow.svgeesus commentedon Mar 24, 2026
So we want the powerless to missing part, but not the carry-forward part.
LeaVerou commentedon Mar 24, 2026
Ok so I think
noneshould not match anything other thannone. If you want to resolvenones, you can just wrap your color in acolor-mix(). Aside from that, color space conversion should be fine, and if it preserves missing components, all the better.So if color space conversion invokes powerless-to-missing, that's fine, you'll just get false. Which color space you convert to shouldn't matter, since powerless component groups are symmetrical.
Though if we go down the route of picking an epsilon, it would need to be different not just per color space, but also per component. I wonder if a better strategy may be to compute deltaEOK (which is computationally cheap once you convert to oklab) and use a fraction of the JND. Though that doesn't help when you have powerless components, so we may need to go down that route anyway…
21 remaining items
svgeesus commentedon Apr 17, 2026
@astearns it seems we are close to consensus here; if you agree, proposed async resolution.
astearns commentedon Apr 20, 2026
The CSSWG will automatically accept this resolution one week from now if no objections are raised here. Anyone can add an emoji to this comment to express support. If you do not support this resolution, please add a new comment.
Proposed Resolution: Define ε as
0.00001foroklabastearns commentedon Apr 27, 2026
RESOLVED: Define ε as
0.00001foroklab[css-color-4] For color comparisons in Oklab, standardized ε to be 0.…
yisibl commentedon Sep 10, 2026
Hi @svgeesus
I ran a test in your tools. Why do
hwb(320deg 30% 40%)andrgb(153, 77, 128)show up as “Not equivalent”?https://apps.colorjs.io/equivalent-colors/?a=hwb(320deg%2030%25%2040%25)&b=rgb(153%2C%2077%2C%20128)
Is this caused by floating-point numbers?

svgeesus commentedon Sep 10, 2026
Not floating point, but rather the limited precision of quantizing to only 8 bits.
hwb(320deg 30% 40%)isrgb(60% 30% 50%)rgb(153, 77, 128)isrgb(60% 30.1961% 50.1961%)Those component values differ by more than ε, which my tool puts as the same value as the one CSSWG resolved on for
Oklabcomparisons.I feel that this value should not be left as implementation defined, by the way.
Note that color equivalence, as defined in section 12, is separate from "visually indistinguishable" which is a much looser classification based on Just Noticeable Differencs (ΔE)..
svgeesus commentedon Sep 10, 2026
Related:
Bug 2025848 - When comparing AbsoluteColor values for equality, conve…