W3C home > Mailing lists > Public > public-houdini@w3.org > June 2015

RE: [css-props-vals] First iteration of L1 spec

From: François REMY <francois.remy.dev@outlook.com>
Date: Sat, 13 Jun 2015 12:44:43 +0200
Message-ID: <DUB405-EAS16846392038EE5173F99A6CA5BA0@phx.gbl>
To: "'Greg Whitworth'" <gwhit@microsoft.com>, "'Florian Rivoal'" <florian@rivoal.net>
CC: <public-houdini@w3.org>, <shanestephens@google.com>

Thanks to all the people involved in this first draft, I'm very much pleased to see things going on that well :-)

I think the choices made regarding the definition of new custom properties look great. It's a nice limited set of things which a browser could understand easily and which could help solve a huge amount of polyfill use cases (regarding the cascade phase). 

A few additions I would like to see added to the allowed types are : 
- "<length-and-percentage>" (accepts "50px", "50%" and "calc(50px + 50%)")
- "<ident>" (any custom identifier) 
- "<ident>(auto, none)" (a list of possible idents which the property does accept)
- "*" (any input, much like a unregistered custom property)

It should also probably be flushed somewhere in the sepc that the "initialValue" should match the given "syntax", of course. 

Otherwise, I think this part looks good.

That being said, I'm sharing Florian's skepticism about the "apply" logic. To me, it looks flawed. Here's why:

[1] It creates a toxic environment

Give how the "apply" hook is proposed to work, it creates an environment that's so hostile to cooperation between polyfills that I don't see me using it for any of my polyfills.

Let's say I create a polyfill for the "size" property which is a shorthand for both "width" and "height". That means that no other polyfill can touch those two essential properties anymore. Ditto for any polyfill which wants to handle "transform". Polyfills are going to conflict with each other all the time, making them unusable outside contained demos. That's not sustainable. 

I'm not sure why you state the polyfills are unlikely to modify native properties. Polyfills have literally no other option than setting the value of native properties to have an effect on the page, I expect most polyfills to actually work this way, and not by going the full Custom Layout road. Sure, things like my CSS Grid polyfill would benefit from Custom Layout but I can see how more lightweight tweaks could simply perform style rewrite to map a new syntax to an older syntax after having applied a bit of magic. This is mostly what my current Grid polyfill does, and it's almost powerful enough so I suspect smaller additions could find it sufficient.

[2] It is insufficient to mimick css properties

Florian's 2.1:
> 2.1) For some properties, but not all, the computed value depends not
> only on the computed value of other properties on the same element (as
> expressed by inputProperties), but also on the parent element.

To restate Florian's 2.1, a fair amount of usefule css properties don't live on their own. Sure, on one hand, a lot of those are affected by the layout phase (which we could arguably leave to Custom Layout), but on the other hand, even properties which do not have a "layout-computed used value" can depend at computation-time on parent state in a non-"inherit" way. Think, for instance, about "justify-self": the ‘auto’ keyword computes to [...] the computed value of ‘justify-items’ on the parent [...] or ‘start’ if the box has no parent.

[3] Transitions seem to conflict with the model

One of the goals of "typed custom properties" is to get transitions working on those custom properties.

Now, I fail to see how we hope to see this working in addition to the "apply" model. Here's why:

Let's say we have a hook computing "--both-numbers" to the sum of "--first-number" and "--second-number" (if both are specified) or computing "--first-number"/"--second-number" from "--both-numbers" (if one of them is missing)). Now, imagine that a transition starts on hover:

    element { --first-number: 0; --second-number: 1; transition: really-all 1s; } 
    element:hover { --first-number: 0; --second-number: 2; }

Since the css engine cannot know how "--both-numbers" is computed, he has to call the "apply" model on every step of the transition. 

How will the css engine know whether the computed value for "--both-numbers" should or should not transition normally after the "apply" model is run? I may be wrong, but it seems impossible to know, which means it won't transition (however, since "--first-number" and "--second-number" do transition and the "apply" logic is called again at every step, it will looks in this specific case like it does transition, which is great). 

Now, the issue happens when you need to handle the real final value of the properties to compute the final value of the computed property, and you want this property to transition normally between those two extremes. This is the case for instance, you set a flag that changes the meaning of some other property, but want the effect of this flag switch to happen gradually (example: a grid where items would move from their old to their new grid area in a transitioned way and not directly like we do now). Since you don't know how far you are in the transition, that's not possible. 

