w3c / csswg-drafts

CSS Working Group Editor Drafts
https://drafts.csswg.org/
Other
4.5k stars 664 forks source link

[css-values][css-backgrounds][css-animations][css-transitions][fill-stroke] How to handle linked list-valued properties? #7164

Open SebastianZ opened 2 years ago

SebastianZ commented 2 years ago

4431 requests to turn box-shadow into a shorthand. During the discussion of this change @dbaron mentioned that there is a general issue in handling list-valued longhand properties.

According to him and @emilio dealing with such values is complicated as the properties can have differently long lists. And this seems especially true for background-* longhands.

As @tabatkins summarized it, they'd prefer if the computed value lists would be aligned with the used value lists, i.e. expanded or truncated depending on the length of the length-controlling property.

Having a deeper look at that, I realized that there are currently differences in handling such properties across browser engines but also within one browser engine. E.g. in Blink, the computed value list length of background-size is controlled by the length of the list in background-image, while transition-duration's computed value is not bound to the one of transition-property and rather corresponds to the authored value. In Gecko, all the values correspond to the authored value.

Looking at the specifications, they seem to only clearly define the used values but the computed value is underdefined. CSS Backgrounds 3 defines how different lengths of authored values are handled and mentions that "the UA must calculate its used value by repeating the list of values until there are enough" (emphasizing by me). It doesn't seem to specify how computed values are actually generated. CSS Animations 1 refers to this specification as well as CSS Transitions 1 does. There might also be more specifications for such list-valued properties that need clarification.

This issue is meant to clarify how such linked list-valued properties should be handled. Maybe the definition for this handling should be moved to a central place (maybe in CSS Values?).

Sebastian

SebastianZ commented 2 years ago

Added Agenda+ as @dbaron, @emilio, and @tabatkins already started the discussion on this in a previous call and it is required for making progress on https://github.com/w3c/csswg-drafts/issues/4431 and other linked list-valued properties.

Sebastian

SebastianZ commented 2 years ago

For what it's worth, this also applies to fill-image and its related list-valued properties.

Sebastian

fantasai commented 2 years ago

I think clarifying and having a central definition might be useful, but in general if there isn't any kind of specific computation specified the computed value mirrors the specified value, so I think per-spec that's what's required. Whether we want to change that is a different question that might be worth discussing.

css-meeting-bot commented 2 years ago

The CSS Working Group just discussed [css-values][css-backgrounds][css-animations][css-transitions][fill-stroke] How to handle linked list-valued properties?.

The full IRC log of that discussion <emeyer> Topic: [css-values][css-backgrounds][css-animations][css-transitions][fill-stroke] How to handle linked list-valued properties?
<emeyer> Github: https://github.com/w3c/csswg-drafts/issues/7164
<emeyer> TabAtkins: Issue 4431 asked to turn box-shadow into a shorthand. Very reasonable. Because it’s a list-valued property, we’d have to make all longhands the same. It was brought up by dbaron that whenever you have these groups of longhands and have to sync up all the values, it’s problematic for implementations.
<emeyer> …What exactly are the reasons, and to what degree should we be avoiding them? This touches on some things being proposed and probably coming, like Toggles. It would be good to know how careful we need to be.
<emeyer> dbaron: I’m most familiar with an implementation that no longer exists, so this may be questionable input, but my experience was that it was a LOT of code to implement these.
<emeyer> …Given that, it’s not a that strong a preference. It’s a case where you can get properties where 90% of the work is parsing, not implementation on the other end.
<lea> syncing up list-valued longhands is also a problem for authors. This does not match a human's mental model about this at all
<emeyer> TabAtkins: Do you recall if it was a particular type of syncing up, or if it was all equally hard.
<emeyer> dbaron: I think it was they were all equally difficult and yet things were sufficiently different that you couldn’t coalesce.
<emeyer> TabAtkins: We should get input from people working on extant rendering engines. Anyone on the call that knows about that?
<emeyer> astearns: Doesn’t sound like we have current implementation experience here on the call. Lea did mention a problem for authors.
<fremy> +1 leaverou
<emeyer> lea: When authors think about list-value properties, they think of each thing as a unit. They think, I want this image at this size in this place, and the syntax forces them to do things in a way they don’t think about it.
<emeyer> TabAtkins: Agreed.
<emeyer> lea: Right, this makes sense with single values.
<emeyer> TabAtkins: We’re asking if it’s okay to expand for things that are list values.
<emeyer> florian: When used with a single value on the list, it’s useful to break up. Authors can help by defining custom properties that define certain combinations.
<emeyer> TabAtkins: That’s partially true. Authors can work around the present situation, and that’s good. But forcing them to work around a lot when we could take that need away isn’t great.
<emeyer> …If we can avoid this being necessary in common cases, that would be great.
<emeyer> astearns: The issue is whether we’re going to more closely specify computed values of list values.
<emeyer> Sebastian: It’s not completely defined yet but most or all list-valued properties base their logic on the definition for background. So they’re just referring to that and still differ on implementation. Even within one engine.
<emeyer> astearns: The difference is something we need to address even if implementation difficulty is a problem. I’m wondering whether we can get to a resolution about that specific difference and then people can start implementation to give feedback on difficulty.
<emeyer> TabAtkins: I’d like to have an idea of the matrix of current possibilities.
<astearns> ack fantasai
<emeyer> fantasai: The main question is are the lists truncated or extended at compute time or use-value time? Specification says use-value. It was suggested this might be easier if compute time was used. The difference in behavior will affect inheriting, which most of these properties don’t do, and might affect result of getComputedStyle.
<emeyer> …I don’t think it would be a problem to switch over to say list-matching happens at computed value time, but don’t know if that would make implementation easier.
<emeyer> astearns: We need to clarify what the current implementations do, how difficult it would be to change, and what we’d like to do in general at computed value time. We should take it back to the issue.
SebastianZ commented 2 years ago

