Re: [csswg-drafts] [css-values-4][css-color-4] Resolve `<percentage>` to `<number>` as a color component in math functions (#8485)

I am not sure what context / behavior should be considered to further discuss about resolving `<percentage>` to `<number>`:

  - in math function / at parse time
  - in `rgb()` and `rgba()` / at parse or serialization time in a specified value
  - in any context / at parse or serialization time in a specified value

<details>
  <summary><strong>Table with all properties or types for which <code>&lt;percentage></code> resolves to <code>&lt;number></code> (there is currently no case with <code>&lt;integer</code>):</strong></summary>

| property / type           | resolved | mapping |
| ------------------------- | -------- | ------- |
| `border-image-slice`      |       no | `[0, unknown]` |
| `mark-border-slice`       |       no | `[0, unknown]` |
| `scale`                   |      yes | `[0, 1]` |
| `<alpha-value>`           |      yes | `[0, 1]` or `[0, 255]` |
| `<brightness()>`          |       no | `[0, 1]` |
| `<cmyk-component>`        |        ? | `[0, 1]` |
| `<contrast()>`            |       no | `[0, 1]` |
| `<custom-params>`         |        ? | `[unknown, unknown]` |
| `<grayscale()>`           |       no | `[0, 1]` |
| `<invert()>`              |       no | `[0, 1]` |
| `<lab()>`                 |        ? | `[0, 100]`, `[-125, 125]`, `[-125, 125]` (respectively for l, a, b) |
| `<lch()>`                 |        ? | `[0, 100]`, `[0, 150]` (respectively for l,c) |
| `<modern-hsl-syntax>`     |      yes | `[0, 255]` |
| `<modern-hsla-syntax>`    |      yes | `[0, 255]` |
| `<modern-rgb-syntax>`     |      yes | `[0, 255]` |
| `<modern-rgba-syntax>`    |      yes | `[0, 255]` |
| `<oklab()>`               |        ? | `[0, 1]`, `[-0.4, 0.4]`, `[-0.4, 0.4]` (respectively for l, a, b) |
| `<oklch()>`               |        ? | `[0, 1]`, `[0, 0.4]` (respectively for l,c) |
| `<opacity()>`             |       no | `[0, 1]` |
| `<predefined-rgb-params>` |        ? | `[0, 1]` |
| `<saturate()>`            |       no | `[0, 1]` |
| `<scale-*()>`             |       no | `[0, 1]` |
| `<sepia()>`               |       no | `[0, 1]` |
| `<xyz-params>`            |        ? | `[0, 1]` |

</details>

**Observations**

Outside math functions, the resolution only applies for `scale`, `<alpha-value>`, and color functions. The specs defining the properties/types for which resolution does not apply, do not require it.

For [`scale`](https://w3c.github.io/csswg-drafts/css-transforms-2/#propdef-scale), I think CSS Transforms 2 let implementers choose to resolve `<percentage>` either at parse time or at serialization time.

  > Numbers are used during serialization of specified and computed values.

For `<alpha-value>`, I think CSSOM gives the same freedom, but it is less clear:

  > If the value is internally represented as an integer between 0 and 255 inclusive (i.e. 8-bit unsigned integer), follow these steps: [...]
  > Otherwise, return the result of serializing the given value as a `<number>`.

It is even less clear in CSS Color 4:

  > If the alpha is any other value than 1, it is explicitly included in the serialization, as a `<number>`, not a `<percentage>`.

For all color functions, CSS Color 4 defines the resolution of `<percentage>` (excluding `<alpha-value>`) in a specified value (section 14), presumably at parse time, except for sRGB functions, in which it is resolved at serialization time (section 15).

In a math function, the resolution happens at parse time, when applicable, but Chrome/FF do not apply it when they would not resolve a naked `<percentage>` at the same place.

I think it is a bug, but respective specs should define its resolution, either at parse time or at serialization time, either explicitly or handwavy.

<details>
  <summary>Test code</summary>

```js
const cases = [
  // label, property = label, ...input
  ['border-image-slice', undefined, '100%', 'calc(100%)'],
  ['mark-border-slice', undefined, '100%', 'calc(100%)'],
  ['scale', undefined, '100%', 'calc(100%)'],
  ['<brightness()>', 'filter', 'brightness(100%)', 'brightness(calc(100%))'],
  ['<contrast()>', 'filter', 'contrast(100%)', 'contrast(calc(100%))'],
  ['<grayscale()>', 'filter', 'grayscale(100%)', 'grayscale(calc(100%))'],
  ['<invert()>', 'filter', 'invert(100%)', 'invert(calc(100%))'],
  ['<opacity()>', 'filter', 'opacity(100%)', 'opacity(calc(100%))'],
  ['<saturate()>', 'filter', 'saturate(100%)', 'saturate(calc(100%))'],
  ['<sepia()>', 'filter', 'sepia(100%)', 'sepia(calc(100%))'],
  ['<scale-*()>', 'transform', 'scale(100%)', 'scale(calc(100%))'],
  ['<alpha-value>', 'opacity', '100%', 'calc(100%)'],
  ['<alpha-value>', 'flood-opacity', '100%', 'calc(100%)'],
  ['<alpha-value>', 'shape-image-threshold', '100%', 'calc(100%)'],
  ['<alpha-value>, <hsl()>, <hsla()>', 'color', 'hsl(0deg 100% 0% / 1000%)', 'hsl(0deg calc(100%) calc(0%) / calc(100%))'],
  ['<alpha-value>, <rgb()>, <rgba()>', 'color', 'rgb(100% 0% 100% / 0%)', 'rgb(calc(100%) calc(0%) calc(100%) / calc(0%))'],
  ['<lab()>', 'color', 'lab(100% 0% 100%)', 'lab(calc(100%) calc(0%) calc(100%))'],
  ['<lch()>', 'color', 'lch(100% 0% 0deg)', 'lch(calc(100%) calc(0%) 0deg)'],
  // May be not supported
  ['<oklab()>', 'color', 'oklab(calc(100%) calc(0%) calc(100%))', 'oklab(calc(100%) calc(0%) calc(100%))'],
  ['<oklch()>', 'color', 'oklch(100 calc(0%) 0deg)', 'oklch(100 calc(0%) 0deg)'],
  ['<predefined-rgb-params>', 'color', 'color(srgb calc(100%) calc(0%) calc(100%))', 'color(srgb calc(100%) calc(0%) calc(100%))'],
  ['<xyz-params>', 'color', 'color(xyz calc(100%) calc(0%) calc(100%))', 'color(xyz calc(100%) calc(0%) calc(100%))'],
  // Not supported (and requires @color-profile --custom {})
  ['<custom-params>', 'color', 'color(--custom calc(100%) calc(0%) calc(100%))', 'color(--custom calc(100%) calc(0%) calc(100%))'], 
  // Not supported
  ['<cmyk-component>', 'color', 'device-cmyk(calc(100%) calc(0%) calc(0%) calc(0%))', 'device-cmyk(calc(100%) calc(0%) calc(0%) calc(0%))'], 
]
cases.forEach(([label, prop = label, ...inputs]) => {
  const res = []
  inputs.forEach(input => {
    style[prop] = ''
    style[prop] = input
    res.push(style[prop])
  })
  console.log(label, ...res)
})
```
</details>

---

> Please use this link to get an up-to-date view of drafts (updates every 5 minutes):
>
> https://w3c.github.io/csswg-drafts/

Sorry, my omnibox and I are used to drafts.csswg.org. I did not know that it may now lag behind w3c.github.io instead of simply returning a server error.

-- 
GitHub Notification of comment by cdoublev
Please view or discuss this issue at https://github.com/w3c/csswg-drafts/issues/8485#issuecomment-1441433340 using your GitHub account


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

Received on Thursday, 23 February 2023 09:25:04 UTC