W3C home > Mailing lists > Public > www-style@w3.org > November 2013

RE: [shadow-dom] Update on Shadow DOM's interaction with CSS

From: François REMY <francois.remy.dev@outlook.com>
Date: Sun, 17 Nov 2013 20:32:31 +0100
Message-ID: <DUB406-EAS25927DCE94BF50D12D9E693A5E50@phx.gbl>
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

This archive was generated by hypermail 2.4.0 : Friday, 25 March 2022 10:08:37 UTC