Web Components

Course 2 · Ch 11
Web Components Intro
Custom elements and shadow DOM basics — defining your own genuinely new HTML tags, with their own encapsulated styling

Every element covered across this entire series so far — div, section, dialog, details — was built into the browser already. Web Components let you define a genuinely new HTML element, with its own behaviour and (optionally) completely isolated CSS. This chapter is an intentionally gentle introduction; Course 3 (Advanced) returns to this topic in full depth.

Custom Elements — Defining Your Own Tag

class GreetingCard extends HTMLElement { connectedCallback() { this.innerHTML = `<p>Hello, ${this.getAttribute('name')}!</p>`; } } customElements.define('greeting-card', GreetingCard);
// Used in HTML exactly like any built-in tag, once defined <greeting-card name="Philip"></greeting-card>
A custom element's tag name MUST contain a hyphen
This is a hard requirement, not a style convention — customElements.define('greeting', ...) (no hyphen) fails outright. The hyphen is specifically how the browser distinguishes a custom element from any current or future standard HTML tag, exactly the same reasoning behind the reserved data- prefix from Chapter 7.

connectedCallback — When the Element Actually Runs Its Setup

connectedCallback() runs when the element is actually inserted into the document — not when the class is defined, and not necessarily the instant the HTML parses it. This timing distinction matters once a custom element needs to read its own attributes or set up its initial content correctly.

Shadow DOM — Genuinely Isolated Internals

A custom element can attach a "shadow root" — a separate, encapsulated DOM subtree with its own scoped CSS that doesn't leak out to the rest of the page, and isn't affected by the rest of the page's CSS either.

Main page (light DOM) <greeting-card> — Shadow Root CSS here can't leak out; page CSS can't leak in
The dashed boundary is the shadow root — a genuine, browser-enforced styling barrier
class GreetingCard extends HTMLElement { constructor() { super(); const shadow = this.attachShadow({ mode: 'open' }); shadow.innerHTML = ` <style> p { color: red; font-weight: bold; } /* stays inside, never affects the rest of the page */ </style> <p>Hello!</p> `; } }
Without Shadow DOM
A custom element's internal HTML is just regular, ordinary DOM — the page's global CSS can style it (intentionally or accidentally), and any internal class names could clash with classes used elsewhere on the page.
With Shadow DOM
A genuine, browser-enforced boundary — internal CSS never leaks out, page-level CSS never leaks in (with deliberate, limited exceptions). Eliminates an entire category of accidental style collision.

Why This Matters — The Underlying Motivation

  • True encapsulation — a genuinely reusable widget (a custom date picker, a rating-stars component) that can't have its internal styling accidentally broken by whatever CSS happens to exist elsewhere on a page it's dropped into.
  • Framework-independent reuse — a properly built web component works in a React app, a Vue app, or a plain HTML page identically, since it's a genuine browser feature rather than tied to any specific JavaScript framework.
  • Native, not a library dependency — unlike component systems baked into specific frameworks, this is built directly into the browser itself, requiring no external library to function at all.
This is genuinely an introduction, not the full picture
Real web components also use the <template> element from Chapter 8 (cloning a template's content into the shadow root is a common, cleaner pattern than building HTML via template literals as shown above), <slot> elements for letting users pass in their own content, and a fuller lifecycle than just connectedCallback. Course 3, Chapter 1 goes through all of this properly — this chapter exists purely to establish the core concept before that deeper dive.

Chapter 11 Quick Reference

  • customElements.define('tag-name', ClassName) — registers a genuinely new HTML element
  • Custom element tag names MUST contain a hyphen — a hard requirement, distinguishes them from standard/future HTML tags
  • connectedCallback() — runs when the element is actually inserted into the document
  • attachShadow({mode: 'open'}) — creates a shadow root, a genuine CSS encapsulation boundary
  • Shadow DOM benefit: internal CSS never leaks out, page CSS never leaks in — eliminates accidental style collisions
  • Framework-independent — a real browser feature, works identically across React/Vue/plain HTML
  • Full depth (template/slot, full lifecycle) covered in Course 3, Chapter 1
  • Next chapter (final, Course 2): practical project — building a real contact form / project page