The first time I read the Custom Elements spec I closed the tab immediately. It felt like ceremony for something that should be simple. Six months later I came back, and something clicked.
The key insight
A custom element is just a class. You register it, the browser instantiates it when it sees your tag, and connectedCallback fires when it lands in the DOM.
class ReadingTime extends HTMLElement {
connectedCallback() {
const words = this.closest('article')
?.textContent.split(/\s+/).length ?? 0;
this.textContent = ${</span><span style="color:#24292E;--shiki-dark:#E1E4E8">Math</span><span style="color:#032F62;--shiki-dark:#9ECBFF">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">ceil</span><span style="color:#032F62;--shiki-dark:#9ECBFF">(</span><span style="color:#24292E;--shiki-dark:#E1E4E8">words</span><span style="color:#D73A49;--shiki-dark:#F97583"> /</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> 200</span><span style="color:#032F62;--shiki-dark:#9ECBFF">)</span><span style="color:#032F62;--shiki-dark:#9ECBFF">} min read;
}
}
customElements.define('reading-time', ReadingTime);
That's it. Drop <reading-time></reading-time> anywhere near an article and it self-populates.
Why this matters
Once you internalize that, a lot of framework code starts looking like work you're doing for the browser, not with it.The platform ships features you never reach for because you assume they aren't there yet.