A reusable component can look self-contained while its markup remains exposed to the page. A page-level query might select a button inside it, and a global button rule might change its appearance. Shadow DOM gives a component a separate DOM tree, reducing these accidental collisions. It is useful, but optional: a custom element can work without it. section 8.1 section 9.1 section 8.0
Call `attachShadow({mode: 'open'})` on an element to create its shadow root, then put the component’s markup in that root rather than in the element’s `innerHTML`. The element is the shadow host; the root contains the separate tree. The `mode` option is required. section 8.2
Listing 8.2. Using the Shadow DOM in a simple component
connectedCallback() {
this.attachShadow({mode: 'open'}); #1
this.shadowRoot.innerHTML =
`<div class="inside-component">My Component</div>` #2
}
The two-line component change: attach an open root, then write markup to `this.shadowRoot`. section 8.2
Try `document.querySelector('.inside-component')` after moving that element into the shadow root. It returns `null`: a query on the document does not enter the separate tree. With an open root, code can deliberately reach it through the host’s `shadowRoot` property and query there. This boundary guards against accidental access, not a determined caller. section 8.2
Two choices affect how you work with that boundary:
- An open root is available as `host.shadowRoot`. A closed root is not available through that property, so keep the value returned by `attachShadow()` if the component needs it. Closed mode is not a reliable security measure; open mode is generally the more practical choice here. section 8.2 section 8.4
- A shadow root can be created and populated in a custom element’s constructor. That differs from writing children directly into the host there; initialization in `connectedCallback` remains an option. section 4.2 section 8.2
A stepper beside a globally styled button
Imagine a stepper with two buttons and a number between them. Without Shadow DOM, a page-wide `button` rule can alter the stepper, while styles written for the component can affect the page. Move the stepper’s buttons, span, and styles into a shadow root: its internal rules can now use simple `button` and `span` selectors without colliding with those outside. To style the host itself from inside the root, use `:host`, not the component’s tag name. section 9.1 section 9.2
CSS isolation has a wrinkle: inherited text properties can still affect content inside a shadow root. A `:host { all: initial; }` rule is one demonstrated reset. Inside the separate tree, IDs also become practical for selecting component parts; a slider can map its internal elements from `this.shadowRoot`. section 9.2 section 9.5
Check the browsers you need before relying on the boundary. In the browser-support period described here, Firefox had shipped support, while the new Chromium-backed Edge was still in preview; IE would not gain native support. ShadyDOM can help with DOM access, but reproducing CSS isolation with ShadyCSS takes more work. Another fallback is to render into the host when `attachShadow` is unavailable and switch from `:host` to ordinary host selectors. That fallback gives up shadow isolation, so its CSS needs care. section 10.3 section 12.1

