Skip to content

[css-color-5] Grammar for parsing relative colors? #7721

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

Closed
GPHemsley opened this issue Sep 11, 2022 · 16 comments
Closed

[css-color-5] Grammar for parsing relative colors? #7721

GPHemsley opened this issue Sep 11, 2022 · 16 comments
Labels
Closed as Question Answered Used when the issue is more of a question than a problem, and it's been answered. Commenter Satisfied Commenter has indicated satisfaction with the resolution / edits. css-color-5 Color modification

Comments

@GPHemsley
Copy link

GPHemsley commented Sep 11, 2022

Is it just me, or is the grammar for parsing relative colors missing?

https://drafts.csswg.org/css-color-5/#relative-colors

Channel keywords are defined for each color function, and example usage is provided, but none of the channel keywords appear in the grammar for them.

For example, 3.1. Relative sRGB Colors defines the allowed channel keywords of rgb() as r, g, b, and alpha.

And Example 13 shows:

rgb(from indianred 255 g b)

But the grammar is listed as:

rgb() = rgb( [<percentage> | none]{3} [ / [<alpha-value> | none] ]? ) |
        rgb( [<number> | none]{3} [ / [<alpha-value> | none] ]? ) |
        rgb( [ from <color> ]? [ <number> | <percentage> | none]{3} [ / [<alpha-value> | none] ]?  )
<alpha-value> = <number> | <percentage>

Which makes no mention of r, g, b, or alpha.

It also describes the grammar for the functions like rgb() as being "extended", but it doesn't link to what it's extending, and indeed appears to be completely replacing the syntax from css-color-4, at least from a grammar definition standpoint.

@GPHemsley GPHemsley changed the title [css-color-5] Grammar for parsing relative colors [css-color-5] Grammar for parsing relative colors? Sep 11, 2022
@GPHemsley
Copy link
Author

[accidentally submitted original post early]

@cdoublev
Copy link
Collaborator

A value definition does not always represent the full production's grammar: it may be completed with some rules written in prose, like the channel keywords and the legacy rgb() whose value definition (w/ comma-separated args) is defined in CSS Color 4 (but is not included as an alternation in the "modern" value definition of rgb()). The motivation is to have a value definition that is less complex and whose parsing is certainly less complex to implement/run.

I hope it helps and that I'm not mistaken.

@GPHemsley
Copy link
Author

GPHemsley commented Sep 11, 2022

A value definition does not always represent the full production's grammar: it may be completed with some rules written in prose, like the channel keywords and the legacy rgb() whose value definition (w/ comma-separated args) is defined in CSS Color 4 (but is not included as an alternation in the "modern" value definition of rgb()).

Ah, I hadn't registered that. You're right.

The motivation is to have a value definition that is less complex and whose parsing is certainly less complex to implement/run.

It seems to me that this approach would actually make things more complex to implement, as one has to interpolate the prose instructions into the grammar parsing.

I suspect the dividing line actually more closely approaches author instructions vs. implementer requirements, an issue that is significantly more complex for areas having longer-standing legacy behavior (like what e.g. HTML has to handle).

Still, I would argue that all parsing requirements (as opposed to, say, implementation details) should be defined in a formal grammar somewhere rather than in prose, even if that means having a separate definition of the legacy rules, à la many mature IETF RFCs.

@svgeesus
Copy link
Contributor

It also describes the grammar for the functions like rgb() as being "extended", but it doesn't link to what it's extending, and indeed appears to be completely replacing the syntax from css-color-4, at least from a grammar definition standpoint.

That is a fair point, it is indeed a replacement, but maybe I should still link to the previous definition in CSS Color 4.

@svgeesus
Copy link
Contributor

Which makes no mention of r, g, b, or alpha.

The thing is that for real usage, it would not be a simple "use 100% of this channel" but instead, a big calc() and those r, g, b and alpha are going to be used inside a calc(), which will evaluate to a <number>

@tabatkins
Copy link
Member

It seems to me that this approach would actually make things more complex to implement, as one has to interpolate the prose instructions into the grammar parsing.

