[webcomponents] Relaxing @host to be more useful

Today, the @host rule contains style rules, and changes the context
that their selectors are processed in to be *solely* the host element
of the component.

In the recent meetup on our side, we discussed some limitations with
this model, and ways to fix it.  In particular:

* it would be nice to be able to style the *contents* of the shadow
DOM based on the host element.  For example, switching between color
themes based on an attribute put on the host element.
* it would be nice to be able to style the host element based on its
*surroundings* in the light DOM.  For example, a component laying out
differently when it finds itself in a nav list versus in the main
content of a page.

We came up with two changes to @host that would let us address these
scenarios gracefully, one addition and one (breaking) change.

First, the addition.  This addresses the first bullet point.
Currently, the spec already has a ::distributed() pseudo-element,
which applies to <content> elements and takes a selector, applying it
over the light-DOM elements distributed by that <content>.
Analogously, we propose adding a ::shadow() element, which applies to
host elements and takes a selector, applying it over the shadow DOM.

This is only valid within @host, which is only valid within the
component's own stylesheets - the outer page can't use it to
arbitrarily target the component's shadow DOM.

Second, the change.  This addresses the second bullet point.  We
propose that the content in which selectors within @host are evaluated
be widened to encompass the entire light DOM.  The @host rule itself
then invokes scoping rules, making all the selectors "scope-filtered"
<http://dev.w3.org/csswg/selectors4/#scope-filtered> with the host as
the scoping element.

In other words, if you want to generically target the host element,
just write "@host { :scope { ... } }".  If you want to target the host
based on an ancestor having class="foo", write "@host { .foo :scope {
... } }".  If you want to target all the <li>s among the light-DOM
descendants of the host, without a side trip through the shadow DOM
and back with ::distributed, just write "@host { li { ... } }".  (This
last usage can in many cases be done with ::distributed(), possible in
concert with ::shadow(), but our proposal makes the selectors much
neater for this case, and actually allows some things that are
impossible with ::distributed, like targeting all the even <li>
elements in the light DOM regardless of which <content> elements
picked them up.)

This is a minor breaking change, because a rule like "@host { * { ...
} }" (the current way to target the host without caring about what it
is) will now instead target the host *and* all of its descendants.
We're not worried about this breaking anyone now, but we should make
the change fast before it gets more painful in the future.

This also makes @host consistent with the @global rule we'll be adding
to scoped style sheets in general (once I sit down and write the
Scoped Styles spec), which shifts selectors in scoped stylesheets from
being scope-contained to scope-filtered, just with the added twist of
also shifting the context from shadow DOM back to light DOM.

~TJ

Received on Wednesday, 6 March 2013 23:04:10 UTC