- From: Tab Atkins Jr. <jackalmage@gmail.com>
- Date: Thu, 19 Sep 2013 16:09:04 -0700
- To: Dimitri Glazkov <dglazkov@google.com>
- Cc: public-webapps <public-webapps@w3.org>, Steve Orvell <sorvell@google.com>, Scott Miles <sjmiles@google.com>, Blake Kaplan <mrbkap@mozilla.com>, Elliott Sprehn <esprehn@gmail.com>, Dominic Cooney <dominicc@google.com>, William Chen <wchen@mozilla.com>, fantasai <fantasai@inkedblade.net>, David Baron <dbaron@dbaron.org>
On Mon, Sep 9, 2013 at 4:32 PM, Dimitri Glazkov <dglazkov@google.com> wrote:
> Part 1: Revenge of the :host
>
> Turns out, it's bad to be Super Man. After the Shadow DOM meetup,
> where we decided that shadow host could be matched by both outer and
> inner trees (http://lists.w3.org/Archives/Public/public-webapps/2013AprJun/0985.html,
> first point), we quickly coded this up in Blink and gave it to Mikey
> ...erm, the Polymer folks to chew on.
>
> The folks spat out that morsel right across the table
> (https://www.w3.org/Bugs/Public/show_bug.cgi?id=22980), and presented
> good arguments to justify their etiquette faux pas.
>
> For what it's worth, it would be fairly easy to make shadow tree rules
> match shadow host only when ":host" is present in a rule.
>
> Unfortunately, this would leave Tab (and other CSS WG folks) in a sad
> state, since addressing these arguments makes it harder to keep a
> straight face with the concept of a pseudo class in regard to :host.
> See discussion on bug for the gory details.
>
> As of now, we are in that angsty state of not knowing what to do next.
> Any ideas are appreciated. Note that there are some well-established
> concepts in CSS and inventing fewer new concepts is much preferred.
> Reuse, reduce, recycle.
Some in-person discussion led to an elegant solution: keep the host
element in the set of elements, but neuter all of its features. As far
as the selectors in the shadow root can see, it's a node with no
tagname, no id, no classes, etc. The one and only thing that will
match it is :host and :host(). (And *, of course.)
Also, just in case it wasn't clear earlier, :host() only matches the
host element. Like :drop/:drop(), the non-function version is just
the "widest possible" version of the functional version - it always
matches the host, while ":host(sel)" only matches the host if
something in the ancestor tree matches "sel".
Here's an example:
<div class="foo">
<x-dialog>
[shadow-root]
<div class="bar">some text!</div>
<style>
div { ... } // matches the div.bar
:host { ... } // matches the x-dialog
x-dialog { ... } // matches nothing...
// there's no node named "x-dialog" viewable from here
</style>
</x-dialog>
</div>
> Part 2: Party ::part part
>
> Another possible wrinkle is the ::part pseudo element. After also
> chewing on ::part for a little while, our brave guinea pi.. erm,
> people also declared it to be tasting somewhat bitter.
>
> The best symptom can be seen here:
> https://www.w3.org/Bugs/Public/show_bug.cgi?id=23162. When explained
> that you would just chain x-nurk::part(woot)::part(zorp)::part(bler)
> to cross each shadow tree boundary, the guinea people looked at me
> like this: <_<. Then they pointed out that this both:
>
> a) implies ever-growing "part" API for each component, which will
> quickly lead to the anti-pattern of developers simply declaring all
> elements in their shadow tree as parts, and
>
> b) just looks ugly and constipated.
>
> Agitated shouts of "Y U NO LET US JUS DO EET" were echoing across the
> San Francisco Bay, frightening America's Cup spectators.
>
> To calm the brave guinea people down, I showed them a magic trick. Out
> of my sleeve, I pulled out two new combinators: A hat (^) and a cat
> (^^).
>
> You would use them instead of ::part. The hat is generally equivalent
> to a descendant combinator, except it crosses 1 (one) shadow tree
> boundary (from shadow host to shadow root). The cat is similar, except
> it crosses any number of boundaries. So, to target "bler" in the
> previous part-y chain could be written as simply as
> x-nurk^^[part=bler] or x-nurk^^#bler if ids are used instead of
> part="bler" attribute. Respectively, you would target "woot" as simply
> x-nurk^#woot.
>
> But wait there's more: you could use these new combinators in
> querySelector, I proclaimed! In the nascent shadow DOM code, we
> already started seeing the blood-curling
> document.querySelector('x-nurk').shadowRoot.querySelector('#woot').shadowRoot.querySelector('#zorp')
> chains of hell -- a problem that these new combinators would solve.
>
> Think of them simply general combinators that opens shadow trees for
> selector traversal, just like Element.shadowRoot did for DOM
> traversal.
>
> The brave guinea people became content and reverted to their natural
> behaviors, but I then started worrying. Did I over-promise and finally
> ruined encapsulation? When will our styling woes finally converge into
> one solution?
>
> Luckily, I have you, my glorious WebApp-erators. Put on your thinking
> hats and help find one uniform solution. Something that fits well into
> CSS, doesn't add too many new moving parts, and keeps the brave guinea
> people at bay. That'll be the day.
In-person discussion convinced me of the use of this. Basically, we
should go ahead and expose a nice, wide feature now, rather than
trying to expose one that is locked down *just right*. We can add
more features that are harder to misuse in the future, once we have a
better idea of the common usage patterns.
So, to be clearer, we'd be adding two new combinators, "^" and "^^".
"^", the shadow combinator (or "hat"), pierces the upper-boundary
encapsulation of a shadow root. The LHS has to be a component,
something with an actual shadow root. "^^", the "all shadows"
combinator (or "cat", or "double-hat"), is identical, but it pierces
*all* shadow root upper boundaries.
Here's another example:
<div class="foo">
<style>
x-dialog ^ .bar { ... } // matches
x-dialog ^^ .bar { ... } // matches
x-dialog ^ .baz { ... } // doesn't match - behind a second SR boundary
x-dialog ^ x-button ^ .baz { ... } // matches
x-dialog ^^ .baz { ... } // matches
.foo ^^ .baz { ... } // doesn't match - div.foo has no SR
</style>
<x-dialog>
[shadow-root]
<div class="bar">some text!</div>
<style>
x-button ^ .baz { ... } // matches
x-button ^^ .baz { ... } // matches
</style>
<x-button>
[shadow-root]
<div class="baz">more text!</div>
</x-button>
</x-dialog>
</div>
Tangentially related to this, it came up that several of these
use-cases want to be able to target an element that is a direct child
of a shadow root, but there's no way to indicate that in selectors
currently. Similar things have come up in other contexts, such as
running .query() on a DocumentFragment. The CSSWG has consistently
refused to expand the meaning of :root, so we figured we could just
introduce another pseudo-class called :top or something, which matches
any element whose parent is not an element.
~TJ
Received on Thursday, 19 September 2013 23:09:52 UTC