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: Wed, 17 Jun 2015 20:50:58 +0200
Message-ID: <DUB405-EAS145E538AD46A95851972951A5A60@phx.gbl>
To: "Tab Atkins" <jackalmage@gmail.com>, "'Shane Stephens'" <shans@google.com>, "'Greg Whitworth'" <gwhit@microsoft.com>, "'Florian Rivoal'" <florian@rivoal.net>
CC: <public-houdini@w3.org>
Thanks Tab and Shane for your replies. 

 

I’ll try to give concrete examples to showcase my points below, and reply to
your two emails at once (given Shane switched to HTML emails already, I feel
less guilty for using colors to differentiate the quotes: blue for Shane,
violet for Tab). 

 

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) 

 

ISSUE: Add the above types to the spec. 

  

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

 

ISSUE: add this somewhere 

 

==================================== 
[1] It creates a toxic environment 
==================================== 

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. 

 

Yes. It's precisely this that led us to propose 'apply'. Consider the
problem from this perspective: how can polyfill authors create new custom
properties without having to know about every other intersecting property in
the world? 

 

As a simple example, take '--extra-margin', which does: 

width += --extra-margin 

 

And '--tile-width', which wants to do: 

width = --tile-width 

  

Depending on the order in which these are registered, you get very different
results. 

 

It's impossible for us to solve this problem for all time, baked into the
browser. Instead we need to make it possible for framework authors to solve
subsets of the problem and be flexible into the future. 

 

'apply' exists not to prevent properties from working together, but to force
the existence of user-level frameworks that can mediate property conflicts
like this. A framework can provide their own registration functionality that
takes the functions and converts them into a single coherent
per-native-property apply function. 

 

 

I feel extremely unsatisfied after reading this argumentation. The
“execution order” issue you outline is something extremely frequent in
computer science and which has been sorted out for a long time. Examples
includes Jersey’s or WinRT’s Http filters, but also a lot of
decorator-driven APIs. 

 

Given the order in which polyfills are going to be executed can be defined
as the order in which they register (see notes later), and given that this
order can be freely altered by the author of the webpage, I don’t see any
reason to believe an user-level framework can do anything which cannot be
provided by the browser, in this case. Enforcing a framework for the sake of
enforcing a framework looks sad to me.

 

Moreoever, requiring the use of a user-level framework (like my own
Parallia) at the spec level is something I want to discourage strongly. If
polyfills have to cooperate at the user level, it’s pretty clear that the
author of the most popular polyfills will somehow force others to use the
same framework and this framework will become a requirements for years to
come. I, for once, don’t want my polyfill framework to becomes the next
JQuery in 10 years, which everyone seems forced to use because plugins
depend on it, but it actually provides no value except providing a
legacy-inspired interface to technologies the browser provide natively. I
want people to be able to innovate in this sector, and by requiring a common
user-level framework we clearly are not helping people to innovate. 

 

Finally, dealing with other people’s plugins is something that doesn’t look
much harder to me than supporting new native additions to the platform. Many
plugins or components you’ll find on the web today assume “box-sizing:
content-box” to work properly. Adding any new property that affects layout
will probably make it harder for polyfills to work properly; whether it’s a
native property or a polyfilled one doesn’t make it any different. That’s
fine, it’s just a limitation of polyfills that they often can’t work
properly with things they didn’t know about when they were made. It’s still
possible to use them in contexts that will not trigger these issues, or to
adapt the polyfill. After all, you have the source code. 

 

 

 

I'd be very interested in hearing *how* you think polyfills are supposed to
coordinate. Seriously, we have no idea how it's possible to coordinate
between properties without the scripts explicitly knowing about each other.
There's a killer ordering problem that you can't solve without either (a)
the stylesheet author specifying an order they run in the stylesheet, or (b)
the scripts coordinating with each other. 

 

You can, of course, fairly trivially write a coordinator that takes several
apply hooks in order and generates a combined apply hook for all of them.
Just union their input and output properties, then run them in the passed
order. [...] Having the spec provide such a coordinator might be possible,
of course. 

 

