<pre class='metadata'>
Title: CSS Shadow Parts
Shortname: css-shadow-parts
Level: 1
Group: CSSWG
Status: ED
Work Status: exploring
URL: http://drafts.csswg.org/css-shadow-parts/
TR: https://www.w3.org/TR/css-shadow-parts-1/
Editor: Tab Atkins-Bittner, Google, http://xanthir.com/contact/, w3cid 42199
Editor: Fergal Daly, Google, fergal@chromium.org
Abstract: This specification defines the ''::part()'' pseudo-element on <a>shadow hosts</a>, allowing <a>shadow hosts</a> to selectively expose chosen elements from their <a>shadow tree</a> to the outside page for styling purposes.
</pre>

<pre class=link-defaults>
spec:selectors-4;
    type:selector; text::hover
    type:dfn; text:live profile
    type:dfn; text:structural pseudo-class
spec:dom; type:dfn; for:/; text:shadow root
spec:infra;
    type:dfn;
        text:string
        text:list
</pre>

Introduction {#intro}
=====================

Shadow DOM allows authors to separate their page into "components",
subtrees of markup whose details are only relevant to the component itself,
not the outside page.
This reduces the chance of a style meant for one part of the page
accidentally over-applying and making a different part of the page look wrong.
However, this styling barrier also makes it harder for a page to interact with its components
when it actually <em>wants</em> to do so.

This specification defines the ''::part()'' pseudo-element,
which allows an author to style specific, purposely exposed elements in a <a>shadow tree</a>
from the outside page's context.
In combination with <a>custom properties</a>,
which let the outside page pass particular values
(such as theme colors)
into the component for it to do with as it will,
these pseudo-elements allow components and the outside page
to interact in safe, powerful ways,
maintaining encapsulation
without surrendering all control.

Motivation {#motivation}
------------------------

For custom elements to be fully useful and as capable as built-in elements
it should be possible for parts of them to be styled from outside.
Exactly what can be styled from outside should be controlled by the element author.
Also, it should be possible for a custom element to present a stable "API" for styling.
That is, the selector used to style a part of a custom element
should not expose or require knowledge of the internal details of the element.
The custom element author should be able to change the internal details of the element
while leaving the selectors untouched.

The previous proposed method for styling inside the shadow tree,
the >>> combinator,
turned out to be <em>too powerful</em> for its own good;
it exposed too much of a component's internal structure to scrutiny,
defeating some of the encapsulation benefits that using Shadow DOM brings.
For this,
and other performance-related reasons,
the >>> combinator was eventually removed from the <a>live profile</a>.

This left us with using <a>custom properties</a> as the only way to style into a shadow tree:
the component would advertise that it uses certain <a>custom properties</a> to style its internals,
and the outer page could then set those properties as it wished on the <a>shadow host</a>,
letting inheritance push the values down to where they were needed.
This works very well for many simple theming use-cases.

However, there are some cases where this falls down.
If a component wishes to allow arbitrary styling of something in its shadow tree,
the only way to do so is to define hundreds of <a>custom properties</a>
(one per CSS property they wish to allow control of),
which is obviously ridiculous
for both usability and performance reasons.
The situation is compounded if authors wish to style the component differently
based on pseudo-classes like '':hover'';
the component needs to duplicate the <a>custom properties</a> used
for each pseudo-class
(and each combination,
like '':hover:focus'',
resulting in a combinatorial explosion).
This makes the usability and performance problems even worse.

We introduce ''::part()'' to handle this case much more elegantly and performantly.
Rather than bundling everything into <a>custom property</a> names,
the functionality lives in selectors and style rule syntax,
like it's meant to.
This is far more usable for both component authors
and component users,
should have much better performance,
and allows for better encapsulation/API surface.

It's important to note that ''::part()''
offers <em>absolutely zero new theoretical power</em>.
It is not a rehash of the ''>>>'' combinator,
it is simply a more convenient and consistent syntax
for something authors can already do with <a>custom properties</a>.
By separating out the explicitly "published" parts of an element
(the <a>part element map</a>)
from the sub-parts that it merely happens to contain,
it also helps with encapsulation,
as authors can use ''::part()'' without fear of accidental over-styling.


Exposing a Shadow Element: {#exposing}
=============================================================
Elements in a <a>shadow tree</a> may be exported for styling by stylesheets outside the tree
using the part and exportparts attributes.

Each element has a <dfn export for="element">part name list</dfn>
which is an [=ordered set=] of tokens.

Each element has a <dfn export for="element">forwarded part name list</dfn>
which is a [=list=] of [=pairs=]
containing a [=string=] for the inner part being forwarded
and a [=string=] giving the name it will be exposed as.

Each <a>shadow root</a> can be thought of as having a <dfn export for="shadow root">part element map</dfn>
with keys that are [=strings=]
and values that are [=ordered sets=] of elements.

The [=part element map=] is described
only as part of the algorithm for calculating style in this spec.
It is not exposed via the DOM,
as calculating it may be expensive
and exposing it could allow access to elements inside closed shadow roots.

[=Part element maps=] are affected by the addition and removal of elements
and changes to the [=part name lists=] and [=forwarded part name lists=] of elements in the DOM.

<div algorithm>
	To <dfn>calculate the [=part element map=]</dfn> of a shadow root, |outerRoot|:

	1. For each [=descendant=] |el| within |outerRoot|:
		1. For each |name| in |el|'s [=part name list=],
			[=list/append=] |el| to |outerRoot|'s [=part element map=][|name|].
		2. If |el| is a [=shadow host=] itself
			then let |innerRoot| be its shadow root.
		3. [=calculate the part element map|Calculate=] |innerRoot|'s [=part element map=].
		4. For each |innerName|/|outerName| in |el|'s [=forwarded part name list=]:
			1. Let |innerParts| be |innerRoot|'s [=part element map=][|innerName|]
			2. [=list/Append=] the elements in |innerParts|
				to |outerRoot|'s [=part element map=][|outerName|]
</div>


Naming a Shadow Element: the <{html-global/part}> attribute {#part-attr}
------------------------------------------------------------------------

Any element in a shadow tree can have a <dfn element-attr for=html-global>part</dfn> attribute.
This is used to expose the element outside of the <a>shadow tree</a>.

The part attribute is parsed as a space-separated list of tokens representing the part names of this element.

Note: It's okay to give a part multiple names.
The "part name" should be considered similar to a class,
not an id or tagname.

<pre class="example">
&lt;style&gt;
  c-e<b>::part(textspan)</b> { color: red; }
&lt;/style&gt;

&lt;template id="c-e-template"&gt;
  &lt;span <b>part="textspan"</b>&gt;This text will be red&lt;/span&gt;
&lt;/template&gt;
&lt;c-e&gt;&lt;/c-e&gt;
&lt;script&gt;
  // Add template as custom element c-e
  ...
&lt;/script&gt;
</pre>

Forwarding a Shadow Element: the <{html-global/exportparts}> attribute {#exportparts-attr}
----------------------------------------------------------------------------------
Any element in a shadow tree can have a <dfn element-attr for=html-global>exportparts</dfn> attribute.
If the element is a shadow host,
this is used to allow styling of parts from hosts inside the <a>shadow tree</a>
by rules outside this the <a>shadow tree</a>
(as if they were elements in the same tree as the host,
named by a part attribute).

The exportparts attribute is parsed as a comma-separated list of part mappings.
Each part mapping is one of:

<dl class=switch>
    : <code>innerIdent : outerIdent</code>
    :: Adds <code>innerIdent</code>/<code>outerIdent</code> to el's <a>forwarded part name list</a>.

    : <code>ident</code>
    :: Adds <code>ident</code>/<code>ident</code> to el's <a>forwarded part name list</a>.

		Note: This is shorthand for <code>ident : ident</code>.

    : anything else
    :: Ignored for error-recovery / future compatibility.
</dl>

Note: It's okay to map a sub-part to several names.

<pre class="example">
&lt;style&gt;
  c-e<b>::part(textspan)</b> { color: red; }
&lt;/style&gt;

&lt;template id="c-e-outer-template"&gt;
  &lt;c-e-inner <b>exportparts="innerspan: textspan"</b>&gt;&lt;/c-e-inner&gt;
&lt;/template&gt;

&lt;template id="c-e-inner-template"&gt;
  &lt;span <b>part="innerspan"</b>&gt;
    This text will be red because the containing shadow
    host forwards <b>innerspan</b> to the document as "textspan"
    and the document style matches it.
  &lt;/span&gt;
  &lt;span <b>part="textspan"</b>&gt;
    This text will not be red because <b>textspan</b> in the document style
    cannot match against the part inside the inner custom element
    if it is not forwarded.
	&lt;/span&gt;
&lt;/template&gt;

&lt;c-e&gt;&lt;/c-e&gt;
&lt;script&gt;
  // Add template as custom elements c-e-inner, c-e-outer
    ...
&lt;/script&gt;
</pre>

Selecting a Shadow Element: the ''::part()'' pseudo-element {#part}
============================================================================================

The <dfn selector>::part()</dfn> pseudo-element
allows you to select elements that have been exposed via a <{html-global/part}> attribute.
The syntax is:

<pre class=prod>
    ::part() = ::part( <<ident>>+ )
</pre>

The ''::part()'' pseudo-element only matches anything
when the <a>originating element</a> is a <a>shadow host</a>.
If the <a>originating element's</a> <a>shadow root's</a> <a>part element map</a>
[=map/contains=] all of the specified <<ident>>s,
''::part()'' matches the element or elements keyed to that <<ident>>.
Otherwise, it matches nothing.

<div class="example">
    For example,
    if you have a custom button
    that contains a "label" element that is exposed for styling
    (via <code>part="label"</code>),
    you can select it with
    ''#the-button::part(label)''.
</div>

<div class="example">
	Part names act similarly to classes:
	multiple elements can have the same part name,
	and a single element can have multiple part names.

	A tabstrip control might have multiple elements
	with <code>part="tab"</code>,
	all of which are selected by ''::part(tab)''.

	If a single tab is active at a time,
	it can be specially indicated with <code>part="tab active"</code>
	and then selected by ''::part(tab active)''
	(or ''::part(active tab)'', as order doesn't matter).
</div>

The ''::part()'' pseudo-element can take additional pseudo-classes after it,
such as ''x-button::part(label):hover'',
but never matches the <a>structural pseudo-classes</a>
or any other pseudo-classes that match based on tree information
rather than local element information.

The ''::part()'' pseudo-element can also take additional pseudo-elements after it,
such as ''x-button::part(label)::before'',
but never match additional ''::part()''s.

<div class=example>
    For example,
    ''x-panel::part(confirm-button)::part(label)''
    never matches anything.
    This is because doing so would expose more structural information
    than is intended.

    If the <code>&lt;x-panel></code>'s internal confirm button had used something like
    <code>part="label => confirm-label"</code>
    to forward the button's internal parts up into the panel's own <a>part element map</a>,
    then a selector like
    ''x-panel::part(confirm-label)''
    would select just the one button's label,
    ignoring any other labels.
</div>

Extensions to the {{Element}} Interface {#idl}
==============================================

<pre class=idl>
partial interface Element {
  [SameObject, PutForwards=value] readonly attribute DOMTokenList part;
};
</pre>

The part attribute’s getter must return a DOMTokenList object
whose associated element is the context object
and whose associated attribute’s local name is part.
The token set of this particular DOMTokenList object are also known as the element’s parts.

Issue(w3c/csswg-drafts#3424): Define this as a superglobal in the DOM spec.

Microsyntaxes for parsing {#parsing}
==============================================

Rules for parsing part mappings {#parsing-mapping}
----------------------------------------------

A <dfn export>valid part mapping</dfn> is a [=pair=] of tokens
separated by a U+003A COLON character
and any number of space characters before or after the U+003A COLON
The tokens must not contain U+003A COLON or U+002C COMMA characters.

The rules for parsing a part mapping are as follows:

1. Let <var>input</var> be the string being parsed.

1. Let <var>position</var> be a pointer into <var>input</var>, initially pointing at the start of the string.

1. [=Collect a sequence of code points=] that are space characters
1. [=Collect a sequence of code points=] that are not space characters or U+003A COLON characters,
    and let <var>first token</var> be the result.
1. If <var>first token</var> is empty then return error.
1. [=Collect a sequence of code points=] that are space characters.
1. If the end of the <var>input</var> has been reached, return the [=pair=] <var>first token</var>/<var>first token</var>
1. If character at <var>position</var> is not a U+003A COLON character, return error.
1. Consume the U+003A COLON character.
1. [=Collect a sequence of code points=] that are space characters.
1. [=Collect a sequence of code points=] that are not space characters or U+003A COLON characters.
    and let <var>second token</var> be the result.
1. If <var>second token</var> is empty then return error.
1. [=Collect a sequence of code points=] that are space characters.
1. If <var>position</var> is not past the end of <var>input</var> then return error.
1. Return the [=pair=] <var>first token</var>/<var>second token</var>.

Rules for parsing a list of part mappings {#parsing-mapping-list}
----------------------------------------------

A <dfn export>valid list of part mappings</dfn> is a number of valid part mappings
separated by a U+002C COMMA character
and any number of space characters before or after the U+002C COMMA

The rules for parsing a list of part mappings are as follow:

1. Let <var>input</var> be the string being parsed.

1. <span data-x="split a string on commas">Split the string <var>input</var> on
    commas</span>. Let <var>unparsed mappings</var> be the resulting list of strings.

1. Let <var>mappings</var> be an initially empty [=list=] of [=pairs=] of tokens.
    This [=list=] will be the result of this algorithm.

1. For each string <var>unparsed mapping</var> in <var>unparsed mappings</var>,
    run the following substeps:

    1. If <var>unparsed mapping</var> is empty or contains only space characters,
        continue to the next iteration of the loop.
    1. Let <var>mapping</var> be the result of parsing <var>unparsed mapping</var>
        using the <span>rules for parsing part mappings</span>.
    1. If <var>mapping</var> is an error then continue to the next iteration of the loop.
        This allows clients to skip over new syntax that is not understood.
    1. Append <var>mapping</var> to <var>mappings</var>.