Regardless of whether we want to support this, clearly, we have now put the burden of managing transitions to the "apply" model but the "apply" model doesn't know about transitions which make things very hard. This is why I proposed to use a "StyleTransitionController" model in the past for this purpose (since I've given some more thoughts on the matter since then I'm going to restate what I mean by this hereafter, but you can have a look to http://lists.w3.org/Archives/Public/public-houdini/2015Apr/0007.html for historical notes and context). My goal is to support transitions from and to custom types, but more importantly to make sure property polyfills can easily cooperate by default, something I don't see provided in the "apply" model and which looks out-of-scope in the "Custom Layout" world.

The model I propose would work as follows:

[1] Registration phase

[1.1] In any order, JavaScript code registers Custom Properties declarations just like proposed in this spec. Those declarations are not JavaScript-related, and only affect the CSS Parser/Cascader.

[1.2] In any order, Javascript code registers StyleMutationObservers for a set of css properties he's interested in (aka "inputProperties"). Those will serve to create instances of the StyleTransitionControllers once required (but I can see other use cases for StyleMutationObservers which do not require tweaking the style any further, like building querySelectorLive or a polyfilled touch-action property).

[1.3] In First-In-Last-Out order*, Javascript code registers StyleTransitionControllers types (=DOMString identifiers) which will be used to ensure the StyleTransitionControllers are always executed in the same order on every element, regardless of when they are actually registered on the elements themselves. It can also be used to ensure one and only one of each type of StyleTransitionController is ever attached to a single element.

[1.4] Once all polyfills are finally registered, maybe we should call "CSS.enablePolyfills" which would freeze things and enable the registrations made previously (to avoid to rerun the parser multiple times).

[*] I propose the First-In-Last-Out order for the following reason: if I load a Grid Polyfill first on my page, I want a subsequent polyfill on my page to rely on Grid being implemented "as-if-natively" (aka, it should be able to modify "native grid properties" and see those taking effect, so the grid polyfill registered first should actually execute last, after all the other polyfills have executed and possibly modified the grid properties it operates on).

[2] Style Cascade Phase

[2.1] At each frame, the browser resolves the style normally, as if StyleTransitionControllers didn't exist (custom properties have their specified or initial value at this point).

[2.2] For each registered StyleMutationObserver, DOM elements for which the value of any "inputProperty" was updated (not as the result of an ongoing transition but by a css change) will be sent as a batch to an event handler.
In those StyleMutationObservers, StyleTransitionControllers can be created for a set of elements and a given StyleTransitionControllers type (alternatively, existing StyleTransitionControllers may see some elements added or removed from their control). 

The reason why a single StyleTransitionController should be able to operate on multiple elements at the same time (and not one-by-one) is that if you want a light-weight layout that just remaps a set of custom properties to "position: absolute" (or another poyfilled layout that will run in Custom Layout) you actually need to operate on multiple elements at a time. However, in some cases, you only need one StyleTransitionController per element, and don't have to care about managing the grouping complexity.

[3] Style Transition Phase

In the order in which their types were originally registered, StyleTransitionControllers are being called with a copy of the current style of all elements they were attached to. I propose that StyleTransitionControllers have full write access to the current style of elements they work on (but maybe we can, like we do in the "apply" model, restrict that to a list of properties?). 

The style in question is a new kind of style which I call "transition-style" in which properties take either a classical value if they are not in transition, or values like "transition(<length>, 10px, 20px, 0.3)" if they are currently in transition. This gives access to the initial and final value of the transition to the StyleTransitionController (and not just the current value like the apply model does). The StyleTransitionController can therefore take appropriate actions if needed. Once the work of a StyleTranstionController is done, the modified current style of those elements is sent to the next StyleTransitionController.

Once all StyleTransitionControllers have been executed, the resulting style is used by the browser as "runtime style" aka this is the style that will be applied for the frame. If the style has any remaining "transition(...)" at that point, the transition is resolved by the browser using the normal transition rules. 

I can see how "big" this model may look like at first sight, but I honestly am under the impression it doesn't incur much more background work than the "apply" model would, it's only a different way to exposing the same internal hooks to JavaScript. 

Best regards,
Received on Saturday, 13 June 2015 10:45:11 UTC

This archive was generated by hypermail 2.3.1 : Saturday, 13 June 2015 10:45:12 UTC