Well, depends somewhat on your parsing strategy. Reproducing the entirety of the math-function grammar inline in the RCS syntax, duplicated for each different set of valid channel keywords, would technically work, but if you're not using the grammar directly it makes noticing the parsing differences more difficult. The prose outlines the changes very straightforwardly, so adapting one's existing parsing code to match shouldn't be too odd.

But importantly, we write specs for several purposes and several audiences, and doing that sort of giant grammar duplication (and keeping it in sync with future math-function additions, ugh) would be a massive hit to readability. This not only harms our "web developer" audience, but can potentially harm our "implementor" audience as well, if they're not just literally dropping in our grammar to their system (as mentioned above). I don't believe any of the browsers do so, for instance; they all have parsing code that spans the spectrum from "grammar-driven" to "completely custom" depending on the property/value, and when it's grammar-driven it's not literally the CSS Value Definition Syntax grammar but some other parsing tool's grammar, necessitating rewrites anyway.

@GPHemsley
Copy link
Author

Which makes no mention of r, g, b, or alpha.

The thing is that for real usage, it would not be a simple "use 100% of this channel" but instead, a big calc() and those r, g, b and alpha are going to be used inside a calc(), which will evaluate to a <number>

Oh, I see. I'm not sure the spec makes that obvious.

It seems to me that this approach would actually make things more complex to implement, as one has to interpolate the prose instructions into the grammar parsing.

Well, depends somewhat on your parsing strategy. Reproducing the entirety of the math-function grammar inline in the RCS syntax, duplicated for each different set of valid channel keywords, would technically work, but if you're not using the grammar directly it makes noticing the parsing differences more difficult.

Rereading the relevant sections of the spec with the new context presented in this thread, I think I would propose a different approach.

The use of calc() and other math functions should already be subsumed into the definitions of <number> and <percentage> (and if they're not, that's a different conversation anyway).