[However, a script] registration order doesn't work; it means that async
scripts cause race conditions. If two things affect the same properties, and
have to run in a particular order to work properly, then it might run
correctly most of the time on one computer due to network conditions, but
fail in production. 

 

There’s a trick here. If you look at the steps I propose again, you’ll see
that the registration I talk about is a DOMString registration (aka
“StyleTransitionController’s types”). As such, this registration should not
be done by the polyfill itself but by a script on the page that registers
all the polyfills in order and at once [*]. The actual implementation is
only created much later, when it’s actually needed. You could potentially
require a StyleTransitionController type and find out it isn’t needed on the
page so skip its loading.

 

As such, the author doesn’t need to know about the polyfill internals to
make the registration, just the name the polyfill needs to have registered
in order to work. After that, it’s up to the author to choose the best
location in the code to make the actual registration.

 

[*] We may actually enforce this by limiting the registration to one single
call accepting an array of strings (or at least we should throw on
subsequent registrations trying to register a type that was not already
registered during the first call, to ignore redundant registrations made by
independent frameworks running after some user code handling the
registration). Or we may leave this open and let authors deal with it. After
all, it’s their code.

 

 

 

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. 

 

Nobody stated this. In fact, all of the examples in the draft we wrote
explicitly modify native properties. I think you misread Florian's email,
where he proposed an additional stage and stated that writing to native
properties *in his new stage* was unlikely. 

 

 

Indeed, it looks like I misread the whole thing; sorry. 

 

 

 

==================================== 
[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. 

 

Access to parent styles isn't forbidden in the draft specification.
Florian's question was about whether to force authors to specify that they
want to access parent styles before enabling it. 

  

 

Okay. That doesn’t seem to be explicitly allowed in the spec either (and
usually a spec defines what’s allowed and not what’s not allowed, as that
would be an infinite set of things :-D) but I guess that’s just because it’s
an early draft. It caused confusion but if it’s just an omission then forget
this comment. 

 

 

 

 

==================================== 
[3] Transitions seem to conflict with the model 
==================================== 

  

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). 

 

One of the following must be true: 

(1) --both-numbers is a typed, declared custom property using the new draft.
In this case it should transition as the system knows how to transition it,
and also knows to track its computed value for changes (which is precisely
how native properties transition). However, things are going to look
terrible because you're transitioning both the property and the things that
the property is calculated from. Don't do that :) 

(2) --both-numbers is a Custom Properties level 1 style property. It doesn't
transition because it can't. 

(3) --both-numbers isn't a property at all, but an internal abstraction.
Obviously it can't transition in this case either. 

 

But I also have to ask what --both-numbers is supposed to be for? Given the
current model, it can't effect anything (you can't set up chains of action
using apply). 

  

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. 

 

The only way you can control the position of things with this approach is by
controlling their top/left, or their transform. Both have wonderful
transition behavior already. 

 

Please note, by the way, that this is just level 1. We need a minimum viable
subset that solves some real problems now, and is also extensible. 

 

----------------------- 

 

It's not clear to me what your proposed model is trying to solve. Can you
provide some clear examples of things that your model would enable which
can't be supported by the current approach? We could start with the
--first-number / --second-number example above, but we need: 

* some kind of effect (what does --both-numbers exist for and what does it
control?) 

* an example script showing how this would be supported using your model 

* one or more example timelines of inputs and outputs that you think aren't
achievable using the current draft. 

 

Cheers , 

    -Shane 

 

As you noted, if I want to animate the location of an object on the screen,
I can use “transform” which transitions perfectly fine. The issue is that
the “apply” hook will be run *after* transitions already took place. So, any
change I’ll make to “transform” in the hook cannot possibly rely on the
“transition” property to animate. That’s my issue at this point. You’ve also
no way to know a transition is in progress, and use this info to precompute
the next frames while the browser is idle, for instance. Like Tab said, it
will be necessary to provide some insight about transitions anyway to the
“apply” hook, which is why I call it a “StyleTransitionController”.

 

