Copyright © 2014 W3C® (MIT, ERCIM, Keio, Beihang), All Rights Reserved. W3C liability, trademark and document use rules apply.
This is a work in progress! For the latest updates from the Web Applications (WebApps) Working Group possibly including important bug fixes, please look at the draft on GitHub.
This specification describes a method of combining multiple DOM trees into one hierarchy and how these trees interact with each other within a document, thus enabling better composition of the DOM.
This is a work in progress! This specification is for review and not for implementation! For the latest updates, including important bug fixes, please look at the draft on GitHub instead.
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 W3C Web Applications (WebApps) as a Working Draft. If you wish to make comments regarding this document, please send them to public-webapps@w3.org (subscribe, archives). All comments are 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.
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:
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.
This section is non-normative.
See the Shadow DOM section of Introduction to Web Components as a non-normative intruduction.
A document tree is a node tree [DOM] whose root node is a document.
Any element can have an associated ordered list of zero or more node trees.
An element hosts a node tree if the node tree is a member of this associated list.
A shadow host is an element that hosts one or more node trees.
A shadow tree is a node tree hosted by a shadow host.
A shadow root is the root node of a shadow tree.
If more than one shadow tree is hosted by the same shadow host, the more recently added shadow tree is called the younger shadow tree and the less recently added shadow tree is called the older shadow tree.
If there is no older shadow tree than a given shadow tree, the shadow tree is called the oldest shadow tree.
If there is no younger shadow tree than a given shadow tree, the shadow tree is called the youngest shadow tree.
The older shadow root is the root node of the older shadow tree.
The younger shadow root is the root node of the younger shadow tree.
The oldest shadow root is the root node of the oldest shadow tree.
The youngest shadow root is the root node of the youngest shadow tree.
For convenience, the shadow root provides its own set of DOM tree accessor methods. No nodes other than the shadow root descendants are accessible with these methods.
A tree of trees is a tree of node trees.
The purpose of introducing a tree of trees here is to define algorithms easily in the following sections. This is a kind of a notation techchique to make the this specification simpler.
Just like a node tree is defined as a set of relationships between nodes, a tree of trees is similarly defined as a set of relationships between node trees:
The ownerDocument
property of a node in a shadow tree must refers to the document of the shadow host which hosts the shadow tree.
Window
object named properties [HTML] must access the nodes in the document tree.
This section is non-normative.
In the figure, there are seven node trees, named A, B, C, D, E and F. The node trees, C, D and E, are hosted by the same shadow host, which participates in the node tree A. The node tree C is the oldest shadow tree. The node tree E is the youngest shadow tree. The following set of relationships holds in the figure:
A composed tree is a node tree which is constructed out of nodes from multiple node trees in a tree of trees. The exact algorithm of constructing a composed tree is specified later.
In rendering a document tree, or presenting it visually, the composed tree must be used instead of the document tree.
The composed tree must be updated before the rendering occurs.
An insertion point is a defined location where nodes in a different node tree appear instead of the nodes's original position when constructing a composed tree.
A distribution is the mechanism that determines which nodes appear at each insertion point. The exact algorithm of a distribution is specified later.
A content insertion point is an insertion point to where the child nodes of the shadow host are distributed. The content element that satisfies all of the following conditions represents a content insertion point:
A shadow insertion point is an insertion point to where the children of the older shadow root are distributed. The shadow element that satisfies of the following conditions represents a shadow insertion point:
Each tree of trees has the distribution result which describes the result of distributions. The distribution result must be equivalent of the following:
An insertion point A is the final destination of a node B if A is the last item of the destination insertion points of B.
When a node A is distributed into an insertion point B, the following steps must happen:
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 appear as if they were child nodes of the shadow host in the context of distribution. Thus, the nodes distributed to a shadow tree could have already been distributed from its parent tree.
Despite being distributed to more than one insertion point, a node still only appears once in the composed tree at the final destination.
The distribution algorithm must be used to determine the distribution result for a tree of trees and must be equivalent to processing the following steps:
The distribution resolution algorithm must be used to determine the distribution result for a given node tree and its descendant trees, and must be equivalent to processing the following steps:
The pool population algorithm must be used to populate nodes from the child nodes of a given node and must be equivalent to processing the following steps:
The pool distribution algorithm must be used to distribute nodes in a pool into the content insertion points in a shadow tree and must be equivalent to processing the following steps:
If no nodes are distributed into a content insertion point CONTENT, the child nodes of CONTENT are distributed into CONTENT as fallback nodes.
If any condition which affects the distribution result changes, the distribution result must be updated before any use of the distribution result.
The matching criteria for an insertion point is a set of compound selectors [SELECTORS4]. These compound selectors are restricted to contain only these simple selectors:
:not()
A node satisfies a matching criteria only if:
The composed tree children calculation algorithm must be used to determine the child nodes of a node in the composed tree and must be equivalent to processing the following steps:
For a given tree of trees TREE-OF-TREES, the composed tree constructed from TREE-OF-TREES must be equivalent to the following tree:
When an event is dispatched in a shadow tree, its path either crosses the shadow trees or is terminated at the shadow root. One exception are the mutation events. The mutation event types must never be dispatched in a shadow tree.
The following events must always be stopped at the root node of the node tree:
abort
error
select
change
load
reset
resize
scroll
selectstart
The event path calculation algorithm must be used to determine event path and must be equivalent to processing the following steps:
This section is non-normative.
Suppose we have the following tree of trees:
A
is a document.
E
, J
, N
, Q
, S
and V
are shadow roots.
I
, M
, P
, R
and U
are content insertion points.
X
is a shadow insertion point.
This tree of trees has the following seven node trees, one document tree and six shadow trees:
A
, B
, C
and D
participate in that.
B
. Node E
, F
, G
, H
and I
participate in that.
H
. Node J
, K
, L
and M
participate in that.
K
. Node N
, O
and P
participate in that.
O
. Node Q
and R
participate in that.
F
. Node S
, T
and U
participate in that.
B
. Node V
, W
and X
participate in that.
This shadow tree is younger than the shadow tree 2.
Let's assume that the distribution result of this tree of trees is:
C
are [I, M]
(C
is re-distributed)
L
are [P, R]
(L
is re-distributed)
G
are [U]
F
are [X]
In this case, if an event is dispatched on node D
, the event path will be:
[D, C, I, M, L, P, R, Q, O, N, K, J, H, G, U, T, S, F, E, X, W, V, B, A]
Note that the event path calculation algorithm is designed to achieve the following goals:
That means if we focus on one node tree and forget all other node trees, the event path would be seen as if the event happened only on the node tree which we are focused on. This is an important aspect in a sense that hosting shadow trees doesn't have any effect to the event path within the node tree the shadow host participate in as long as the event is not stopped somewhere in the descendant trees.
For example, from the view of the document tree 1, the event path would be seen as [D, C, B, A]
.
From the view of the shadow tree 2, the event path would be seen as [I, H, G, F, E]
.
The similar things also apply to other node trees.
It is also worth pointing out that if we exclude all insertion points and shadow roots from an event path, the result would be equivalent to the inclusive ancestors of the node on which the event is dispatched, in the composed tree.
In the cases where event path is across multiple node trees, the event's information about the target of the event is adjusted in order to maintain 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 encapsulation.
The retargeting algorithm is used to determine relative targets, and it must be equivalent to processing the following steps:
The retargeting process must occur prior to dispatch of an event.
The Touch
target
[TOUCH-EVENTS] attribute must be adjusted in the same way as an event with a relatedTarget
. Each Touch
target
in the TouchList
returned from TouchEvent
touches()
, changedTouches()
and targetTouches()
must be the result of related target resolution algorithm, given NODE and Touch
target
as arguments.
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.
At the time of event dispatch:
Event
target
and currentTarget
attributes must return the relative target for the node on which event listeners are invokedMouseEvent
relatedTarget
attribute must return the adjusted related targetMouseEvent
offsetX
and offsetY
attributes must return the coordinates relative to the origin of the padding edge of the relative targetTouch
target
attribute must return the adjusted related targetrelatedTarget
and target
are the same for a given node, its the event listeners must not be invoked. TouchEvent
is not subject to this rule.Event
eventPhase attribute must return AT_TARGET if the relative target is same as the node on which event listeners are invokedbubbles
attribute value is false, run these substeps:
eventPhase
attribute to AT_TARGET
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.
This section is non-normative.
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 id="play-button">PLAY</button> <input type="range" id="timeline"> <shadow-root id="timeline-shadow-root"> <div id="slider-thumb" id="timeline-slider-thumb"></div> </shadow-root> </input> <div id="volume-slider-container"> <input type="range" id="volume-slider"> <shadow-root id="volume-shadow-root"> <div id="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
.
Per the retargeting algorithm, 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 see 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 the related target resolution algorithm, we should have the following set of ancestors and adjusted related targets:
Ancestor | Relative Target | Adjusted related Target |
---|---|---|
#player |
#player |
#player |
#player-shadow-root |
#volume-slider |
#timeline |
#controls |
#volume-slider |
#timeline |
#volume-slider-container |
#volume-slider |
#timeline |
#volume-slider |
#volume-slider |
#timeline |
#volume-shadow-root |
#volume-slider-thumb |
#timeline |
#volume-slider-thumb |
#volume-slider-thumb |
#timeline |
The node, #player
, has both target
and relatedTarget
being the same value (#player
), which means that we do not dispatch the event on this node and its ancestors.
This section is non-normative.
Selection [EDITING] is not defined. Implementation should do their best to do what's best for them. Here's one possible, admittedly naive way:
Since nodes which are in the different node trees never have the same root, there may never exist a valid DOM range that spans multiple node trees.
Accordingly, selections may only exist within one node tree, because they are defined by a single range. The selection, returned by the window.getSelection()
method never returns a selection within a shadow tree.
The getSelection()
method of the shadow root object returns the current selection in this shadow tree.
To maintain 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 value of the activeElement property, and it must be equivalent to processing the following steps:
The value of the contenteditable
attribute must not propagate from shadow host to its shadow trees.
User agents with assistive technology traverse the composed tree, and thus enable full use of WAI-ARIA [WAI-ARIA] semantics in the 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 [HTML] in the shadow trees, with a few exceptions.
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.
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 composed tree, 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 content insertion point. The matching criteria value is the universal selector only when the element needs to show fallback content. Otherwise, contains no content insertion points or an content insertion point that matches nothing. |
fieldset |
Contains two content insertion points with the following matching criteria:
|
details |
Contains two content insertion points with the following matching criteria:
|
All other elements | Contains one content insertion point with the universal selector as the matching criteria |
ShadowRoot
ObjectThe ShadowRoot
object represents the shadow root.
interface ShadowRoot : DocumentFragment {
HTMLElement getElementById (DOMString elementId);
NodeList getElementsByClassName (DOMString className);
NodeList getElementsByTagName (DOMString tagName);
NodeList getElementsByTagNameNS (DOMString? namespace, DOMString localName);
Selection? getSelection ();
Element
? elementFromPoint (double x, double y);
readonly attribute Element
? activeElement;
readonly attribute Element
host;
readonly attribute ShadowRoot
? olderShadowRoot;
attribute DOMString innerHTML;
readonly attribute StyleSheetList styleSheets;
};
activeElement
of type Element
, readonly , nullableRepresents 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.
host
of type Element
, readonly Represents the shadow host which hosts the context object.
On getting, the attribute must return the shadow host which hosts the context object.
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:
shadow host
olderShadowRoot
of type ShadowRoot
, readonly , nullableRepresents the older shadow root relative to the context object
On getting, the attribute must return a result that is equivalent to running the following steps:
For HTML elements, the UA-provided shadow trees must not be accessible.
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.
elementFromPoint
Returns an element at specified coordinates.
Eventually, this needs to be part of CSSOM View Module specification [CSSOM-VIEW]
When invoked, it must return result of running the following steps:
ShadowRoot
instance, throw an InvalidNodeTypeError
.x
is greater than the viewport width excluding the size of a rendered scroll bar (if any), or if y
is greater than the viewport height excluding the size of a rendered scroll bar (if any), return null.x
and y
in the viewport, determined through hit testingParameter | Type | Nullable | Optional | Description |
---|---|---|---|---|
x | double | ✘ | ✘ | |
y | double | ✘ | ✘ |
Element
, nullablegetElementById
Parameter | Type | Nullable | Optional | Description |
---|---|---|---|---|
elementId | DOMString | ✘ | ✘ |
HTMLElement
getElementsByClassName
Parameter | Type | Nullable | Optional | Description |
---|---|---|---|---|
className | DOMString | ✘ | ✘ |
NodeList
getElementsByTagName
Parameter | Type | Nullable | Optional | Description |
---|---|---|---|---|
tagName | DOMString | ✘ | ✘ |
NodeList
getElementsByTagNameNS
Parameter | Type | Nullable | Optional | Description |
---|---|---|---|---|
namespace | DOMString | ✔ | ✘ | |
localName | DOMString | ✘ | ✘ |
NodeList
getSelection
Returns the current selection in the shadow tree.
When invoked, it must return the selection in the shadow tree.
Selection
, nullableThe nodeType
attribute of a ShadowRoot
instance must return DOCUMENT_FRAGMENT_NODE
. Accordingly, the nodeName
attribute of a ShadowRoot
instance must return "#document-fragment"
.
Invoking the cloneNode()
method on a ShadowRoot
instance must always throw a DATA_CLONE_ERR
exception.
Element
Interfacepartial interface Element {
ShadowRoot
createShadowRoot ();
NodeList getDestinationInsertionPoints ();
readonly attribute ShadowRoot
? shadowRoot;
};
shadowRoot
of type ShadowRoot
, readonly , nullableRepresents the youngest shadow root that context object hosts.
On getting, the attribute must return the youngest shadow root that context object hosts, or null if no such shadow root is accessible.
For HTML elements, the UA-provided shadow trees must not be accessible.
createShadowRoot
ShadowRoot
objectShadowRoot
object to the ordered list of shadow roots associated with the context object as the youngest shadow rootShadowRoot
object.ShadowRoot
getDestinationInsertionPoints
NodeList
consisting of insertion points in the destination insertion points of the context object.NodeList
content
elementThe content
element represents an insertion point in the shadow tree.
If a content
element does not satisfy the condition of an insertion point, it must have the same rendering behavior as the HTMLUnknownElement
.
select
, a set of comma-separated tokensinterface HTMLContentElement : HTMLElement {
attribute DOMString select;
NodeList getDistributedNodes ();
};
select
of type DOMString, getDistributedNodes
NodeList
consisting of nodes in the distributed nodes of the context object.NodeList
shadow
elementThe shadow
element represents an shadow insertion point in a shadow tree.
If a shadow
element does not satisfy the condition of an insertion point, it must have the same rendering behavior as the HTMLUnknownElement
.
interface HTMLShadowElement : HTMLElement {
NodeList getDistributedNodes ();
};
getDistributedNodes
NodeList
consisting of nodes in the distributed nodes of the context object.NodeList
Event
Interfacepartial interface Event {
readonly attribute object path;
};
path
of type object, readonly Represents the event path.
On getting, the attribute must create and return a new JavaScript Array object, that is a copy of the event path for the context object.
Use Array
as the return type of the path
attribute in WebIDL.
WebIDL bugs: Array
subclassing
and class, not interface.
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.
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, 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, Takashi Sakamoto, and Yoshinori Sano 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.