Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[css-values-4][css-color-4] Resolve <percentage> to <number> as a color component in math functions #8485

Closed
cdoublev opened this issue Feb 22, 2023 · 10 comments
Labels
Closed as Question Answered Used when the issue is more of a question than a problem, and it's been answered. css-color-4 Current Work css-values-4 Current Work

Comments

@cdoublev
Copy link
Collaborator

The definitions of rgb(), rgba(), hsl(), hsla(), has recently received two modifications in CSS Color 4 (not yet visible online at this time), which allow to mix <number> and <percentage> arguments. Basically:

rgb() = rgb(<number>{3}) | rgb(<percentage>{3}) # Before
rgb() = rgb([<number> | <percentage>]{3})       # After

With the new syntax, my problem is that calc(100%) now resolves to 1.

CSS Values 4 defines the simplification of a mathematical function representing <percentage>:

  1. If root is a numeric value:
    • If root is a percentage that will be resolved against another value, and there is enough information available to resolve it, do so, and express the resulting numeric value in the appropriate canonical unit. Return the value.
    • [...]
    • Otherwise, return root.

[...] However, "raw" percentages—ones which do not resolve against another value, such as in opacity—might not block simplification.

To find out if I can resolve a root percentage, I am currently looking for <number> | <percentage> or <integer> | <percentage> (in any order) as contexts, therefore calc(100%) is not resolved in rgb() defined with the syntax that does not mix <number> and <percentage>.

CSS Color 4 does not require to resolve <percentage> in rgb() and rgba() (aside: it is required for non-sRGB color functions), but requires to serialize it as a base 10 <number> in the range [0-255].

For compatibility, the sRGB component values are serialized in <number> form, not <percentage>. Also for compatibility, the component values are serialized in base 10, with a range of [0-255], regardless of the bit depth with which they are stored.

In Chrome/FF, rgb(calc(100%) 0% 0%) serializes to rgb(255, 0, 0) as a specified value.

I am not sure if the right way to solve my problem is to consider that there is not enough information to resolve a percentage in a math function as an argument of rgb() and rgba(), regardless of their syntax (separated/mixed <number> and <percentage>). This would mean that determining if there is enough information to resolve it as a <number> cannot be simply based on if the context is <number> | <percentage> or <integer> | <percentage>.

@cdoublev
Copy link
Collaborator Author

This would mean that determining if there is enough information to resolve it as a <number> cannot be simply based on if the context is <number> | <percentage> or <integer> | <percentage>.

It seems there are a few other productions in which the range to resolve <percentage> to <number> is not [0,1]. And it cannot be simplified in some of them (eg. border-image-slice, in which it resolves against the width of the border image).

It seems a bit odd that a naked <percentage> (not wrapped in a math function) in rgb() is mapped to <number> when serializing whereas it is directly resolved to <number> when simplifying a math function but anyway, I understand this.

@svgeesus
Copy link
Contributor

has recently received two modifications in CSS Color 4 (not yet visible online at this time)

Please use this link to get an up-to-date view of drafts (updates every 5 minutes):

https://w3c.github.io/csswg-drafts/

The changes you mention were made two days ago and here they are

@svgeesus
Copy link
Contributor

The CSS Color 4 spec says

0% represents the minimum value for that color channel in the sRGB gamut, and 100% represents the maximum value. A is equivalent to a , but with a different range: 0 again represents the minimum value for the color channel, but 255 represents the maximum

@svgeesus
Copy link
Contributor

I think the general point you raise is worth some further discussion, so re-opening

@cdoublev
Copy link
Collaborator Author

cdoublev commented Feb 23, 2023

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 specified values
  • in any context / at parse or serialization time in specified values

edit:

After re-reading this issue, I assume the general point was resolving <percentage> to <number> in color functions, either at parse time (such as in a math function) or at serialization time (as currently specified), so I removed the irrelevant parts of this comment.

Excluding in math functions and color functions, resolving a value specified as a <percentage> to a <number> before computed value time seems to only apply for scale and <alpha-value> (in opacity, flood-opacity, shape-image-threshold).

For scale, idea is it would parse into a number internally and serialize as a number (#3399 (comment)).

For <alpha-value>, I am not sure. It looks like CSSOM assumes it may have been resolved at parse time.


Another (probably more interesting) point is why <percentage> should resolve to <number> before computed value time, regardless of the context.

I guess it would be weird if lab(calc(100%) 100% 0) serialize to lab(calc(100) 100% 0), but I wonder if it is worth it, and there are many contexts in which <percentage> could be resolved before computed value time but is not, therefore this is inconsistent.

Chrome/FF resolves <percentage> in math functions only when they would resolve a naked <percentage> (before computed value time) at the same place.

@svgeesus
Copy link
Contributor

svgeesus commented Jul 5, 2023

Unrelated to math functions but somewhat related to this issue, I changed the percent to number mappings for rgb() and rgba() to use a percentage reference range like all the other color functions, rather than the looser prose inherited from older specifications:

percent reference range for rgb() and rgba()

@cdoublev
Copy link
Collaborator Author

cdoublev commented Aug 9, 2023

I edited my previous comment. Basically, the relevant questions are:

  • why <percentage> should resolve to <number> in color functions before computed value time?
  • why <percentage> should resolve to <number> in math functions? (regardless of any higher context)

@svgeesus
Copy link
Contributor

svgeesus commented Aug 23, 2023

Re-reading this issue, I am not sure what the remaining problems are, if any. The spec has changed several times since the issue was opened, and to me at least now seems clear:

Percentages: Allowed for r, g and b
For r, g and b: 0% = 0.0, 100% = 255.0
For alpha: 0% = 0.0, 100% = 1.0

and

The percentage reference range of the color channels comes from the historical fact that many graphics engines stored the color channels internally as a single byte, which can hold integers between 0 and 255. Implementations should honor the precision of the channel as authored or calculated wherever possible. If this is not possible, the channel should be rounded towards +∞.

and this seems to work in practice live test:

let test = document.createElement("div");
test.style.color = "rgb(90 calc(100%) 100%)";
console.log(test.style.color);

Firefox correctly reports

"rgb(90, 255, 255)"

and Safari and Chrome do not seem to implement mixed number and percent in modern rgb() yet.

@cdoublev
Copy link
Collaborator Author

cdoublev commented Aug 23, 2023

Right, back-compatibility requires serializing rgb(), hsl(), hwb(), to legacy rgb(), thus to resolve percentages, even when they are wrapped in a math function (whereas CSS Values requires to preserve it), into a number.

I ask why this should also apply in other color functions (and also for whatever property value defined with <alpha-value>, and scale), because:

  1. resolving a percentage in a math function is relatively complex (and browsers do not always do it, eg. an angle percentage in a gradient function)
  2. if the author specified 100% but 1 is serialized as a specified value, it is not the specified value anymore, for what reasons?

@cdoublev
Copy link
Collaborator Author

Closing in favor of #9395, which describes the problem better.

@svgeesus svgeesus added the Closed as Question Answered Used when the issue is more of a question than a problem, and it's been answered. label Sep 22, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Closed as Question Answered Used when the issue is more of a question than a problem, and it's been answered. css-color-4 Current Work css-values-4 Current Work
Projects
None yet
Development

No branches or pull requests

2 participants