Also, polyfills will want to allow transitions from/to “auto” or “none”
values, which is why I believe since we have to provide the
“StyleTransitionController” with “from/to/currentTransitionPoint” for all
properties which have been updated or are transitionning.

 

Here’s something else I might want to do: a lightweight multiline layout
with fixed-height variable-width boxes which relies on a
“StyleTransitionController”, “position: absolute” and “transform” to work.
Some custom properties controls the visibility state of items, and when the
position of an element changes, I want to be able to initiate a transition
between the original location and final location of the items, following the
“transition” property normally % the “transform” property (so it may be
‘none’ for no transition, or ‘ease-in 3s’, or anything else). If I initiate
some transition, I want a polyfill further down the road to be able to see
the transition going on and interpret it (so if I set a transition between
“20px” and “auto”, the second polyfill may actually map this to a transition
from “20px” to “50px” by precomputing the auto value.

 

Here’s how it would work with StyleTransitionControllers:

 

-          The first controller detects that the position of some items must
change. It overrides the “transform” property from “<old-value>” to
“transition(<old-value>, <new-value>, <transform>, 3s ease-out, 0%)”. 

-          If there would be no other controller, the browser would detect
here that the “transform” property should transition and will add the
relevant transition. If “transform” is used for input of some controller,
those will be called on later frames since the browser will update it using
its normal transition behavior.

 

That’s for the basic case. Now let’s imagine there’s a second controller
(that’s maybe a L2 feature).

 

-          The second controller receives the values as defined by the
previous controller. Its purpose is to redefine how transitions work for
“transform”, because the matrix-based transition doesn’t work for the author
for some reason. So it sees that the “transform” property is going to
transition and decides it want to inform the browser not to use a known
interpolation algorithm to do the transition. It can set “transform” to
“transition(<old-value>, <new-value>, <custom-transition>, 3s, 0%)” instead,
and let the following controller or the browser take the focus. 

-          When the browsers see this transition request for the first time,
it starts a 3s transition and uses the old value because 0% implies it, but
it notes it can’t make this interpolation. So, on the next frame, the
browser will provide “transition(<old-value>, <new-value>,
<custom-transition>, 3s, 2%)” as input to the StyleTransitionControllers and
the second controller (that knows how to deal with the value) will set
transform to an intermediate value between “<old-value>” and “<new-value>”
using its own custom algorithm; if it did leave the
“transition(<old-value>,…,<custom-transition>, 3s, 2%)” value as is, since
the browser cannot apply the transition, “<new-value>” is used directly
(like if there was no transition going on). So, in fact, the only time the
browsers understands “<custom-transition>” is the first time at 0% to
initiate the transition and at 100% when it ends. But it can keep the info
about the algorithm and give this back to the StyleTransitionControllers
which could remap the transition to something the browser can understand.

-          A big advantage here is that if the transition should stop
(property updated again), the browser can use the output value of last frame
as the input value of the next frame and provide “transform:
transition(<last-intermediate-value>, <new-value-2>, 9s ease-out, 0%)” to
the controllers on the next frame. Browsers can also know how to rewind a
transition by decreasing the percentage.

 

I don’t know if this was clear. This is harder to explain than I initially
thought it would be, so don’t hesitate to ask for more info if anything
isn’t clear.

 

 

 

Don't gender the CSS engine, that's weird. ^_^ It's inanimate, and so "it"
is the correct pronoun. 

 

Sorry Tab. As you probably know, “it” doesn’t exist in French and every noun
has a gender. It’s a mistake you’ll see me doing a lot, please bear with it.
I promise I’ll pay extra attention to it, but there’s only so much attention
you can give to an email and this one certainly exceeded it :)
Received on Wednesday, 17 June 2015 18:55:06 UTC

This archive was generated by hypermail 2.3.1 : Wednesday, 17 June 2015 18:55:07 UTC