A custom element alone does not protect its styles: a page rule such as p { color: red } also reaches inside the component. Shadow DOM gives a component its own isolated DOM and CSS, while <template> and <slot> make templating and content passing easy.
Shadow DOM: host, shadow root, light DOM
- host — the element that carries the shadow root;
- shadow root — an isolated subtree;
- light DOM — the children you write inside the host.
class FancyCard extends HTMLElement {
constructor() {
super();
const root = this.attachShadow({ mode: 'open' });
root.innerHTML = `
<style>p { color: crimson; }</style>
<p>Inside the shadow</p>`;
}
}
customElements.define('fancy-card', FancyCard);
const card = document.createElement('fancy-card');
document.body.append(card);
console.log(card.shadowRoot.mode); // open
console.log(card.shadowRoot.querySelector('p').textContent); // Inside the shadow
console.log(document.querySelector('p')); // null⚠️
mode: 'closed'gives no real security (and makes tests and browser extensions harder). Most components chooseopen.