Re: Shadow tree style isolation primitive

On Fri, Jan 9, 2015 at 5:40 AM, Anne van Kesteren <annevk@annevk.nl> wrote:
> I'm wondering if it's feasible to provide developers with the
> primitive that the combination of Shadow DOM and CSS Scoping provides.
> Namely a way to isolate a subtree from selector matching (of document
> stylesheets, not necessarily user and user agent stylesheets) and
> requiring a special selector, such as >>>, to pierce through the
> boundary.
>
> This is a bit different from the `all` property as that just changes
> the values of all properties, it does not make a selector such as
> "div" no longer match.
>
> So to be clear, the idea is that if you have a tree such as
>
>   <section class=example>
>     <h1>Example</h1>
>     <div> ... </div>
>   </section>
>
> Then a simple div selector would not match the innermost div if we
> isolated the section. Instead you would have to use section >>> div or
> some such. Or perhaps associate a set of selectors and style
> declarations with that subtree in some manner.

It's probably feasible, sure.  But I'm not sure that it's necessary,
or that browsers will go for it.  Using a shadow root as the isolation
boundary is *really convenient*, because it's a separate tree
entirely; the fact that outside rules don't apply within it, and
inside rules don't apply outside, falls out for free.

Let's assume we did it, though.  We'd have to have some mechanism for
defining an isolation boundary, and denoting whether rules were
"inside" or "outside" the boundary.  This sounds like an at-rule,
like:

@isolate .example {
  h1 { ... }
  div { ... }
}

Now, a problem here is that you have a conflict between nesting
isolated things and specifying isolation.  Say you have <foo> and
<bar> elements, both of which need to be isolated. You'd think you
could just write:

@isolate foo {
  ...
}
@isolate bar {
  ...
}

But this won't work! If you have markup like
<foo><bar>...</bar></foo>, the <bar> there is inside the <foo>'s
isolation boundary, so the @isolate rule can't find it.  You'd need to
*also* nest the "@isolate bar" rule (and all its styling rules) within
the foo one, and vice versa.  The effect of this on *three* mutually
isolated components is, obviously, terrible; let's not even mention
trying to use multiple modules together that weren't explicitly
designed together.

Alternately, say that it does work - the @isolate selector pierces
through isolation boundaries.  Then you're still screwed, because if
the outer page wants to isolate .example blocks, but within your
component you use .example normally, without any isolation, whoops!
Suddenly your .example blocks are isolated, too, and getting weird
styles applied to them, while your own styles break since they can't
cross the unexpected boundary.

Basically, trying to smuggle private state into a global declarative
language is a bitch.

So, CSS is out.  We can't reasonably do this within the confines of
CSS application.  It needs to be handled at a different layer.  We
could do it in HTML, potentially - some new global attribute that
creates a styling boundary that prevents outside styling from
targeting anything inside.  Then you can just use standard <style
scoped> to apply your own styles within the boundary - as long as the
scoping root is inside the boundary, styling is allowed.

But that means you have to add an attribute to every element that uses
this styling boundary, and move your style info into inline scoped
blocks.  That's annoying. :/

Let's check out JS. If you can mark some elements as always being
styling boundaries, then whenever they're constructed, whether
manually or via the parser, they'll get the right mechanics
automatically.  And since this is JS, it shouldn't be too hard to say
"always attach this stylesheet to the element whenever it gets
created", or perhaps introduce some explicit ability to do this in the
platform.

This last one, though, is pretty much exactly Custom Elements, just
with the children staying in the light tree rather than being moved
into a shadow tree.  But keeping them in the light tree has
complications; it means that everything in the platform needs to be
made aware of the isolation boundary.  Should qSA respect the
isolation boundaries or not?  Depends on what you're using it for.
What about things that aren't CSS at all, like getElementsByTagName()?
 That's equivalent to a qSA with the same argument, but it's not a
"selector", per se.  Manual tree-walking would also need to be made
aware of this, or else you might accidentally descend into something
that wants isolation.  Shadow DOM at least gives an answer to all of
these, by putting the elements in a separate tree.  You don't need to
think of every one individually, or deal with inconsistent design when
someone forgets to spec their new tree-searching thing to respect the
boundary.

So, do you still think it's worth it to try to subdivide the
functionality further? I think it's packaged in a reasonable way at
the moment.

~TJ

Received on Monday, 12 January 2015 21:29:31 UTC