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

>1) Real properties have a distinct computed value and used value computation
>phase, which both work differently. If we don't try to reproduce these 2
>phases with a fairly high similarity to how browsers do it for native properties,
>we will be quite severely limiting what you can do with custom properties and
>you wouldn't be able to emulate / polyfill what native properties do. I don't
>think this mechanism would count as a primitive in the extensible web
>manifesto sense, which is what I think we should be trying to expose here.

Yep, we stated as such in the spec as this is one of the drawbacks. I also wouldn't say that this is truly limiting as we state that you set a syntax that the engine will already understand, a good example of what this would provide is the capability to polyfill the often requested 'size' property using whatever computational setup you desired (I'll assume it has an aspect ratio so saying 100px, would result in a 100x100 box:

Ex:

apply: function(el) {
  el.outputStyle.width = el.style.get('--size');
 el.outputStyle.height = el.style.get('--size');
}

Granted you may want to allow two values or just one and handle them separately, all of which you could do. We don't disagree that it would be valuable to be able to setup custom computed values, but this is a good start. Especially since a lot (not all) of the polyfills are just giving support for new props.

>So I think we need 2 apply hooks: a computeHook and a useHook (or maybe
>layoutHook, or reflowHook...).
>
>2) For the computed value hook, a model of input and output makes sense,
>although I'd probably do it a bit differently.
>
>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. I'd add an
>(optional) flag for that.
>
>dictionary ComputeDescriptor
> {
>  ComputeCallback computeHook;
>  boolean? dependsOnParent;
>  sequence<DOMString>? inputProperties;
>  sequence<DOMString> outputProperties;
>};
>
>When dependsOnParent is true, the computed values of all properties on the
>parent element (recursively?) would but accessible through the
>ElementProxy, and not if it's false.

Valid point and worth discussing, this adds more complexity and I would like to have a good use case for this before we add it in. Can you provide an example of something you would like to polyfill that would need this behavior?

>We could live without this, always include ancestors, and you just don't look at
>things you don't need, but informing the browser about it should allow for
>parallelism opportunities that wouldn't be there otherwise. Also, not getting
>access to things you don't intend to use makes your code a bit more robust
>and easy to reason about.
>
>We could also refine it to try to express which properties on which ancestor
>you depend on, but that might be overkill.
>
>2.2) I would change this sentence from the explanation of inputProperties:
>> If this value is not null, then the apply function is only called for elements or
>pseudoelements on which the listed properties all have non-initial values.
>
>First, this sentence needs to be tweaked to take into account parent/ancestor
>elements.
>
>Also, this description makes it sound like this is a process that only happens
>one, but in fact, you'll need to call the hook every time one of the input
>property (or ancestor) changes, regardless of whether it's changing to or from
>an initial or non-initial value.

>Then, I would add a clause saying that if a function appears both in the output
>and input list, then the apply hook gets called even if all the input properties
>are at the initial value. Otherwise, you cannot have a property which has both:
> - dependencies on other properties for the computed value
> - the initial value computes to something other than itself regardless of
>dependencies
>
>This should be a fairly rare case, but I don't see why it would be impossible. I
>can't think of any existing property that has both of these, but each of these
>does happen, and there is no fundamental reason they cannot be combined.

Another valid point. Thanks!

>2.3) In addition to throwing if a property has already been registered for
>output, since you have enough information in the input and output list to do
>cycle detection, I would also throw an exception on any attempt to register a
>computed value apply hook that would introduce a loop.

Yep, that's what I was going to state when reading your above statement that we should fire all the time. I am also curious though if we should throw, or simply set an alert in the console as you may desire for a loop of some sort (thinking of custom animations in future versions when we figure out the computational part). I'll let Shane handle that though as I'm not the expert in animations.

>2.4) Since we're making this explicitely about computed values, the proxy
>element should not give access to its children.

>3) For the used value / layout / reflow hook, I don't think we can simply model
>things in terms of an input and output list of properties.
>
>If we look at how browsers do this for native properties, it's much more
>intricate than that. On each element, you have some sort of partial ordering in
>terms of which property is taken into account after which one, which is sort of
>like input/output dependencies, but also you need to know if this is a
>property whose used value is calculated while walking down the tree (like
>width), or one that is calculated while walking up the DOM (like height). And
>neither this up/down or input/output ordering are a static ones, they may
>change depending various things, or be intertwined. And sometimes you
>need multiple passes...
>
>I haven't though about this long enough to be sure how that can be solved,
>but I am pretty sure that the current proposal falls short of what we'd need to
>approach the expressivity of native properties.

Hmmmmm, again I'm not sure if this complexity is necessary for this stage. I'm not against this necessarily, I just would rather keep it simple until we hit a use case where someone can't accomplish a polyfill (that's truly needed) and can't be helped out by one of the other pieces of Houdini (namely custom Layout + Fragment tree).

I may be off base, and please correct me if I am misunderstanding the direction, but my expectation is to have a custom layout, that registers all of its custom properties and any handling of those for the cascade. But custom layout is where a lot of this workhorse stuff you suggest will happen, you'll iterate over the datastructure doing your layout (in example of height) and continuing to move down the trees creating your structure and updating the values as necessary. This will allow for your potential need for multipass, I just don't think it needs to be here; but as I said I could be wrong. 

>4) I think the problem you've called "Elephants in the room" in the spec is
>much more important to solve for the layout/reflow hook than for the
>computed value hook. Conflicts at computed value time should be pretty rare,
>even when mixing polyfills as you don't often need to affect the computed
>value of native properties. Not saying never, but I think that wouldn't be a
>major downside.

I plan to put an agenda item for the Houdini meeting in August with this title. There are problems that just exist based on how the platform currently works that we're having to work around that we should discuss. What things can we do, and what things should we do? The last thing I want to do is provide APIs that technically allow for full custom layout but do it in a hackish manner and potentially provide foot guns to developers. This is one of them, but I have a few others that I really want us to consider.

>On the other hand, it is probably essential that we find a way to avoid this
>conflict on the reflow hook, as modifying the value of native properties is
>what a custom property with a reflow hook would need to do in order to take
>effect.
>
>5) I don't know if we can express solve the reflow/layout hook well without
>first exposing the box tree / fragment tree.
>
>Let's say you're trying to use custom properties to polyfill hyphenation. You
>might be tempted to go fiddle with the dom to inject the hyphens and line
>breaks where you want them, but that would trigger a style recompilation
>followed by a reflow, and potential infinite loops. Even if you don't, it wouldn't
>be that simple to undo when the property is switched to another value.

Sure, as all of the pieces of the Houdini puzzle begin to take shape, we'll quickly realize that each one has merit on its own, but only when combined do you truly find its true power (I'm avoiding to write a cheezy Captain Planet cliché here :)  )

>Or let's say you're trying to polifyll regions. Trying to create new fragments by
>making new DOM elements would get you in the same mess.
>
>All in all, my conclusion is that this is way harder than I thought. Maybe I'm
>trying too hard to get close to what you can do with real properties, but I am
>afraid that if you don't do that, advanced polyfills, which should be users of
>this kind of features, won't be able to use this and will still need to reinvent
>everything manually.

We said this many times, and again I think this comes back to needing to discuss what fundamental issues exist in the platform that we may need other WGs to help out in solving to allow us to make this work how the underlying engine works. These may end up not being huge issues, but we should at the very least discuss them and how to address them.

Thanks again for taking the time to review the spec :)

> - Florian

Received on Friday, 12 June 2015 17:47:33 UTC