I've created three fiddles now to check the current behavior of computed values again. One with longhand properties where the length-controlling properties have longer lists than the controlled ones, one with longhand properties where the length-controlling properties have shorter lists than the controlled ones, and one with shorthand properties. Testing with those fiddles, my initial comment on that seems to be slightly incorrect because in Gecko background-* longhands are treated special when it comes to the computed value.

Tests

Here are my observations regarding the list lengths of computed and used values in tabular form. I don't have access to MacOS/Safari, so maybe someone else can check the test cases there.

Longhands with controlling property lists longer than controlled ones

https://jsfiddle.net/SebastianZ/wyupok0h/

Chrome Firefox
background-size computed and used: repeated to match controlling property computed and used: repeated to match controlling property
transition-duration computed: as authored, used: repeated to match controlling property computed: as authored, used: repeated to match controlling property
animation-duration computed: as authored, used: repeated to match controlling property computed: as authored, used: repeated to match controlling property

Longhands with controlling property lists shorter than controlled ones

https://jsfiddle.net/SebastianZ/bxj4y3ar/

Chrome Firefox
background-size computed and used: truncated to match controlling property computed: as authored, used: repeated to match controlling property
transition-duration computed: as authored, used: truncated to match controlling property computed: as authored, used: truncated to match controlling property
animation-duration computed: as authored, used: truncated to match controlling property computed: as authored, used: truncated to match controlling property

Shorthands with controlling property lists longer than controlled ones

https://jsfiddle.net/SebastianZ/wmeLxhtf/

Chrome Firefox
background-size computed and used: padded with initial value to match controlling property computed and used: padded with initial value to match controlling property
transition-duration computed and used: padded with initial value to match controlling property computed and used: padded with initial value to match controlling property
animation-duration computed and used: padded with initial value to match controlling property computed and used: padded with initial value to match controlling property

"Repeated" refers to the spec. text, which currently reads like this:

The lists are matched up from the first value: excess values at the end are not used. If a property doesn’t have enough comma-separated values to match the number of layers, the UA must calculate its used value by repeating the list of values until there are enough.

Summary of current behavior

Both Chromium and Gecko special-case the background-* longhands by repeating them for the computed value in case they are shorter than background-image. When background-image is shorter than the longhands, Chromium truncates the longhands while Gecko keeps them as authored. All other list-valued longhands are kept as authored for the computed value in both engines.

What do we want?

@LeaVerou mentioned on the last call, from an author's perspective, it may be a little easier to understand what's happening if computed and used values were the same. That means, repeating when the controlling property list is longer and truncating if it is shorter. I agree with her on that.

@emilio, @tabatkins, @nt1m Could you please check back with your teams regarding the current implementations, the complications, and what would make implementations actually easier?

Sebastian

css-meeting-bot commented 2 years ago

The CSS Working Group just discussed list-valued properties, and agreed to the following:

