Shadow DOM

W3C Working Draft 14 May 2013

This version
http://www.w3.org/TR/2013/WD-shadow-dom-20130514/
Latest version
http://www.w3.org/TR/shadow-dom/
Latest editor's draft
http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html
Previous version
http://www.w3.org/TR/2012/WD-shadow-dom-20121016/
Revision history
https://dvcs.w3.org/hg/webcomponents/log/tip/spec/shadow/index.html
Participate
Discuss on public-webapps@w3.org (Web Applications Working Group)
File bugs (w3.org's Bugzilla)
Editor
Dimitri Glazkov, Google, <>

Abstract

This specification describes a method of establishing and maintaining functional boundaries between DOM trees and how these trees interact with each other within a document, thus enabling better functional encapsulation within the DOM.

Status of This Document

This section describes the status of this document at the time of its publication. Other documents may supersede this document. A list of current W3C publications and the latest revision of this technical report can be found in the W3C technical reports index at http://www.w3.org/TR/.

This document was published by the Web Applications Working Group as a Working Draft. If you wish to make comments regarding this document, please send them to public-webapps@w3.org (subscribe, archives). All feedback is welcome.

Publication as a Working Draft does not imply endorsement by the W3C Membership. This is a draft document and may be updated, replaced or obsoleted by other documents at any time. It is inappropriate to cite this document as other than work in progress.

This document was produced by a group operating under the 5 February 2004 W3C Patent Policy. W3C maintains a public list of any patent disclosures made in connection with the deliverables of the group; that page also includes instructions for disclosing a patent. An individual who has actual knowledge of a patent which the individual believes contains Essential Claim(s) must disclose the information in accordance with section 6 of the W3C Patent Policy.

Table of Contents

  1. 1 About this Document
  2. 2 Dependencies
  3. 3 Introduction
    1. 3.1 Functional Encapsulation Example
  4. 4 Shadow Trees
    1. 4.1 Upper-boundary Encapsulation
    2. 4.2 Lower-boundary Encapsulation
    3. 4.3 Satisfying Matching Criteria
    4. 4.4 ::distributed() pseudo-element
    5. 4.5 Hosting Multiple Shadow Trees
    6. 4.6 Reprojection
    7. 4.7 Composition
    8. 4.8 Nested Shadow Trees
    9. 4.9 Rendering Shadow Trees
    10. 4.10 Custom Pseudo-elements
  5. 5 Events
    1. 5.1 Event Retargeting
    2. 5.2 Retargeting relatedTarget
    3. 5.3 Retargeting Focus Events
    4. 5.4 Events that are Always Stopped
    5. 5.5 Event Dispatch
    6. 5.6 Event Retargeting Example
  6. 6 Styles
    1. 6.1 CSS Variables
    2. 6.2 text-decoration Property
    3. 6.3 @host @-rule
  7. 7 User Interaction
    1. 7.1 Ranges and Selections
    2. 7.2 Focus Navigation
    3. 7.3 Active Element
    4. 7.4 Editing
    5. 7.5 Assistive Technology
  8. 8 HTML Elements in Shadow Trees
    1. 8.1 Inert HTML Elements
    2. 8.2 HTML Forms
  9. 9 HTML Elements and Their Shadow Trees
  10. 10 Elements and DOM Objects
    1. 10.1 ShadowRoot Object
      1. 10.1.1 ShadowRoot Attributes
      2. 10.1.2 ShadowRoot Methods
    2. 10.2 Extensions to Element Interface
      1. 10.2.1 Attributes
      2. 10.2.2 Methods
    3. 10.3 CSSHostRule Interface
      1. 10.3.1 CSSHostRule Attributes
      2. 10.3.2 CSSHostRule Methods
    4. 10.4 The content HTML element
    5. 10.5 The shadow HTML element
  11. 11 Shadow DOM Example
  12. Acknowledgements

About this Document

All diagrams, examples, notes, are non-normative, as well as sections explicitly marked as non-normative. Everything else in this specification is normative.

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in the normative parts of this document are to be interpreted as described in RFC2119. For readability, these words do not appear in all uppercase letters in this specification.

To help with layering and to avoid circular dependencies between various parts of specification, this document consists of three consecutive narratives:

  1. setting up the stage for the specification,
  2. explaining of the conceptual model and algorithms behind it, and
  3. expressing this model with DOM interfaces and HTML elements.

In a sense, these parts can be viewed as math, which sets up the reasoning environment, physics, which is the theoretical reasoning about the concept, and mechanics, which is the practical application of this reasoning.

Any point, at which a conforming UA must make decisions about the state or reaction to the state of the conceptual model, is captured as algorithm. The algorithms are defined in terms of processing equivalence. The processing equivalence is a constraint imposed on the algorithm implementors, requiring the output of the both UA-implemented and the specified algorithm to be exactly the same for all inputs.

Dependencies

This document relies on the following specifications:

Introduction

Web application developers often encounter the need to provide encapsulation within a DOM tree. Despite being part of one document tree (a tree that has document as its root), there are typically many functional tree fragments, as well as assumptions about these fragments operating independently. This specification calls this type of encapsulation a functional encapsulation, as opposed to trust encapsulation, which deals with limiting information flow based on trust and ensuring security of data and state within an application.

Functional encapsulation is primarily concerned with establishing functional boundaries in a document tree. A functional boundary (or just boundary hereon) is a delineation of functional concerns between two loosely coupled units of functionality.

Functional Encapsulation Example

A Web application user interface is commonly composed of several user interface elements (or widgets), each functionally its own tree. In cases where a widget is tasked with hosting other widgets, the need arises for the widget to understand where its tree ends and another widget's tree begins.

This need for observing the functional boundaries in a document tree is even larger when a widget is operated on—added, moved, or removed in the document tree—by an outside actor, such as the Web application that consumes these widgets. Unless the widget consumer knows exactly how a widget's tree is designed, it is impossible for the consumer to reasonably operate on the widget. A typical workaround has been providing alternative means of operation by the widget developer, which, in striving for API consistency quickly extrapolates into a complete set of widget-specific, DOM-like APIs.

Shadow Trees

To solve this problem at its core, a new abstraction is introduced. The shadow DOM allows multiple DOM trees (in addition to the document tree) to be composed into one larger tree when rendered. The existence of multiple DOM trees is enabled by letting any element in the document tree to host one or more additional DOM trees. These shadow trees are governed by a set of rules that establish encapsulation boundaries while retaining the standard DOM composability semantics.

The encapsulation boundaries between the document tree and shadow trees are called shadow boundaries. The elements that host shadow trees are called shadow hosts, and the roots of the shadow trees are called shadow roots.

When rendered, the shadow tree takes place of the shadow host's content.

To enable composition of shadow host's children and the shadow tree, a notion of insertion points is added to the abstraction. An insertion point is a defined location in the shadow tree, to which the shadow host's children are transposed when rendering. The mechanism that determines which shadow host's children are transposed into which insertion points is called distribution.

Thus, the encapsulation of a shadow tree can be viewed as a two-fold problem:

  1. the upper-boundary encapsulation, or governing the boundary between the shadow root and the shadow host; and
  2. the lower-boundary encapsulation, or governing the boundary between the insertion points and the shadow host's children.

Upper-boundary Encapsulation

To maintain the upper-boundary encapsulation, the following scoping constraints must apply to all nodes in a shadow tree:

For convenience, the shadow root provides its own set of DOM tree accessor methods. No nodes other than shadow root descendants are accessible with these methods.

The parentNode and parentElement attributes of the shadow root object must always return null.

Lower-boundary Encapsulation

To maintain the lower-boundary encapsulation, the distribution of child nodes of the shadow host among the insertion points in the associated shadow tree must have the following traits:

An insertion point may be active or inactive. An active insertion point participates in the distribution process, whereas the inactive insertion does not. If not specifically set to be inactive, the insertion point must be considered active.

If an insertion point is not in a shadow tree, it must have the same rendering behavior as the HTMLUnknownElement.

The distribution algorithm must produce an outcome that is equivalent of the outcome of processing these steps:

Input
TREE, a shadow tree
POOL, a list of DOM nodes
Output
The nodes in POOL are distributed among insertion points in TREE.
  1. Repeat for each active insertion point in TREE, in tree order:
    1. Let POINT be the current insertion point
    2. Repeat for each node in POOL:
      1. Let NODE be the current node
      2. If the NODE satisfies POINT's matching criteria:
        1. Distribute the NODE to POINT
        2. Remove NODE from the POOL
      3. Otherwise, continue to repeat
    3. Continue to repeat

Satisfying Matching Criteria

The matching criteria for an insertion point is a set of compound selectors. These compound selectors are restricted to contain only these simple selectors:

A node satisfies a matching criteria only if:

  1. all compound selectors in the set, contain only the simple selectors specified above; and
  2. a node matches at least one compound selectors in the set or the set is empty.

::distributed() pseudo-element

The ::distributed(selector) is a functional pseudo-element taking a relative selector as an argument. It represents a relationship between an insertion point in a shadow tree and an element whose inclusive ancestor is distributed into that insertion point. The argument is scope-contained-selectors, with the reference element set initialized to the parent of the element, distributed to the insertion point.

For example, the following selector represents the all div elements that that are descendants of an element, distributed into an insertion point


    ::distributed(div)

This selector represents all child span elements that are distrubted into insertion points with class attribute victory


    .victory::distributed(>span)

Should reference element set be insertion point or parent?

All other types of selectors must not cross the shadow boundary from a shadow tree to the tree of its shadow host.

Hosting Multiple Shadow Trees

A shadow host may host more than one shadow tree. In such cases, the trees are stacked in the order they were added the host, starting with the tree added most recently. This set of trees is called a tree stack. The more recently added tree is called the younger tree, and the less recently added tree is called the older tree. The most recently added tree is called the youngest tree.

To facilitate composing multiple shadow trees of the same host, a special kind of insertion point is defined. The shadow insertion point designates a place in the shadow tree, where an older tree is inserted when rendering. If multiple shadow insertion points exist in a shadow tree, only the first, in tree order, is recognized. It is said that the shadow tree is assigned to a shadow insertion point if the shadow tree is rendered in place of this shadow insertion point.

Just like other insertion points, the shadow insertion points can be active or inactive.

Reprojection

One case that deserves special consideration is the situation when an insertion point is a child node of another shadow host. In such situations, the nodes distributed into that insertion point must appear as if they were child nodes of the shadow host in the context of distribution within the shadow tree, hosted by said shadow host. Thus, the nodes distributed to a shadow tree could have already been distributed by the nesting tree. The effect of a node being distributed into more than one insertion point is called reprojection.

Despite being distributed to more than one insertion point during reprojection, a node is still only rendered once, because of the constraints under which the reprojection occurs: since the insertion points are only subject to reprojection when they are children of a shadow host, they are never rendered. Instead the shadow tree is rendered in their place.

Composition

The composition of shadow trees for a given shadow host is performed with the tree composition algorithm, which must be equivalent to processing the following steps:

Input
HOST, a shadow host
Output
All insertion points and shadow insertion points are populated.
  1. Let TREE be the youngest tree in the HOST's tree stack
  2. Let POOL be an empty list of nodes
  3. For each child node of HOST:
    1. Let CHILD be this node
    2. If CHILD is an insertion point:
      1. Let REPROJECTED be the list of nodes, distributed into this insertion point as a result of running tree composition algorithm on this insertion point's shadow tree
      2. If REPROJECTED is an empty list, set it to the list of all child nodes of CHILD
      3. Add all nodes in REPROJECTED into POOL
    3. Otherwise, add CHILD to POOL
  4. Repeat while TREE exists:
    1. Let POINT be the first encountered active shadow insertion point in TREE, in tree order
    2. Run the distribution algorithm, supplying POOL and TREE as input
    3. If POINT exists:
      1. Find the next older tree, relative to TREE in the HOST's tree stack
        1. If there is no older tree, stop.
        2. Otherwise:
          1. Set TREE to be this older tree
          2. Assign TREE to the POINT
          3. Continue to repeat
    4. Otherwise, stop.

When an insertion point or a shadow insertion point has nothing assigned or distributed to them, the fallback content must be used instead when rendering. The fallback content is all descendants of the element that represents the insertion point. The insertion points or shadow insertion points in fallback content must be considered inactive.

Nested Shadow Trees

Any element in a shadow tree can be a shadow host, thus producing nested shadow trees. A shadow tree is nested when its shadow host is itself a part of a shadow tree. Conversely, a shadow tree A is said to be nesting the shadow tree B if B is nested by A. If a shadow host is declared in the document, the document is the nesting tree of its shadow trees.

A shadow tree is enclosed by another shadow tree when the enclosing tree nests the enclosed tree through one or more levels of nesting. All shadow trees are enclosed by the document tree.

Rendering Shadow Trees

Rendering of shadow trees, or presenting them visually, is defined as a specialization of rendering any DOM tree, and must happen as these steps:

Input
HOST, a shadow host
Output
Rendering of the HOST, including its shadow trees
  1. Run tree composition algorithm for the given shadow host
  2. As content of the shadow host, render the youngest tree as a any DOM tree, with the following shadow rendering exceptions:

This process of rendering produces a structure that is a composition of several DOM trees, including the document tree. The term "as rendered" is used to refer to this structure.

Custom Pseudo-Elements

In certain situations, the author of a shadow tree may wish to designate one or more elements from that tree as a structural abstraction that provides additional information about the contents of the shadow tree.

For example, when developing a 2-dimensional range slider widget, which offers a thumb that could be moved in 2 directions in a rectangular space, the author decides to allow the widget users to style the thumb directly.

The custom pseudo-elements enable this scenario. A custom pseudo-element is an association between an element in a shadow tree and a string value. This string value is called a custom pseudo-element value. The custom pseudo-element value must be considered valid if it starts with a U+0078 LATIN SMALL LETTER X, followed by U+002D HYPHEN-MINUS. Otherwise, the custom pseudo-element value must be considered invalid.

In the case of the aforementioned 2-dimensional slider widget, the author could build the widget's shadow tree as follows:


var widget = document.createElement('div');
var root = widget.createShadowRoot();
var thumb = document.createElement('div');
thumb.pseudo = 'x-thumb';
root.appendChild(thumb);

Once the pseudo property is set, the consumer of the widget could style the widget's thumb using x-thumb pseudo-element, reaching into the widget's shadow tree directly to the thumb:


div::x-thumb {
    width: 10px;
    height: 10px;
    color: bisque;
}

Using the x- prefix was raised as a problem (see bug 20600), but no one presented better alternatives yet.

Events

When an event is dispatched in a shadow tree, its path either crosses the shadow boundary or is terminated at the shadow boundary. One exception are the mutation events. The mutation event types must never be dispatched in a shadow tree.

For event path to cross the shadow boundary, it must be populated with adjusted parents, or nodes that appear as parents as rendered. The parent calculation algorithm is used to determine the adjusted parent of any given node and create the list of ancestors for event dispatch. This algorithm must be equivalent to processing the following steps:

Input
NODE, a node
CONTEXT, null or a reprojection context node
Output
PARENT, a node's adjusted parent
  1. If NODE is a shadow root:
    1. If NODE is currently assigned to a shadow insertion point:
      1. Let the PARENT be the shadow insertion point to which NODE is assigned to
    2. Otherwise, let the PARENT be the shadow host of the NODE
  2. If NODE is currently distributed to an insertion point in a shadow tree, let PARENT be the insertion point to which the NODE is distributed
  3. If NODE is an insertion point and CONTEXT is not null:
    1. If the parent node of NODE is a shadow host and its shadow tree contains an insertion point to which CONTEXT is distributed, let PARENT be that insertion point
  4. Otherwise, let PARENT be the node's parent node.

Event Retargeting

In the cases where events cross the shadow boundaries, the event's information about the target of the event is adjusted in order to maintain upper boundary encapsulation. Event retargeting is a process of computing relative targets for each ancestor of the node at which the event is dispatched. A relative target is a node that most accurately represents the target of a dispatched event at a given ancestor while maintaining the upper boundary encapsulation.

The retargeting algorithm is used to determine relative targets, and it must be equivalent to processing the following steps:

Input
NODE, a node
Output
TARGETS, a list of tuples, each containing NODE's ancestor and its relative target
  1. Let STACK be a stack of nodes
  2. Let ANCESTOR be NODE
  3. Repeat while ANCESTOR exists:
    1. If ANCESTOR is an insertion point:
      1. Let CONTEXT be the top-most item in STACK that is not an insertion point or null if such item does not exist.
      2. Let TOP be the item at the top of STACK, or ANCESTOR if STACK is empty
      3. Push TOP into STACK
    2. Otherwise, let CONTEXT be null
    3. If STACK is empty, push ANCESTOR into STACK
    4. Let TARGET be the node at the top of STACK
    5. Add (TARGET, ANCESTOR) tuple to TARGETS
    6. If ANCESTOR is a shadow root:
      1. Pop STACK, if STACK is not empty
    7. Set ANCESTOR to be the result of parent calculation algorithm, given ANCESTOR and CONTEXT as input

The retargeting process must occur prior to dispatch of an event.

Some events have a relatedTarget property, which holds a node that's not the event's target, but is related to the event.

For instance, a mouseover event's relatedTarget may hold the node from which the mouse has moved to event's target. In the case where relatedTarget is in a shadow tree, the conforming UAs must not leak its actual value outside of this tree. In cases where both relatedTarget and target are part of the same shadow tree, the conforming UAs must stop events at the shadow boundary to avoid the appearance of spurious mouseover and mouseout events firing from the same node.

Thus, if an event has a relatedTarget, its value and extent of event dispatch must be adjusted. In general:

  1. For a given node, the relatedTarget must be changed to its ancestor (or self) that is in the same shadow tree as the node
  2. Event listeners must not be invoked on a node for which the target and relatedTarget are the same.

The related target resolution algorithm must be used to determine the value of the relatedTarget property and must be equivalent to processing the following steps:

Input
NODE, the node on which event listeners would be invoked
RELATED, the related target for the event
Output
ADJUSTED, the adjusted related target for NODE
  1. Let TARGET be NODE
  2. Let ADJUSTED be undefined
  3. Repeat while TARGET exists:
    1. Let STACK be an empty stack of nodes
    2. Let ANCESTOR be RELATED
    3. Let LAST be undefined
    4. Repeat while ANCESTOR exists:
      1. Let CONTEXT be null
      2. If STACK is empty, push ANCESTOR into STACK
      3. Otherwise, if ANCESTOR is an insertion point:
        1. Let CONTEXT be the top-most item that is not an insertion point in STACK or null, if such item does not exist.
        2. If LAST is distributed or assigned into ANCESTOR:
          1. Let HEAD be the node at the top of the STACK
          2. Push HEAD into STACK
      4. If ANCESTOR and TARGET are in the same tree:
        1. Let ADJUSTED be the node at the top of the stack
        2. Stop.
      5. If ANCESTOR is a shadow root, pop STACK
      6. Let LAST be ANCESTOR
      7. Set ANCESTOR to be the result of parent calculation algorithm, given ANCESTOR and CONTEXT as input
    5. If TARGET is a shadow root, let TARGET be the shadow host of TARGET
    6. Otherwise, let TARGET be TARGET's parent node

Retargeting Focus Events

The focus, DOMFocusIn, blur, and DOMFocusOut events must be treated in the same way as events with a relatedTarget, where the corresponding node that is losing focus as a result of target gaining focus or the node that is gaining focus, and thus causing the blurring of target acts as the related target.

Events that are Always Stopped

The following events must always be stopped at the nearest shadow boundary:

Event Dispatch

At the time of event dispatch:

Upon completion of the event dispatch, the Event object's target and currentTarget must be to the highest ancestor's relative target. Since it is possible for a script to hold on to the Event object past the scope of event dispatch, this step is necessary to avoid revealing the nodes in shadow trees.

Event Retargeting Example

Suppose we have a user interface for a media controller, represented by this tree, composed of both document tree and the shadow trees. In this example, we will assume that selectors are allowed to cross the shadow boundaries and we will use these selectors to identify the elements. Also, we will invent a fictional shadow-root element to demarcate the shadow boundaries and represent shadow roots:


<div id="player">
    <shadow-root id="player-shadow-root">
        <div id="controls">
            <button class="play-button">PLAY</button>
            <input type="range" id="timeline">
                <shadow-root id="timeline-shadow-root">
                    <div class="slider-thumb" id="timeline-slider-thumb"></div>
                </shadow-root>
            </input>
            <div class="volume-slider-container">
                <input type="range" class="volume-slider">
                    <shadow-root id="volume-shadow-root">
                        <div class="slider-thumb" id="volume-slider-thumb"></div>
                    </shadow-root>
                </input>
            </div>
        </div>
    </shadow-root>
</div>

Let's have a user position their pointing device over the volume slider's thumb (#volume-slider-thumb), thus triggering a mouseover event on that node. For this event, let's pretend it has no associated relatedTarget.

Just before the event is dispatched, we perform retargeting:

  1. Since #volume-slider-thumb is not an insertion point, we push it onto STACK, and add the first tuple to TARGETS as (#volume-slider-thumb, #volume-slider-thumb)
  2. We then calculate parent as #volume-shadow-root and proceed to second iteration, adding (#volume-slider-thumb, #volume-shadow-root) tuple to TARGETS
  3. Because #volume-shadow-root is a shadow root, we pop STACK, thus emptying it
  4. Next parent calculation yields #volume-slider, and so we begin the third iteration
  5. Here, STACK is empty again, so we push #volume-slider into it and add (#volume-slider, #volume-slider) to TARGETS
  6. The parent of #volume-slider is #volume-slider-container not a shadow root and not an insertion point, which dictates that we add (#volume-slider, #volume-slider-container) to TARGETS
  7. Next up, we see #controls, again neither a shadow root nor an insertion point, which means we add (##volume-slider, #controls) to TARGETS
  8. The next ancestor is #player-shadow-root, a shadow root, and thus we add it (#volume-slider, #player-shadow-root) to TARGETS, then pop STACK, emptying it
  9. Its parent is #player, neither shadow root nor insertion point, and STACK is empty, so we push #player to STACK, then add (#player, #player) to TARGETS
  10. In our example, there are no further parents, causing us to exit the loop and stop.

At the end of this process, we should have the following set of ancestors and relative targets:

Ancestor Relative Target
#player #player
#player-shadow-root #volume-slider
#controls #volume-slider
#volume-slider-container #volume-slider
#volume-slider #volume-slider
#volume-shadow-root #volume-slider-thumb
#volume-slider-thumb #volume-slider-thumb

After we dispatch the mouseover event using these newly computed relative targets, the user decides to move their pointing device over the thumb of the timeline (#timeline-slider-thumb). This triggers both a mouseout event for the volume slider thumb and the mouseover event for the timeline thumb.

Let's study how the relatedTarget value of the volume thumb's mouseout event is affected. For this event, the relatedTarget is the timeline thumb (#timeline-slider-thumb). Per algorithm:

  1. Starting with NODE as #volume-slider-thumb and RELATED as #timeline-slider-thumb,
  2. We examine ancestors of RELATED:
    1. First up is #timeline-slider-thumb, and we push it into STACK
    2. Then it's #timeline-shadow-root, a shadow root, so we pop STACK, emptying it
    3. Next is #timeline, we push it into STACK
    4. Then comes #controls and after it, #player-shadow-root, a shadow root, so we pop STACK, emptying it
    5. Final ancestor is #player, we push it into STACK
  3. Set TARGET to #volume-shadow-root
  4. We again examine ancestors of RELATED and notice that it produces the same result as our previous such examination
  5. We then set TARGET to #volume-slider and
  6. Repeat examination of ancestors once more—and realize that when ANCESTOR value is #timeline, the ANCESTOR and TARGET are in the same tree, and thus
  7. Return #timeline as the adjusted relatedTarget value.

Performing this computation with NODE as #player yields the result of both target and relatedTarget being the same value (#player), which means that we do not dispatch the event on this node and its ancestors.

Styles

Each shadow root has an associated list of zero or more style sheets, named shadow root style sheets. This is an ordered list that contains all style sheets, associated with the shadow root, in tree order.

When a shadow host has multiple shadow roots, the cascade order of CSS rules, defined in shadow roow style sheets, in ascending order of precedence must match the order of the tree stack, ending with the youngest tree.

To enforce upper-boundary encapsulation, CSS rules declared in an enclosing tree must not apply in a shadow tree, except when the apply-author-styles flag is set for this tree. The flag signals that the rules declared in the enclosing trees are applicable in the shadow tree.

Even when the apply-author-styles is set, the selectors still must not cross the shadow boundary per scoping constraints. In other words, with apply-author-styles set, the document CSS rules only match wholly inside or outside of the shadow tree.

For the purposes of the cascade order, the CSS rules for shadow trees that have the apply-author-styles flag set must be treated as scoped selectors with shadow root as their scope. In this context, for shadow trees A and B, the A's shadow root is treated as descendant of B's shadow root if A is enclosed by B.

If a CSS rule in a nesting tree ends with a compound selector that contains a pseudo-element whose name equals to a valid value of a custom pseudo-element that exists in this shadow tree, this pseudo-element must match the element, associated with this custom pseudo-element.

Conversely, to enforce lower-boundary encapsulation, CSS rules declared in a shadow root style sheets must not apply in the document tree, with two exceptions:

  1. Rules that contain select ::distributed() pseudo-element match elements in the enclosing trees;
  2. The @host @-rule matches a shadow host in the nesting tree.

In a document that contains shadow trees, the CSS properties must be inherited from parent nodes, produced using the parent calculation algorithm. This requirement has the following effects:

If the reset-style-inheritance flag is set for a shadow tree, all inheritable CSS properties must behave as if they were explicitly set to the initial value at the upper boundary of the tree.

If the reset-style-inheritance flag is set for an insertion point in a shadow tree, all inheritable CSS properties must behave as if they were explicitly set to the initial value at the lower boundary of the tree.

CSS Variables

The shadow host styles being inherited by the children of the shadow root must also apply to CSS Variables. This provides a way for document tree styles to send signals into the shadow trees, and for the shadow trees to receive these signals with the use of the var() function.

text-decoration Property

The text decorations, specified by the text-decoration property must not be propagated from shadow hosts to shadow trees.

@host @-rule

Within styles, specified in shadow trees, the author may use a @host @-rule. The syntax of this rule is defined as this addition to CSS Grammar:

The following production is added to the grammar:


host
    : HOST_SYM S* '{' S* ruleset* '}' S*
    ;

The following rule is added to the tokenizer:


@{H}{O}{S}{T}        {return HOST_SYM;}

The declarations of the rules in a @host @-rule must only be matched against the shadow host of the shadow tree in which the style is specified, but only if this shadow tree is rendered. When the shadow tree is not rendered (when it's an older tree that is not assigned to a shadow insertion point), the declarations must not be applied.

When a rule in @host @-rule matches an element, it must have higher specificity than any selector, but lower specificity than declarations from a style attribute.

User Interaction

Ranges and Selections

Since a node in a document tree and a node in a shadow tree never have the same root, there may never exist a valid DOM range that spans either both a document tree and a shadow tree, or multiple shadow trees.

Accordingly, selections may only exist within one tree, because they are defined by a single range. To maintain upper boundary encapsulation, the selection, returned by the window.getSelection() method must never return a selection within a shadow tree.

The getSelection() method of the shadow root object must return the current selection in this shadow tree.

Focus Navigation

Shadow trees participate in sequential or directional focus navigation as part of the document tree. The navigation order within a shadow tree must be computed as a list of focusable elements in tree order as-rendered, with the exception of any elements, distributed into insertion points, and is called shadow DOM navigation order.

Since the elements, distributed into insertion points are not part of the shadow tree, their order remains to be controlled by the navigation order of the nesting tree.

For sequential focus navigation, the shadow DOM navigation order sequence must be inserted into the document navigation order:

  1. immediately after the shadow host, if the shadow host is focusable; or
  2. in place of the shadow host as if the shadow host were assigned the value of auto for determining its position.

For directional focus navigation, it is up to the user agent to integrate the shadow DOM navigation order into the document navigation order.

Active Element

To maintain upper-boundary encapsulation, the value of the Document object's focus API property activeElement must be adjusted. To prevent loss of information when adjusting this value, each shadow root must also have an activeElement property to store the value of the focused element in the shadow tree.

The active element adjustment algorithm is used to determine the values of the respective activeElement properties, and it must be equivalent to processing the following steps:

Input
ELEMENT, the focused element
Output
Adjusted values of activeElement properties.
  1. Run the retargeting algorithm with ELEMENT as input
  2. For each TUPLE in the resulting list of tuples:
    1. Let ADJUSTED be the first value of the TUPLE
    2. Let NODE be the second value of the TUPLE
    3. If NODE is a Document or a shadow root, set the value of the activeElement to ADJUSTED.

Editing

The value of the contenteditable attribute must not propagate from shadow host to its shadow trees.

Assistive Technology

User agents with assistive technology traverse the document tree as rendered, and thus enable full use of of WAI-ARIA semantics in the shadow trees.

HTML Elements in Shadow Trees

Comparatively, a shadow tree can be seen as somewhere between just part of a document and itself being a document fragment. Since it is rendered, a shadow tree aims to retain the traits of a typical tree in a document. At the same time, it is an encapsulation abstraction, so it has to avoid affecting the document tree. Thus, the HTML elements must behave as specified in the shadow trees, with a few exceptions.

Inert HTML Elements

A subset of HTML elements must behave as inert, or not part of the document tree. This is consistent how the HTML elements would behave in a document fragment. These elements are:

All other HTML elements in the shadow trees must behave as if they were part of the document tree, with the scoping constraints of their respective trees applied.

HTML Forms

The forms in HTML are scoped at the document level. That is, all form elements and form-associated elements are accessible using the document DOM object's tree accessors. Per scoping constraints, this must exclude elements in the shadow trees.

Instead, each shadow tree must scope its form elements and form-associated elements. Because the form's ownerDocument is the shadow host's document, the form submission must continue to work as specified.

HTML Elements and Their Shadow Trees

Per specification, some HTML elements are designed to either not render their contents or have special requirements in regard to contents rendering. In order to reconcile these differences in rendering behavior with the shadow DOM tree composition, all HTML elements must have an equivalent of a shadow tree that is created and populated at the time of element instantiation. It is up to a user agent to define the content of these trees. However, all conforming user agents must satisfy the following requirements:

HTML Element Shadow Tree Requirements
img, iframe, embed, object, video, audio, canvas, map, input, textarea, progress, meter If the element can have fallback content, contains one insertion point. The matching criteria value is the universal selector only when the element needs to show fallback content. Otherwise, contains no insertion points or an insertion point that matches nothing.
fieldset Contains two insertion points with the following matching criteria:
  1. legend:first-of-type
  2. universal selector
details Contains two insertion points with the following matching criteria:
  1. summary:first-of-type
  2. universal selector
All other elements Contains one insertion point with the universal selector as the matching criteria

Elements and DOM Objects

ShadowRoot Object

The ShadowRoot object represents the shadow root.


interface ShadowRoot : DocumentFragment {
    HTMLElement getElementById(DOMString elementId);
    NodeList getElementsByClassName(DOMString tagName);
    NodeList getElementsByTagName(DOMString className);
    NodeList getElementsByTagNameNS(DOMString? namespace, DOMString localName);
    Selection? getSelection();
    Element? elementFromPoint(float x, float y);
    
    attribute bool applyAuthorStyles;
    attribute bool resetStyleInheritance;
    readonly attribute Element? activeElement;
    attribute DOMString innerHTML;
    readonly attribute StyleSheetList styleSheets;
}

ShadowRoot Attributes

applyAuthorStyles of type bool
Represents the apply-author-styles flag and indicates whether or not the rules in author styles associated with the element's document apply to the shadow tree. If false (default value), the author styles are not applied to the shadow tree. If true, the author styles are applied.
On getting, the attribute must return the current value of the apply-author-styles flag for the shadow host's tree.
On setting, the attribute must set the value of the apply-author-styles flag for the shadow host's tree to specified value.
resetStyleInheritance of type bool
Represents the reset-style-inheritance flag and indicates whether or not the inheritable CSS properties are set to the initial value at the shadow boundary. If false (default value), the properties continue to inherit. If true, the properties are set to initial value.
On getting, the attribute must return the current value of the reset-style-inheritance flag for the shadow host's tree.
On setting, the attribute must set the value of the reset-style-inheritance flag for the shadow host's tree to specified value.
activeElement of type Element, readonly
Represents the currently focused element in the shadow tree.
On getting, the attribute must return the currently focused element in the shadow tree or null, if there is none.
innerHTML of type DOMString
represents the markup of ShadowRoot's contents.
On getting, the attribute must return the result of running the HTML fragment serialization algorithm with the context object as shadow host.
On setting, these steps must be run:
  1. Let FRAGMENT be the result of invoking the fragment parsing algorithm with the new value as MARKUP, and the context object as shadow host
  2. Replace all with FRAGMENT within the shadow root.
styleSheets of type StyleSheetList, readonly
Represents the shadow root style sheets.
On getting, the attribute must return a StyleSheetList sequence containing the shadow root style sheets.

The nodeType attribute of a ShadowRoot instance must return DOCUMENT_FRAGMENT_NODE. Accordingly, the nodeName attribute of a ShadowRoot instance must return "#document-fragment".

ShadowRoot Methods

getElementById
Must behave exactly like document.getElementById, except scoped to the shadow tree.
getElementsByClassName
Must behave exactly like document.getElementsByClassName, except scoped to the shadow tree.
getElementsByTagName
Must behave exactly like document.getElementsByTagName, except scoped to the shadow tree.
getElementsByTagNameNS
Must behave exactly like document.getElementsByTagNameNS, except scoped to the shadow tree.
getSelection
Returns the current selection in the shadow tree.
When invoked, it must return the selection in the shadow host's tree.
elementFromPoint
Returns an element at specified coordinates.
Eventually, this needs to be part of CSSOM View Module specification
When invoked, it must return result of running the following steps:
  1. If context object is not a ShadowRoot instance, throw an InvalidNodeTypeError.
  2. If either argument is negative, x is greated than the viewport width excluding the size of a rendered scroll bar (if any), or if y is greated than the viewport height excluding the size of a rendered scroll bar (if any), return null.
  3. Let HIT be the element at coordinates x and y in the viewport, determined through hit testing
  4. Let TARGETS be the result of running the retargeting algorithm with HIT as its argument
  5. Let TUPLE be the tuple in TARGETS, whose second value is context object
  6. Return first value of TUPLE.

Invoking the cloneNode() method on a ShadowRoot instance must always throw a DATA_CLONE_ERR exception.

Extensions to Element Interface


partial interface Element {
    ShadowRoot createShadowRoot();
    readonly attribute ShadowRoot? shadowRoot;
    attribute DOMString pseudo;
}

Attributes

pseudo of type DOMString
Represents the custom pseudo-element value, associated with the element.
On getting, the attribute must return the current custom pseudo-element value or empty string if there is no custom pseudo-element associated with this element.
On setting, these steps must be run:
  1. If the custom pseudo-element, associated with this element already exists, discard it
  2. If the new value is not null, create a new custom pseudo-element with element and the new value as its value.
shadowRoot of type ShadowRoot
Represents the top shadow tree in the "as rendered" structure.
On getting, the attribute must return the youngest tree that has the context object as its shadow host, or null if no such shadow tree is accesible.
For HTML elements, the UA-provided shadow trees must not be accessible.

Methods

createShadowRoot
When invoked, these steps must be run:
  1. If the context object is not an element, throw an InvalidNodeTypeError.
  2. Otherwise:
    1. Create a new instance of the ShadowRoot object
    2. Establish the context object as the shadow host of the ShadowRoot object
    3. Add the ShadowRoot object at the top of the tree stack of its host
    4. Return ShadowRoot object.

CSSHostRule Interface

The CSSHostRule represents the @host at-rule in a CSS style sheet.


interface CSSHostRule : CSSRule {
    readonly attribute CSSRuleList cssRules;
    unsigned long insertRule(DOMString rule, unsigned long index);
    void deleteRule(unsigned long index);
};

CSSHostRule Attributes

cssRules of type CSSRuleList
Represents the CSS rules in the @host style block
On getting, the attribute must return the list of all CSS rules, specified in the @host at-rule, represented by the context object.

CSSHostRule Methods

insertRule(DOMSTring rule, unsigned long index)
When invoked, the method must insert a CSS rule rule into the CSS rule list returned by cssRules at index.
deleteRule(unsigned long index)
When invoked, the method must remove a CSS rule rule from the CSS rule list returned by cssRules at index.

The HOST_RULE type is added to CSSRule interface.


partial interface CSSRule {
    const unsigned short HOST_RULE = 1001;
};
HOST_RULE
When the value of the type attribute is HOST_RULE, then the object that implements this interface must implement CSSHostRule interface.

The content HTML element

The content HTML element represents a insertion point in the shadow tree.

Context
Where flow content is expected.
Content model
Transparent
Children
Anything as fallback content
Content attributes
Global attributes
select, a set of comma-separated tokens
represents the matching criteria for distributing child nodes of the shadow host. Each token must be a compound selector.
resetstyleinheritance, a boolean attribute
indicates the state of the reset-style-inheritance flag for this insertion point. If present, the value of the flag must be set to true. Otherwise, the value must be set to false.
DOM Interface

interface HTMLContentElement : HTMLElement {
    attribute DOMString select;
    attribute boolean resetStyleInheritance;
    NodeList getDistributedNodes();
}
    
Attributes
select of type DOMString
Must reflect the select attribute.
resetStyleInheritance of type boolean
Must reflect the resetstyleinheritance attribute.
Methods
getDistributedNodes
Must return a static NodeList consisting of nodes, currently distributed into this insertion point.

The shadow HTML element

The shadow HTML element represents an shadow insertion point in a shadow tree.

Context
Where flow content is expected.
Content model
Transparent
Children
Anything as fallback content
Content attributes
Global attributes
resetstyleinheritance, a boolean attribute
indicates the state of the reset-style-inheritance flag for this shadow insertion point. If present, the value of the flag must be set to true. Otherwise, the value must be set to false.
DOM Interface

interface HTMLShadowElement : HTMLElement {
    readonly attribute ShadowRoot? olderShadowRoot;
    attribute boolean resetStyleInheritance;
}
    
Attributes
olderShadowRoot of type ShadowRoot
Represents the shadow tree that is rendered in place of this shadow insertion point
On getting, the attribute must return a result that is equivalent to running the following steps:
  1. Let TREE be the shadow tree that contains the context object
  2. If TREE does not exist, return null.
  3. If context object, in tree order, is not the first active instance of shadow element in TREE, return null.
  4. Let HOST be the shadow host of TREE
  5. From HOST's tree stack, return the older tree relative to TREE, or null if no such shadow tree is accessible.
For HTML elements, the UA-provided shadow trees must not be accessible.
resetStyleInheritance of type boolean
Must reflect the resetstyleinheritance attribute.

Shadow DOM Example

Bob was asked to turn a simple list of links into a News Widget, which has links organized into two categories: breaking news and just news. The current document markup for the stories looks like this:


<ul class="stories">
    <li><a href="//example.com/stories/1">A story</a></li>
    <li><a href="//example.com/stories/2">Another story</a></li>
    <li class="breaking"><a href="//example.com/stories/3">Also a story</a></li>
    <li><a href="//example.com/stories/4">Yet another story</a></li>
    <li><a href="//example.com/stories/4">Awesome story</a></li>
    <li class="breaking"><a href="//example.com/stories/5">Horrible story</a></li>
</ul>

To organize the stories, Bob decides to use shadow DOM. Doing so will allow Bob to keep the document markup uncluttered, and harnessing the power of insertion point makes sorting stories by class name a very simple task. After getting another cup of Green Eye, he quickly mocks up the following shadow tree, to be hosted by the ul element:


<div class="breaking">
    <ul>
        <content select=".breaking"></content> <!-- insertion point for breaking news -->
    </ul>
</div>
<div class="other">
    <ul>
        <content></content> <!-- insertion point for the rest of the news -->
    </ul>
</div>

Bob then styles the newborn widget according to comps from the designer by adding this to the shadow tree mockup:


<style>
    div.breaking {
        color: Red;
        font-size: 20px;
        border: 1px dashed Purple;
    }
    div.other {
        padding: 2px 0 0 0;
        border: 1px solid Cyan;
    }
</style>

While pondering if his company should start looking for a new designer, Bob converts the mockup to code:


function createStoryGroup(className, contentSelector)
{
    var group = document.createElement('div');
    group.className = className;
    // Empty string in select attribute or absence thereof work the same, so no need for special handling.
    group.innerHTML = '<ul><content select="' + contentSelector + '"></content></ul>';
    return group;
}

function createStyle()
{
    var style = document.createElement('style');
    style.textContent = 'div.breaking { color: Red;font-size: 20px; border: 1px dashed Purple; }' +
        'div.other { padding: 2px 0 0 0; border: 1px solid Cyan; }';
    return style;
}

function makeShadowTree(storyList)
{
    var root = storyList.createShadowRoot();
    root.appendChild(createStyle());
    root.appendChild(createStoryGroup('breaking', '.breaking'));
    root.appendChild(createStoryGroup('other', ''));
}

document.addEventListener('DOMContentLoaded', function() {
    [].forEach.call(document.querySelectorAll('ul.stories'), makeShadowTree);
});

Well done, Bob! With the cup of coffee still half-full, the work is complete. Recognizing his awesomeness, Bob returns to teaching n00bs the ways of WoW.

A few months pass.

It's election time. With Bob at his annual conference, Alice is charged with adding another, temporary box to the news widget, filled with election-related stories. Alice studies Bob's code, reads up on the shadow DOM spec and realizes that, thanks to multiple shadow tree support, she doesn't have to touch his code. As usual, her solution is elegant and simple, fitting neatly right under Bob's code:


// TODO(alice): BEGIN -- DELETE THIS CODE AFTER ELECTIONS ARE OVER.
var ELECTION_BOX_REMOVAL_DEADLINE = ...;

function createElectionStyle()
{
    var style = document.createElement('style');
    // TODO(alice): Check designer's desk for hallucinogens.
    style.textContent = 'div.election { color: Magenta;font-size: 24px; border: 2px dotted Fuchsia; }';
    return style;
}

function makeElectionShadowTree(storyList)
{
    var root = storyList.createShadowRoot();
    // Add and style election story box.
    root.appendChild(createElectionStyle());
    root.appendChild(createStoryGroup('election', '.election'));
    // Insert Bob's shadow tree under the election story box.
    root.appendChild(document.createElement('shadow'));
}

if (Date.now() < ELECTION_BOX_REMOVAL_DEADLINE) {
    document.addEventListener('DOMContentLoaded', function() {
        [].forEach.call(document.querySelectorAll('ul.stories'), makeElectionShadowTree);
    });
}
// TODO(alice): END -- DELETE THIS CODE AFTER ELECTIONS ARE OVER.

Using the shadow element allows Alice to compose Bob's widget inside of hers—without having to change a line of production code. Smiling to herself, Alice realizes that Bob may have come up with a way to keep the document markup clean, but she is the one who takes the cake for using shadow tree composition in such a cool way.

Acknowledgements

David Hyatt developed XBL 1.0, and Ian Hickson co-wrote XBL 2.0. These documents provided tremendous insight into the problem of functional encapsulation and greatly influenced this specification.

Alex Russell and his considerable forethought triggered a new wave of enthusiasm around the subject of shadow DOM and how it can be applied practically on the Web.

Dominic Cooney, Hajime Morrita, and Roland Steiner worked tirelessly to scope the problem of functional encapsulation within the confines of the Web platform and provided a solid foundation for this document.

The editor would also like to thank Alex Komoroske, Anne van Kesteren, Brandon Payton, Brian Kardell, Darin Fisher, Eric Bidelman, Deepak Sherveghar, Edward O'Connor, Elisée Maurer, Elliott Sprehn, Erik Arvidsson, Glenn Adams, Hayato Ito, Jonas Sicking, Malte Ubl, Mike Taylor, Oliver Nightingale, Olli Pettay, Rafael Weinstein, Richard Bradshaw, Ruud Steltenpool, Sam Dutton, Sergey G. Grekhov, Shinya Kawanaka, Tab Atkins, and Takashi Sakamoto for their comments and contributions to this specification.

This list is too short. There's a lot of work left to do. Please contribute by reviewing and filing bugs—and don't forget to ask the editor to add your name into this section.