- From: François REMY <francois.remy.dev@outlook.com>
- Date: Sun, 17 Nov 2013 20:32:31 +0100
- To: "'Tab Atkins Jr.'" <jackalmage@gmail.com>, "'www-style list'" <www-style@w3.org>
Yes, yes, yes, and re-yes :-) You have my full support over this set of proposals (but I bet this isn't a real surprise to you). ± -----Message d'origine----- ± De : Tab Atkins Jr. [mailto:jackalmage@gmail.com] ± Envoyé : dimanche 17 novembre 2013 19:17 ± À : www-style list ± Objet : [shadow-dom] Update on Shadow DOM's interaction with CSS ± ± I just realized I haven't sent an update about shadow DOM in quite a while. ± Let's fix that! ± ± After extensive discussion, rejiggering, implementing, testing, and repeats of ± this cycle multiple times, we've finally settled on a syntax and ability set that ± seems to work right, without confusion, and neither adding too many or too ± little restrictions for real-world content. This has been stable for months ± after lots of banging and testing, so we're pretty confident about it now. ± ± This email is long, so here's a quick summary: ± ± 1. The "hat" combinator ^ pierces one shadow boundary, and then selects all ± descendants inside the shadow. ± 2. The "cat" combinator ^^ pierces arbitrary shadow boundaries, and is a ± descendant combinator at all levels. ± 3. Inside a shadow root, the host element is part of the tree, but defined to ± not match *any* selector except :host. (Including :not(*), if we can swing it.) ± 4. The :host(compound-selector) functional pseudo-class matches the host ± element iff it or an ancestor (all the way up to the document ± root) matches the selector. ± 5. The ::content pseudo-element switches into the distributed nodes for a ± given <content> element. ± 6. The :top pseudo-class matches elements without element parents. ± (This is a generalization of the :root concept, because CSSWG doesn't want to ± generalize :root itself.) ± ± As a reminder, shadow DOMs are, by default, sealed against outside CSS. ± They exist in a different context that outer-document selectors can't match ± into. (You can relax this, but it's not the default.) ± ± There are some cases where you need to have styles poke into a shadow ± DOM, though. Some of these cases can be solved by Variables, as they ± inherit into a shadow DOM by default. Just define a couple of theming ± variables that your component uses, and authors can set those on the ± component root. ± ± That's not enough, though. Sometimes you want to allow arbitrary styling of ± stuff inside a shadow DOM, but just want it to be intentional, rather than ± accidentally leaking in. Previously, we tried to do this with the ::part pseudo- ± element and manually declaring that certain elements in your shadow were ± surfaced as ::part, but this proved too limiting. Some use-cases demand ± arbitrary styling of everything, which is inconvenient. When using Shadow ± DOM for real applications, it also ended up being necessary to reach down ± through more than one shadow, as you sometimes refactor things into ± components for organization reasons rather than to enforce any strict ± separation. ± ± The solution we came up with several months ago and which has proved ± stable are two new combinators, affectionally named "hat" and "cat", and a ± few related features. ± ± ## Hat And Cat Combinators ## ± ± The "hat" or "shadow" combinator is spelled ^ and pierces one shadow ± boundary. "foo ^ bar" matches if and only if foo is an element hosting a ± shadow root, and bar is inside foo's shadow tree. Note that it matches all ± elements inside the shadow tree by default, like a descendant combinator; ± "foo ^ *" matches *every* element in the shadow tree. See :host, below, ± for how to select more precisely. This is the standard way to style the ± contents of a Web Component from the outside. ± ± The "cat" or "general shadow" combinator is spelled ^^ and pierces an ± arbitrary number of shadow boundaries. It also acts like a descendant ± combinator, but from the start: in "foo ^^ bar" foo doesn't have to be a ± component - it could have a descendant that's a component and holds bar. ± In other words, it's a standard descendant combinator, with the definition of ± "child" expanded to be "child, or contents of a shadow tree". This is a ± powerful, dangerous combinator, but it's proven necessary for lots of ± common use-cases. Without it, the Polymer folk just hacked around the lack ± in silly ways. It's most commonly used in the root document to select all ± components of a certain type, regardless of where they are in the tree, and ± style them in a certain way, like ":root ^^ foo-button ^ bar { ... }". (We tried ± to do something more controlled, where components surfaced their inner ± components with a manual switch, but it ended up being horrible and hard to ± use.) ± ± Both of these act similarly to the scoping cascading rules - styles from an ± outer tree win over styles from an inner tree. This is the opposite of how ± scoped styles work (which has inner scopes beating outer scopes), but the ± same when you instead consider it along a ± defaults->customization axis rather than an outer->inner axis. Outer ± trees only poke into inner trees when they're trying to override defaults. ± ± ## Selecting the Host Element ## ± ± Next, we had the problem of what to do with the host element. You want ± the component to be able to style its host element, but the host isn't actually ± inside the shadow tree. The host element is also "leaky" - you don't have ± control over its markup, so it might accidentally have the same class that ± you're using inside the component for something else, or even the same ID ± (IDs must be unique per-tree, but the host is in a different tree...). ± ± The solution we eventually came up with is that the host element does show ± up in the shadow tree for selecting purposes, but as a completely ± anonymous node. There is no way to select it with any selector except the ± :host pseudoclass. If it's acceptable, we'd like to avoid it being matched by ± :not(), too, but that's not a strict requirement. ± (For predictability reasons - if you switch from, say, "div, p, details {...} input ± {...}" to ":not(input) {...} input {...}" or something, we don't want the host ± element to suddenly get matched.) ± ± The :host pseudo-class matches the host element inside a shadow tree, and ± is the only way to do so. It serves no other purpose. Note that you can ± select down into the shadow tree from the host, like ":host > p" to find the ± top-level p elements. (You can also use the :top pseudo-class for this.) ± ± We also consistently found a need to be able to pass theming information ± *into* a component without doing the theming yourself. In other words, you ± need to be able to style your component differently based on some outside ± context. The solution we came up with is the ± :host() functional pseudo-class. ± ± :host(<compound-selector>) matches the host element if it, or one of its ± ancestors (hopping up shadow roots all the way to the document root), ± matches the compound selector. This way, you can do something like placing ± class="light-theme" on some element in your document, and any ± components that know about that class can switch their styling with ± ":host(.light-theme) p { background: white; }" etc. :host() (no ± argument) is identical to :host. Note that the host element is not anonymous ± while being matched against this selector. ± ± There are some use-cases that want to detect classes/attrs/tagname/etc on ± the host element itself, not one of its ancestors. This can be done by ± combining the two forms, in a slightly awkward but workable ± fashion: while ":host(.foo)" matches if the host element or one of its ± ancestors has a "foo" class, ":host(.foo:host)" matches only if the host ± element itself has a "foo" class. ± ± ## Selecting Distributed Elements ## ± ± This is an older feature that's been around for a while, but under a different ± name. When you distribute light-dom elements into a shadow DOM with the ± <content> element, those elements still aren't actually part of the shadow ± tree, and for good reason. But you do sometimes want to style them, so you ± need a way to select them. Previously, we had a ::distributed pseudo- ± element hanging off of <content> elements, but that wasn't a great name ± (Daniel complained about it in a thread). ± We've renamed it to ::content. ± ± ## Selecting "Top-level" Elements ## ± ± We've ended up exposing two different trees (shadow tree, and distributed ± tree inside <content>) whose root is not an element. This makes it difficult ± to select "top-level" elements, or do any other selection that wants to be ± rooted from the top. ± ± To fix this, we propose a :top pseudo-class that matches elements without ± element parents. This matches the "top-level" elements distributed by ± <content> (in that context, their parent is the distribution root, or whatever ± it's called), and the top-level elements inside a shadow tree. ± ± (Shadow-tree top-level elements do technically have a non-element shadow ± root parent, but note that :host also acts like it's a root for all of them. We ± feel this is okay.) ± ± ~TJ
Received on Sunday, 17 November 2013 19:33:08 UTC