The full IRC log of that discussion <TabAtkins> Topic: list-valued properties
<TabAtkins> github: https://github.com/w3c/csswg-drafts/issues/7164
<TabAtkins> Rossen_: Who's in a good position to summarize?
<TabAtkins> fremy: We have a couple of list-valued properties (more than one value)
<TabAtkins> fremy: And sometimes these properties interact with each other, each with a list
<TabAtkins> fremy: So question is - how do you represent this value in computed/used values?
<TabAtkins> fremy: preserve author-provided length, repeat as necessary, truncate
<TabAtkins> fremy: Say author provides three values, bu tonly one is used, should used value report just the one or all three?
<Rossen_> q?
<TabAtkins> fremy: Lea mentioned on the call that for her it seems easier to ahve computed and used be the same
<TabAtkins> fremy: So this seems to mean repeating the value as necessary to match the controlling property's length, or truncating to match
<TabAtkins> fremy: sebastian had same
<TabAtkins> fremy: but seems strange to me
<TabAtkins> fremy: computed value is what you inherit from
<TabAtkins> fremy: seems strange to say you inherit value to children that doesn't have all the values author provided
<TabAtkins> fremy: Even if the parent doesn't use them, might want to use them in the children
<TabAtkins> fremy: But apart from that...
<emilio> q+
<TabAtkins> fantasai: issue was initially filed due to complexity of adding more such property groups
<TabAtkins> fantasai: specifically about box-shadow longhands
<TabAtkins> fantasai: So question was if there's a way to make this repeating list situation easier to implement, or at least consistent
<TabAtkins> fantasai: previously the spec deferred to bckground specs
<Rossen_> q?
<TabAtkins> fantasai: Which gets the length and repeats/truncates at used value time, computed value is as specified
<TabAtkins> emilio: Not in Blink tho
<Rossen_> ack emilio
<TabAtkins> emilio: the firefox behavior preserves the length of all lists
<TabAtkins> emilio: I think that's generally simpler, avoids interdependencies at computed value time,w hcih are annoying
<TabAtkins> emilio: Also it's weird to lose values because the controlling property wasn't long enough, when inheriting
<TabAtkins> emilio: So I prefer that behavior. IMPlementing Chrome behavior is just more special cases during style resolution
<TabAtkins> emilio: so I'd rather keep the props independent and match them up at used value time
<TabAtkins> dbaron: I think there may be some relationship between these rules and the interpolation rules
<Rossen_> ack dbaron
<TabAtkins> dbaron: One thing to watch out for is if we add to this set of properties, which have interp rules that do LCM interp rather than trunctation/repetition
<TabAtkins> dbaron: bc i think we have two interp variants
<TabAtkins> dbaron: there's def a variant where two diff lengths gives you a list of LCM length
<TabAtkins> dbaron: there's one where you get the longer length, padded with a neutral default for the shorter
<TabAtkins> TabAtkins: I assume the third would be to interp the shorter length and truncate?
<TabAtkins> dbaron: Don't think that's present in anything
<TabAtkins> astearns: There's def a variant where we just don't interp between diff lengths
<TabAtkins> fantasai: LCM seems awful
<TabAtkins> fantasai: 5 and 7 would give 35 values. probably not what the author wanted
<TabAtkins> dbaron: It does the right thing in many cases, such as stroke-dasharray
<TabAtkins> dbaron: Might be others that do that
<TabAtkins> fantasai: So my starting proposal is we keep the spec as-is (specified in B&B), maybe clarify. computed value is as specified, used value is repeated or truncated as necessary.
<TabAtkins> fantasai: And make sure all specs use that behavior and file tests and bugs as appropriate
<TabAtkins> fantasai: If we shoud do something different somebody should propose it
<fantasai> TabAtkins: I agree, I think that's the right proposal
<fantasai> TabAtkins: iank_ & co, does that sound like something we can do?
<TabAtkins> rune: dunno off the top of my head if we can do it
<dbaron> s/rune/futhark/
<TabAtkins> rune: I think it's probably fixable?
<TabAtkins> Rossen_: So not opposed?
<TabAtkins> futhark: Not right now based on what I know
<TabAtkins> Rossen_: And it makes sense as a path forward
<TabAtkins> s/rune/futhark/
<TabAtkins> Rossen_: So we have a proposal. Anything else to add?
<fantasai> TabAtkins: if this is the resolution, does that unblock our ability to do more sets of list-valued properties?
<fantasai> TabAtkins: or are we they still problematic to add?
<Rossen_> q
<TabAtkins> emilio: They're still harder to implement, but 🤷
<TabAtkins> emilio: Less special-casey than the current behavior for sure
<fantasai> s/behavior/Blink behavior/
<dbaron> s/current behavior/current Blink behavior/
<TabAtkins> Rossen_: so proposal is to keep the spec as is
<TabAtkins> Rossen_: did something need to be clarified?
<TabAtkins> fantasai: Don't specifically say the computed value is same as specified value, it's implied. can be louder in the spec so it's obvious
<TabAtkins> fantasai: Should review other list-valued specs besides B&B to make sure it's fine
<TabAtkins> fantasai: And might want to add a generic definition in V&U so we're not all reffing B&B.
<fantasai> TabAtkins: Proposed resolution is keep spec as-is, add generic definition to V&U
<TabAtkins> fantasai: And make sure all specs are consistent with it
<TabAtkins> Rossen_: So anythig to be added? Objections
<TabAtkins> RESOLVED: Keep list-valued property definition as-is (as specified in B&B). Genericize definition and move to V&U.
fantasai commented 2 years ago

Edits for Values and Units L4 done: #linked-properties

We'll need updates for all the remaining tagged specs. Dropping css-backgrounds-3 in favor of css-backgrounds-4 because L3 is practically a REC already.