Open SebastianZ opened 2 years ago
I have to say, even when it was me that brought up the keyword solution, it is my least favorite. I am split between option 1 and 2.
Option 1 allows reusing known gradient functions syntax and combines it with <1d-image>
values. So, as @LeaVerou expressed it, we get the gradient controls for free. And any addition to the gradient functions or <1d-image>
automatically works everywhere. For me, the big downside of this solution is that it reuses gradient terminology, which seems somewhat incorrect to me.
Option 2 is guided by gradient syntax but adds its own functions. This makes them semantically correct. And it still allows authors to define them in a similar way as gradients. But the big downside here is that the syntax regarding the color stops is not the same because the meaning of thickness is different from the positioning syntax for color stops in gradients. And another downside is that existing functions and data types are not reused. So, any additions to them also require some updates to those functions.
Sebastian
Agree that keyword is bad; divergent syntaxes based on keywords aren't great for authors, and make Typed OM cry.
I also think that, if we believe it's worthwhile to specify gradients with stripe syntax (which, I believe, hasn't been sufficiently established yet) that it's better to give them their own function rather than nest functions. (aka option 2 over all the others)
But the big downside here is that the syntax regarding the color stops is not the same because the meaning of thickness is different from the positioning syntax for color stops in gradients.
This is fine? It's literally the entire point of them, after all, and I'm not seeing why this is a problem in linear-stripes()
but okay in linear-gradient(..., stripes())
.
And another downside is that existing functions and data types are not reused. So, any additions to them also require some updates to those functions.
No, we'd just create a non-terminal for the first arg of each of the gradient functions, and then use it in both gradients and stripes. Then any updates apply to all at the same time.
But the big downside here is that the syntax regarding the color stops is not the same because the meaning of thickness is different from the positioning syntax for color stops in gradients.
This is fine? It's literally the entire point of them, after all, and I'm not seeing why this is a problem in
linear-stripes()
but okay inlinear-gradient(..., stripes())
.
It might just be my gut feeling but linear-gradient(..., stripes())
looks more explicit than linear-stripes()
. But yeah, it's a weak point.
And another downside is that existing functions and data types are not reused. So, any additions to them also require some updates to those functions.
No, we'd just create a non-terminal for the first arg of each of the gradient functions, and then use it in both gradients and stripes. Then any updates apply to all at the same time.
What I meant by that is that neither the gradient functions nor the new <1d-image>
data type are reused by that option. Though you're right that we could share more between both types of functions to mitigate this issue.
We might also introduce a <flex-color-stop>
/ <stripe>
data type for the stripes syntax, which could be shared with the stripes()
function and reused in other places in the future.
I currently also slightly tend to option 2 (with the adjustments mentioned above), as its semantic seems a little clearer to me.
Sebastian
background-image: linear-stripes(45deg, red 25%, yellow 30%, lime 40%, blue);
But the big downside here is that the syntax regarding the color stops is not the same because the meaning of thickness is different from the positioning syntax for color stops in gradients.
The syntax isn't fully described in this issue - the lengths above would be the thicknesses? So we have a red stripe from 0-25%, a yellow stripe from 25%-55%, a lime stripe from 55%-95% and blue for the final 5%?
This is a sensible way way to specify them, but I think it also means it must be option 2 as we really shouldn't make the meaning of lengths within a function depend on some other parameter.
Overall I really like this suggestion - it's essentially syntactic sugar for a linear-gradient with a 0-length transition between colors, which are a bit of a pain to specify when there are lots of stripes.
background-image: linear-stripes(45deg, red 25%, yellow 30%, lime 40%, blue);
But the big downside here is that the syntax regarding the color stops is not the same because the meaning of thickness is different from the positioning syntax for color stops in gradients.
The syntax isn't fully described in this issue - the lengths above would be the thicknesses? So we have a red stripe from 0-25%, a yellow stripe from 25%-55%, a lime stripe from 55%-95% and blue for the final 5%?
Correct. The actual syntax for the color stops hasn't landed in the spec. yet but it is defined in #7029. With that syntax, the blue stripe would actually be interpreted as flexible value taking up the remaining space. So, in this case it is equivalent to 5%. In the following example red and blue would split the remaining space of 30% between them in a ratio 2:1:
background-image: linear-stripes(45deg, red 2fr, yellow 30%, lime 40%, blue 1fr);
Same here with the remaining space being 100% - 50px:
background-image: linear-stripes(45deg, red 2fr, yellow 20px, lime 30px, blue 1fr);
This is a sensible way way to specify them, but I think it also means it must be option 2 as we really shouldn't make the meaning of lengths within a function depend on some other parameter.
The meaning of lengths also stays the same in option 1 because of reusing stripes()
which uses this syntax within the gradients functions.
I can see the logical conflict here as authors might expect that the same syntax is shared between both but this issue also applies to option 2.
Overall I really like this suggestion - it's essentially syntactic sugar for a linear-gradient with a 0-length transition between colors, which are a bit of a pain to specify when there are lots of stripes.
Right, I mentioned that earlier somewhere, but yes, the point of this is to simplify 0-length transitions between colors. That means e.g. linear-stripes(red, yellow, lime, blue)
would be equivalent to linear-gradient(red 25%, yellow 25%, yellow 50%, lime 50%, lime 75%, blue 75%)
and instead of linear-gradient(red 20px, yellow 20px, yellow 50%, lime 50%, lime calc(100% - 20px), blue calc(100% - 20px))
you could write linear-stripes(red 20px, yellow, lime, blue 20px)
.
In addition to that we get flexible values, which isn't really possible at the moment (or at least extremely cumbersome because you'd have to calculate the percentages yourself).
Sebastian
I've added a note and an example to my initial comment that the syntax with nested functions would allow for mixing gradients and stripes.
Given that, I still slightly tend to option 2. And others seem to agree with that. So let's see if we can resolve on that.
Sebastian
Weak preference for Option 1. I think it's easier for authors to combine two concepts that they know than to learn about a third concept, even if the third concept is very very similar to the two existing concepts. Option 1 is something some may even try anyway, whereas Option 2 is something you either know about or don't.
Agreed on the arguments against Option 3, yeah, let's not do that.
I agree with @tabatkins in https://github.com/w3c/csswg-drafts/issues/7244#issuecomment-1115391083 :
I also think that, if we believe it's worthwhile to specify gradients with stripe syntax (which, I believe, hasn't been sufficiently established yet) that it's better to give them their own function rather than nest functions. (aka option 2 over all the others)
Wrt
No reuse of
<1d-image>
I don't think we care? How we organize our non-terminals is not something authors should ever need to care about.
No reuse of
<1d-image>
I don't think we care? How we organize our non-terminals is not something authors should ever need to care about.
But it does affect the language: Re-using <1d-image>
means that if we define more types of 1D images down the line, they work with gradients out of the box, without anyone having to resolve, spec, and implement new syntax.
That's not at all guaranteed. "It works in the spec" doesn't mean we get the implementation for free - see all the bugs where browsers still don't support calc() in various spots that take a numeric value.
Skipping a little spec work isn't a great argument to organize something a particular way, especially if that implies a suboptimal authoring syntax (nested functions). I'm also not sure how likely a second 1d-image even is.
That's not at all guaranteed. "It works in the spec" doesn't mean we get the implementation for free - see all the bugs where browsers still don't support calc() in various spots that take a numeric value.
Skipping a little spec work isn't a great argument to organize something a particular way, especially if that implies a suboptimal authoring syntax (nested functions). I'm also not sure how likely a second 1d-image even is.
Not sure if this is in reply to me, but I'm not talking about spec work at all, I'm talking about the author facing syntax.
I believe, the main advantage of option 1 besides reusing existing syntax is that it allows mixing smooth gradients and stripes. This is not possible with the other options.
Given the example from the original post:
background-image: linear-gradient(45deg, black, stripes(red, yellow, lime, blue) 100px, white);
This resembles a gradient using current syntax of:
background-image: linear-gradient(
45deg,
black,
red calc(50% - 50px), red calc(50% - 25px),
yellow calc(50% - 25px), yellow 50%,
lime 50%, lime calc(50% + 25px),
blue calc(50% + 25px), blue calc(50% + 50px),
white);
So, it gets rid of all the complicated calculations required now. And for anyone wondering how that looks like, here's the result:
Also, option 1 and 2 are non-exclusive. So we may go with 2 first to cover the stripes-only use cases and discuss the mixed-gradient-and-stripes case separately.
Sebastian
I believe, the main advantage of option 1 besides reusing existing syntax is that it allows mixing smooth gradients and stripes. This is not possible with the other options.
That is an excellent point. Composability FTW. I think I'm now strongly in favor of Option 1.
- Add
<1d-image>
to existing gradient functions [...] Cons [...]
- Semantically incorrect
What if we call it bands()
instead of stripes()
? Then it doesn't seem that incorrect inside gradients…
A color stop has a color and 0-2 length-percentages.
Here it seems that you are using 100px
as the size of the stripes, and let them be auto placed between the adjacent color stops. But what if the author want to control the position of the stripes?
I tend to think it's more consistent to just keep using 0-2 length-percentages for the position, instead of the size.
The behavior could be that stripes()
produces 2 color stops (start and end positions of the stripes), and the area inside is painted with the stripes. Then,
linear-gradient(90deg, stripes(magenta, yellow, lime));
linear-gradient(90deg, stripes(magenta, yellow, lime) 0% 100%);
linear-gradient(90deg, magenta 33.33%, yellow 33.33% 66.66%, lime 66.66%);
linear-gradient(90deg, black, stripes(magenta, yellow, lime), black);
linear-gradient(90deg, black, stripes(magenta, yellow, lime) 33.33% 66.66%, black);
linear-gradient(90deg, black, magenta 33.33% 44.44%, yellow 44.44% 55.55%, lime 55.55% 66.66%, black);
linear-gradient(90deg, black, stripes(magenta, yellow, lime) 50%, black);
linear-gradient(90deg, black, stripes(magenta, lime) 50%, black);
linear-gradient(90deg, black, stripes(magenta, lime) 50% 50%, black);
linear-gradient(90deg, black, magenta 50%, lime 50%, black);
background-image: linear-gradient(45deg, black, stripes(red, yellow, lime, blue) 100px, white);
I actually skipped over this example, sorry. Now I'm not at all sure what it's actually supposed to be doing. That middle argument appears to be expecting the 100px to be a width, rather than a position (which is what all other lengths in a gradient are). How is that meant to mix with the other stops?
I'd assumed that the option 1 was about allowing <1d-image>
as an alternative to the <color-stop-list>
production. That is straightforward to define (tho it's still nesting functions). I'm just not sure what exactly you're expecting with your more complex example. I think at best you'd have to write that as:
linear-gradient(
45deg,
black,
stripes(red, yellow, lime, blue) calc(50% - 50px) calc(50% + 50px),
white
);
That is, the 1d-image would have to be paired with a double-position, and it defines the color within that range (using the range's size as the background painting area, basically).
The important question here, tho, is: how much do we expect this sort of thing to actually occur, such that it's worthwhile to add this additional complexity? Complex gradients can be a little verbose to define already; is that stopping people from using gradients in those cases? Do we have reason to believe that this type of complex gradient is something authors want to use a lot, so they'd benefit from making it easier?
The CSS Working Group just discussed [css-images-4] Allow stripes to be used as gradients
, and agreed to the following:
RESOLVED: We go with option 2 and worry about composability in the future
In https://github.com/w3c/csswg-drafts/issues/2532 it was discussed to allow one-dimensional images in two-dimensional contexts. Lately it was resolved to discuss this feature separately from the original use case of one-dimensional images.
So far, there were four ideas to achieve that. The direct way of adding
<1d-image>
to<image>
is discussed in #7241. The other three all have in common that they refer to gradients or gradient-like funcions. I've summarized them here (in the order they were mentioned) including their pros and cons:Add
<1d-image>
to existing gradient functionsExamples:
Pros:
Cons:
Create separate stripes functions
Pros:
Cons:
<1d-image>
Add keyword to existing gradient functions
Pros:
Cons:
<1d-image>
Sebastian