[csswg-drafts] [css-color-4] § 12: at what stage are colors compared, and when are two <color-space>s the same? (#14472)

svgeesus has just created a new issue for https://github.com/w3c/csswg-drafts:

== [css-color-4] § 12: at what stage are colors compared, and when are two <color-space>s the same? ==
[§ 12 Comparing `<color>` Values](https://drafts.csswg.org/css-color-4/#comparing-color-values) leaves two things undetermined. Both are observable, and implementations have diverged.

### 1. What stage of the pipeline does the comparison happen at?

[§ 12](https://drafts.csswg.org/css-color-4/#comparing-color-values) says "Given two `<color>` values C1 and C2". `<color>` is a syntactic production, which reads like specified values — but both consumers the section names, `style()` container queries and CSS Transitions, compare computed values.

This matters whenever a notation does not survive resolution. [§ 15.1 Resolving sRGB values](https://drafts.csswg.org/css-color-4/#resolving-sRGB-values) resolves `hsl()`, `hwb()`, hex and named colors to *the corresponding sRGB color*, and a missing hue has no counterpart to survive into:

```css
hsl(none 0% 50%)  /* vs */  rgb(50% 50% 50%)
```

As computed values these are the same sRGB gray with no missing components, so they are equivalent by step 2. As specified values, step 3 is reachable and the missing hue may disqualify the pair. [§ 15.1](https://drafts.csswg.org/css-color-4/#resolving-sRGB-values) does not mention `none` or [missing components](https://drafts.csswg.org/css-color-4/#missing-color-component) at all.

### 2. When are two `<color-space>`s the same?

The final paragraph of [§ 12](https://drafts.csswg.org/css-color-4/#comparing-color-values) answers this for the sRGB family, but nothing covers the polar and rectangular forms of the same space:

```css
oklch(50% 0 none)  /* vs */  oklab(50% 0 0)
```

If `oklab` and `oklch` are different `<color-space>`s, step 3 returns false on the missing hue. If they are one color space in two syntactic forms — which is what they are colorimetrically, and [§ 4.1](https://drafts.csswg.org/css-color-4/#color-syntax) groups `hsl()`, `hwb()`, `lch()` and `oklch()` together as [cylindrical polar](https://drafts.csswg.org/css-color-4/#cylindrical-polar-color) representations — then step 2 applies, the missing hue has no counterpart in the rectangular form, and the answer is true.

The two polar families resolve differently, and that difference is not itself in question:

| notation | [§ 15](https://drafts.csswg.org/css-color-4/#resolving-color-values) resolves to | polar form survives? |
|---|---|---|
| `hsl()`, `hwb()` | the corresponding sRGB color ([§ 15.1](https://drafts.csswg.org/css-color-4/#resolving-sRGB-values)) | no |
| `lch()` | the corresponding CIE Lab **or LCH** color ([§ 15.2](https://drafts.csswg.org/css-color-4/#resolving-lab-lch-values)) | yes |
| `oklch()` | the corresponding Oklab **or OkLCh** color ([§ 15.3](https://drafts.csswg.org/css-color-4/#resolving-oklab-oklch-values)) | yes |

The sRGB row is constrained by backward compatibility — `hsl()` has serialized as `rgb()` since CSS Color 3, and content depends on it — whereas the Lab and Oklab families were new enough to preserve the authored form. So this asymmetry is load-bearing and cannot be removed; [§ 12](https://drafts.csswg.org/css-color-4/#comparing-color-values) has to account for it rather than assume it away.

[§ 12](https://drafts.csswg.org/css-color-4/#comparing-color-values)'s final paragraph is already the precedent for doing so: it decouples "which `<color-space>` is this compared in" from "what is its computed value", for exactly the family where the two come apart. What is missing is the equivalent statement for `lab()`/`lch()` and `oklab()`/`oklch()`, which can be made without disturbing how those notations resolve or serialize.

One consequence worth confirming either way: if the comparison is on computed values, step 1 can never fire for `hsl()` or `hwb()`, since they are no longer cylindrical by then, and [powerless components](https://drafts.csswg.org/css-color-4/#powerless-color-component) would only ever matter for `lch()` and `oklch()`.

### Suggested direction

1. State in [§ 12](https://drafts.csswg.org/css-color-4/#comparing-color-values) which values are compared. If computed, the final paragraph becomes a consequence of [§ 15.1](https://drafts.csswg.org/css-color-4/#resolving-sRGB-values) rather than a rule of its own.
2. Record in [§ 15.1](https://drafts.csswg.org/css-color-4/#resolving-sRGB-values) what becomes of `none`, documenting what implementations already do: a component with an sRGB counterpart keeps its missingness (`rgb(none 128 0)`), one without loses it (`hsl(none 100% 50%)` → `rgb(255 0 0)`).
3. Say whether the polar and rectangular forms of one space are the same `<color-space>` for this comparison, and if so which components are compared. Step 4 already treats `oklab` as the canonical meeting point.

### Implementation status

Chromium and Gecko currently disagree with [§ 12](https://drafts.csswg.org/css-color-4/#comparing-color-values) and with each other on adjacent cases, which suggests the section is being read in more than one way:

- <https://issues.chromium.org/issues/559142618> — no cross-notation comparison at all
- <https://bugzilla.mozilla.org/show_bug.cgi?id=2070595> — step 1 not applied

Both engines fail every positive subtest of <https://github.com/web-platform-tests/wpt/pull/62570>, which covers powerless hues within a single color space.


Please view or discuss this issue at https://github.com/w3c/csswg-drafts/issues/14472 using your GitHub account


-- 
Sent via github-notify-ml as configured in https://github.com/w3c/github-notify-ml-config

Received on Thursday, 10 September 2026 15:39:37 UTC