RE: reaching the CSS parser

Adding missing references:

    [1] http://joshvarty.wordpress.com/2014/07/26/learn-roslyn-now-part-4-csharpsyntaxwalker/ or http://blogs.msmvps.com/matthieu/2014/05/24/roslyn-basis/ 
    [2] https://docs.google.com/presentation/d/1roCpgHDGnR_JiuZOOwGIgWPMF88cJvdbBdTlycuBOkA/present?slide=id.i0

_________________________________________________

Thanks Daniel for coming up with this list, I'm sure it will help guide the meeting you'll have in Sydney.

Given I won't be able to join you there to discuss this, please find here some of my thoughts on the matter for reference. Feel free to comment now, or leave this for later.


===========================================
1. parse a selector and get an object representing it 
===========================================

        On one hand, there seems to be a clear need for something along those lines. Even very common libraries like JQuery do have some internal parser for CSS (and, guess what, it sucks). On the other hand, understanding CSS is a moving target. We live in a moving world, and providing API parsing CSS is a two-headed sword: how can we guarantee later on that libraries relying on the functions will understand new constructs without failing? It seems to be possible to meet this criteria if we make sure the representation we give is sufficiently generic and safe & provides everything to the consumer, including helpers like "SelectorSegment.matches(element)" so that consumers may implement a visitor pattern and only act/override on things they are able to understand. To ensure this, the construction would have to be immutable and provide visitor helpers, in a way that is somehow similar to projects like Roslyn [1].

        In the meantime, I think projects like Tab Atkins' css parser moved the needle far enough to cover our needs here in a non-hacky way. That being said, see (4).


===========================================
2. extend querySelector() to namespaces 
===========================================

        Not a priority for me, but I get the point.



===========================================
3. parse a value and get an object representing it
===========================================

        While this one may look very similar to the (1), I see a major difference here. While parsing CSS in general became an easier task, and will probably remain so for a long time in large part thanks to subtle changes introduced in the way additions to css-syntax are now handled via the new spec (those are now simple consumers of the main api, and not direct extensions of it), values are a quickly moving target. Worse, changes in values affect older properties, and therefore shims written today will effectively become broken in subtle ways as css values evolve.

        Parsing the values of the CSS file and dealing with transitions is a significant part of the work for most css properties. API should be provided to make things easier in this area, and are more critical to polyfills than other object-model refinements.



===========================================
4. parse a complete stylesheet
===========================================

        Unfortunately, parsing stylesheets is something we will never be able to do. Polyfills right now are limited by CORS restriction. Exposing cssText on stylesheets won't solve this problem, and means that requiring polyfills to parse stylesheets is a non-starter for many use cases. 



===========================================
 5. access to unknown properties and values
===========================================

        It took me a while to sort this issue out. I learnt a lot from my past experience, and I'm now most skeptical than ever that this is something we really want to do.

        Now that we have custom properties, I got the conviction we have an amazing opportunity for a clean break. It's very easy to start coding using custom properties, and to just "map" those to the real properties if the browser happen to support them. Yet, using the real property directly and polyfill as-needed is an approach that has many issues. John Dalton has made a very good case about this in recent talks [2]: you actually want your code to rely on a layer translating your intent to the browser, even if you believe the browser could understand it directly, because if some browser happen to understand it partially or in a buggy way, your polyfill will not be able to step in as it will conflict with the native implementation (example: 'break-before' is supported in IE but not the 'region' value, which create issues impossible to solve with a polyfill, if cascade would apply normally-overidden valid values instead of 'invalid values' which get polyfilled). 

> Custom properties probably aren't sufficient to simulate non-inherited properties.

        This isn't entirely true as " * { --property: inherit } " is sufficient to recreate the expected behavior, if your consumer library understand that the "inherit" value requires you to get the value of the ancestor, recursively. I don't see this as a major concern at this point.

> They also probably aren't sufficient to simulate error handling 
> (which requires discarding invalid values and falling back to the 
> next value in the cascade).

        This is a more concerning issue. Which is the other side of the initial subject: instead of trying to make the browser deal with our stuff, we want it not to deal with stuff he could deal with. Deletion is often easier than addition (who can do more can do less). Another advantage is that we can perform such deletion at run-time, and not declaratively:

    CSSCascade.getSpecifiedStyle('--property', { filter: function valueFilter(e) { .... return true ... return false ... }  });

        A problem I don't see discussed here is the following one: how do you detect that the value of a property applied to an element changed. This is a major issue, which I believe can be solved using StyleMutationObserver. To the contrary of what the name meanse, the StyleMutationObserver has to deal with more than just value changes, but also in the case of custom properties "possible value changes" (because the browser doesn't know about the filters we use). This is dealt in my polyfills by logging every rule-matching change on an element if the rule contains a watched property. It's then up to the user code to call getSpecifiedStyle with the right filter and detect if a change happened or not.

        As a corollary, solving the property mutability issue gives you as a by-product as css selector live matcher (what I call querySelectorLive).



===========================================
 6. add a new pseudo-class or functional pseudo-class to the parser, attached to some JS code replying a boolean 
===========================================

        Brian has probably more input to provide about this than I have. 

        My feelings are that an API I would like to use is an API which would give me selectors of elements for which the custom pseudo-class matching-or-unmatching-state could be important (aka, in the case of "#prequel target.b:--is-valid sequel", event would be fire for "#prequel target" rule matching changes); then you can at any point set or clear the pseudo-states using "element.pseudoStates['--is-valid']=true/false" (for instance, you can setup events on the target, and reply to those events). 

        To make API like querySelector work, calling the function would force the MutationObserver related to those pseudo-states to be executed synchronously before the matching, like asking for "offsetWidth" forces the layout to happen in the same way.



===========================================
7. add a new property to the parser, to allow layout experiments through the box tree
===========================================

        I'm not sure what you mean by this, but I suspect you propose new custom-layout APIs which I also believe is a good thing.




Best regards,
François

Received on Thursday, 25 December 2014 14:30:17 UTC