The problem lies in that the parameters to rgb() and its ilk are not defined individually, but using {3}. And while I recognize that this simplifies the definitions that require all three parameters to be of the same type, it does sort of relegate the channel keywords to something of an afterthought. (And to the extent that that affects "extending" the existing definitions, we've already established that this spec is actually replacing them wholesale.)

Incidentally, as far as making parsing differences more difficult to notice, on the way to filing this issue, I encountered code that was a little overzealously combining the definitions of hsl() and hwb() (based on definitions in earlier specs), so I would certainly support an approach that makes those differences clearer. The fact that channel keywords are only mentioned in prose IMO makes it easier to overlook that the definitions of these two functions are not entirely the same.

@svgeesus
Copy link
Contributor

Oh, I see. I'm not sure the spec makes that obvious.

This is true for every property: you can use calc() anywhere you want; the calculated result is what has to conform to the grammar.

I guess the place to make that explicit is in the CSS Custom Properties spec, and then with well-chosen examples for those values which are especially likely to be used with custom properties.

On the other hand I see your point; the impact of CSS Custom Properties is extensive, and we need to communicate this better.

The use of calc() and other math functions should already be subsumed into the definitions of <number> and <percentage> (and if they're not, that's a different conversation anyway).

Agreed.

The problem lies in that the parameters to rgb() and its ilk are not defined individually, but using {3}. And while I recognize that this simplifies the definitions that require all three parameters to be of the same type, it does sort of relegate the channel keywords to something of an afterthought.

Forbidding someone from using, say, the r value as part of the green channel, is more trouble than it is worth and there could be valid use cases (imagine someone doing custom chromatic adaptation by making each of the channels of xyz-d65 be a weighted sum of x, y and z for example).

@svgeesus
Copy link
Contributor

I encountered code that was a little overzealously combining the definitions of hsl() and hwb() (based on definitions in earlier specs)

The only thing which is the same with those two is the hue channel, which has identical definitions in both. What was the code doing?

@GPHemsley
Copy link
Author

The problem lies in that the parameters to rgb() and its ilk are not defined individually, but using {3}. And while I recognize that this simplifies the definitions that require all three parameters to be of the same type, it does sort of relegate the channel keywords to something of an afterthought.

Forbidding someone from using, say, the r value as part of the green channel, is more trouble than it is worth and there could be valid use cases (imagine someone doing custom chromatic adaptation by making each of the channels of xyz-d65 be a weighted sum of x, y and z for example).

Hmm. I think I was thinking of it more from the perspective of naming the part of grammar in its definition, rather than restricting the use of these keywords elsewhere.

But I do think I am beginning to get a better picture of the design of these keywords: They're not so much part of the grammar as they are defined as part of the concept the grammar represents. As in, "once you have this color, these keywords are defined to represent certain characteristics of it". I think that was the part I was struggling with, and is perhaps the part that needs to be most clarified.

In particular, I get the sense that the Relative Color Syntax section has some circular definitions and/or is conflating the syntactic definitions with the concepts behind them.

Separately (and I don't understand colorspaces enough to know if this is a nonsense question): Does it make sense to define these relative color keywords separately/independently from the function definitions? If you define a color using rgb(), can you extract a w value from it, for instance?

I encountered code that was a little overzealously combining the definitions of hsl() and hwb() (based on definitions in earlier specs)

The only thing which is the same with those two is the hue channel, which has identical definitions in both. What was the code doing?

servo/rust-cssparser#293

@GPHemsley
Copy link
Author

I encountered code that was a little overzealously combining the definitions of hsl() and hwb() (based on definitions in earlier specs)

The only thing which is the same with those two is the hue channel, which has identical definitions in both. What was the code doing?

servo/rust-cssparser#293

I see this conflation already led to servo/rust-cssparser@bf31c1d which then led to #6911.

@fantasai fantasai added the css-color-5 Color modification label Nov 16, 2022
@svgeesus
Copy link
Contributor

Separately (and I don't understand colorspaces enough to know if this is a nonsense question): Does it make sense to define these relative color keywords separately/independently from the function definitions?

Not a nonsense question.

If you define a color using rgb(), can you extract a w value from it, for instance?

We considered that and decided against it, so you only get the channels of the colorspace in use. This avoids having to coerce colors to the sRGB gamut (for hsl() and hwb()) or define a default RGB space (if you extract the r value of an oklab() color for example.

So it would have made sense, but introduced complexity with no obvious value, so the answer to your earlier question is "no".

@svgeesus
Copy link
Contributor

But I do think I am beginning to get a better picture of the design of these keywords: They're not so much part of the grammar as they are defined as part of the concept the grammar represents. As in, "once you have this color, these keywords are defined to represent certain characteristics of it". I think that was the part I was struggling with, and is perhaps the part that needs to be most clarified.

That is precisely what it is doing. I will take a look over the break and see if that can be more clearly expressed.

@svgeesus
Copy link
Contributor

It also describes the grammar for the functions like rgb() as being "extended", but it doesn't link to what it's extending, and indeed appears to be completely replacing the syntax from css-color-4, at least from a grammar definition standpoint.

Now fixed; clarifies that it is extending the modern (not the legacy) syntax, and uses tokens like <modern-rgb-syntax> to make clear what exactly from CSS color 4 is being extended.

@svgeesus
Copy link
Contributor

svgeesus commented Jan 5, 2024

@GPHemsley does the current text satisfy your concern, now, or is more clarification needed?

If an origin color is specified, the remaining arguments can either be specified directly, as normal, or be specified as a channel keyword referring to one of the channels of the origin color. Math functions can also use these keywords to do dynamic modifications of the origin color’s channels.

@GPHemsley
Copy link
Author

I'm far removed from this now, but a cursory reread seems to indicate that things are much clearer now, thank you.

@svgeesus svgeesus closed this as completed Jan 5, 2024
@svgeesus svgeesus added Commenter Satisfied Commenter has indicated satisfaction with the resolution / edits. Closed as Question Answered Used when the issue is more of a question than a problem, and it's been answered. labels Jan 5, 2024
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. Commenter Satisfied Commenter has indicated satisfaction with the resolution / edits. css-color-5 Color modification
Projects
None yet
Development

No branches or pull requests

5 participants