<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>Colas.Nahaboo.net - html</title>
    <subtitle>Colas Nahaboo personal site, with discussions about programming code, web and computing topics, surfing and SUPing, and various musings.</subtitle>
    <link rel="self" type="application/atom+xml" href="https://colas.nahaboo.net/tags/html/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://colas.nahaboo.net"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-08-31T00:00:00+00:00</updated>
    <id>https://colas.nahaboo.net/tags/html/atom.xml</id>
    <entry xml:lang="en">
        <title>Invent your own HTML tags!</title>
        <published>2026-08-31T00:00:00+00:00</published>
        <updated>2026-08-31T00:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://colas.nahaboo.net/blog/invent-your-own-html-tags/"/>
        <id>https://colas.nahaboo.net/blog/invent-your-own-html-tags/</id>
        
        <content type="html" xml:base="https://colas.nahaboo.net/blog/invent-your-own-html-tags/">&lt;p&gt;The HTML spec has had a proper way to invent your own tags since 2019. Most web developers seem to have missed it.&lt;/p&gt;
&lt;p&gt;Want a simple way to have a word in blue? just use &lt;code&gt;&amp;lt;ink-blue&amp;gt;word&amp;lt;/ink-blue&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;My &lt;a href=&quot;/blog/migration-of-this-site-to-the-new-zola-tera2&quot;&gt;migration of this site to Zola Tera 2&lt;/a&gt; forced me to rethink shortcodes and ad-hoc styling, which is what prompted me to dig into &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://html.spec.whatwg.org/multipage/custom-elements.html&quot;&gt;§4.13 of the HTML standard&lt;/a&gt; for the first time. I found three pieces, all now supported by every current engine.&lt;/p&gt;
&lt;h2 id=&quot;what&quot;&gt;What?&lt;a class=&quot;zola-anchor&quot; href=&quot;#what&quot; aria-label=&quot;Anchor link for: what&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Custom elements are an old idea — Chrome shipped a v0 API in 2014, the cross-browser v1 standard followed a few years later. The pattern is straightforward: extend &lt;code&gt;HTMLElement&lt;/code&gt;, register the class via &lt;code&gt;customElements.define()&lt;/code&gt;. The simplest example, lifted from the spec, is a country-flag icon:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #4C4F69; background-color: #EFF1F5;&quot; &gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color: #DF8E1D;font-style: italic;&quot;&gt; FlagIcon&lt;/span&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color: #DF8E1D;font-style: italic;&quot;&gt; HTMLElement&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;  constructor&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;    super&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D20F39;&quot;&gt;    this&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;.&lt;/span&gt;&lt;span&gt;_countryCode&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt; null&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;  static&lt;/span&gt;&lt;span&gt; observedAttributes&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; =&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt;&amp;quot;country&amp;quot;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;  attributeChangedCallback&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #E64553;font-style: italic;&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color: #E64553;font-style: italic;&quot;&gt; oldValue&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color: #E64553;font-style: italic;&quot;&gt; newValue&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D20F39;&quot;&gt;    this&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;.&lt;/span&gt;&lt;span&gt;_countryCode&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; =&lt;/span&gt;&lt;span&gt; newValue&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D20F39;&quot;&gt;    this&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;_updateRendering&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;  connectedCallback&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D20F39;&quot;&gt;    this&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;_updateRendering&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;  get&lt;/span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt; country&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;() {&lt;/span&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt; return&lt;/span&gt;&lt;span style=&quot;color: #D20F39;&quot;&gt; this&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;.&lt;/span&gt;&lt;span&gt;_countryCode&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;  set&lt;/span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt; country&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #E64553;font-style: italic;&quot;&gt;v&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;) {&lt;/span&gt;&lt;span style=&quot;color: #D20F39;&quot;&gt; this&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;setAttribute&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt;&amp;quot;country&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;,&lt;/span&gt;&lt;span&gt; v)&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;customElements&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;define&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt;&amp;quot;flag-icon&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;,&lt;/span&gt;&lt;span&gt; FlagIcon)&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And use it as:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #4C4F69; background-color: #EFF1F5;&quot; &gt;&lt;code data-lang=&quot;html&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #179299;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #1E66F5;&quot;&gt;flag-icon&lt;/span&gt;&lt;span style=&quot;color: #DF8E1D;&quot;&gt; country&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt;&amp;quot;nl&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #1E66F5;&quot;&gt;flag-icon&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;strong&gt;only&lt;/strong&gt; rule I had to learn the hard way: the name must contain a hyphen. &lt;code&gt;&amp;lt;flag-icon&amp;gt;&lt;/code&gt; works; &lt;code&gt;&amp;lt;flagicon&amp;gt;&lt;/code&gt; does not. The hyphen is the spec&#39;s way of ensuring your tag can never collide with a future native one.&lt;/p&gt;
&lt;p&gt;Three lifecycle hooks do most of the heavy lifting — &lt;code&gt;connectedCallback&lt;/code&gt;, &lt;code&gt;disconnectedCallback&lt;/code&gt;, &lt;code&gt;attributeChangedCallback&lt;/code&gt;. There is also &lt;code&gt;adoptedCallback&lt;/code&gt; (when your element moves documents) and the newer &lt;code&gt;connectedMoveCallback&lt;/code&gt; (added so the element keeps its state when moved inside the DOM without being disconnected). That is all there is to it.&lt;/p&gt;
&lt;h2 id=&quot;elementinternals&quot;&gt;ElementInternals&lt;a class=&quot;zola-anchor&quot; href=&quot;#elementinternals&quot; aria-label=&quot;Anchor link for: elementinternals&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This is the piece that surprised me the most. Set one static field and call one method, and &lt;strong&gt;your custom element behaves like a real form control&lt;/strong&gt; — it appears in &lt;code&gt;form.elements&lt;/code&gt;, submits values, has &lt;code&gt;validity&lt;/code&gt;, &lt;code&gt;willValidate&lt;/code&gt;, and &lt;code&gt;labels&lt;/code&gt;. It also carries default accessibility semantics, so screen readers treat your tag like the proper role without you attaching ARIA manually:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #4C4F69; background-color: #EFF1F5;&quot; &gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color: #DF8E1D;font-style: italic;&quot;&gt; MyCheckbox&lt;/span&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color: #DF8E1D;font-style: italic;&quot;&gt; HTMLElement&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;  static&lt;/span&gt;&lt;span&gt; formAssociated&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color: #FE640B;&quot;&gt; true&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;  static&lt;/span&gt;&lt;span&gt; observedAttributes&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; =&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt;&amp;quot;checked&amp;quot;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;  constructor&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;    super&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D20F39;&quot;&gt;    this&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;.&lt;/span&gt;&lt;span&gt;_internals&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color: #D20F39;&quot;&gt; this&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;attachInternals&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D20F39;&quot;&gt;    this&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;.&lt;/span&gt;&lt;span&gt;_internals&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;.&lt;/span&gt;&lt;span&gt;role&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt; &amp;quot;checkbox&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D20F39;&quot;&gt;    this&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;.&lt;/span&gt;&lt;span&gt;_internals&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;.&lt;/span&gt;&lt;span&gt;ariaChecked&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt; &amp;quot;false&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D20F39;&quot;&gt;    this&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;addEventListener&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt;&amp;quot;click&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color: #D20F39;&quot;&gt; this&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;.&lt;/span&gt;&lt;span&gt;_onClick&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;bind&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: #D20F39;&quot;&gt;this&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;  attributeChangedCallback&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #E64553;font-style: italic;&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color: #E64553;font-style: italic;&quot;&gt; oldValue&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color: #E64553;font-style: italic;&quot;&gt; newValue&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D20F39;&quot;&gt;    this&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;.&lt;/span&gt;&lt;span&gt;_internals&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;setFormValue&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: #D20F39;&quot;&gt;this&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;.&lt;/span&gt;&lt;span&gt;checked&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; ?&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt; &amp;quot;on&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; :&lt;/span&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt; null&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D20F39;&quot;&gt;    this&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;.&lt;/span&gt;&lt;span&gt;_internals&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;.&lt;/span&gt;&lt;span&gt;ariaChecked&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color: #D20F39;&quot;&gt; this&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;.&lt;/span&gt;&lt;span&gt;checked&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;  get&lt;/span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt; checked&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;() {&lt;/span&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt; return&lt;/span&gt;&lt;span style=&quot;color: #D20F39;&quot;&gt; this&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;hasAttribute&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt;&amp;quot;checked&amp;quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;  set&lt;/span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt; checked&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #E64553;font-style: italic;&quot;&gt;flag&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;) {&lt;/span&gt;&lt;span style=&quot;color: #D20F39;&quot;&gt; this&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;toggleAttribute&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt;&amp;quot;checked&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt; Boolean&lt;/span&gt;&lt;span&gt;(flag))&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;  _onClick&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;() {&lt;/span&gt;&lt;span style=&quot;color: #D20F39;&quot;&gt; this&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;.&lt;/span&gt;&lt;span&gt;checked&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; = !&lt;/span&gt;&lt;span style=&quot;color: #D20F39;&quot;&gt;this&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;.&lt;/span&gt;&lt;span&gt;checked&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;customElements&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;define&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt;&amp;quot;my-checkbox&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;,&lt;/span&gt;&lt;span&gt; MyCheckbox)&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A nice extra: &lt;code&gt;this._internals.states.add(&quot;checked&quot;)&lt;/code&gt; plus the CSS &lt;code&gt;:state(checked)&lt;/code&gt; pseudo-class lets you flip states from JavaScript and style them with CSS, no attribute-flipping dance.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ElementInternals&lt;/code&gt; has been &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals&quot;&gt;Baseline widely available since March 2023&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;declarative-shadow-dom&quot;&gt;Declarative Shadow DOM&lt;a class=&quot;zola-anchor&quot; href=&quot;#declarative-shadow-dom&quot; aria-label=&quot;Anchor link for: declarative-shadow-dom&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The piece I missed most. &lt;code&gt;&amp;lt;template shadowrootmode=&quot;open&quot;&amp;gt;&lt;/code&gt; inside your custom element lets the shadow tree be &lt;strong&gt;rendered from pure HTML&lt;/strong&gt; — no JavaScript required for the initial render:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #4C4F69; background-color: #EFF1F5;&quot; &gt;&lt;code data-lang=&quot;html&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #179299;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #1E66F5;&quot;&gt;my-component&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #179299;&quot;&gt;  &amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #1E66F5;&quot;&gt;template&lt;/span&gt;&lt;span style=&quot;color: #DF8E1D;&quot;&gt; shadowrootmode&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt;&amp;quot;open&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #179299;&quot;&gt;    &amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #1E66F5;&quot;&gt;style&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #1E66F5;&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color: #1E66F5;&quot;&gt; color&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;:&lt;/span&gt;&lt;span&gt; tomato&lt;/span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt; }&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color: #1E66F5;&quot;&gt;style&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #179299;&quot;&gt;    &amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #1E66F5;&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;Hello from a shadow root&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #1E66F5;&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #179299;&quot;&gt;  &amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #1E66F5;&quot;&gt;template&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #179299;&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #1E66F5;&quot;&gt;my-component&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For static sites this is a game changer — Zola, Hugo, plain Markdown — your shadow tree ships as part of the HTML, the browser wires it up at parse time. No flash of unstyled component, no JS bundle needed.&lt;/p&gt;
&lt;p&gt;When upgrading from HTML that contains a declarative shadow root, check &lt;code&gt;ElementInternals.shadowRoot&lt;/code&gt; first — if it is already populated, your markup already shipped the tree and you should not call &lt;code&gt;attachShadow()&lt;/code&gt; again.&lt;/p&gt;
&lt;p&gt;Browser support landed between March 2023 and February 2024 — Chrome 111+, Safari 16.4+, Firefox 123+. There is also &lt;code&gt;shadowrootclonable&lt;/code&gt; and &lt;code&gt;shadowrootdelegatesfocus&lt;/code&gt; for cloning and focus delegation. See the &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://web.dev/articles/declarative-shadow-dom&quot;&gt;Declarative Shadow DOM guide on web.dev&lt;/a&gt; for the details.&lt;/p&gt;
&lt;h2 id=&quot;closing&quot;&gt;Closing&lt;a class=&quot;zola-anchor&quot; href=&quot;#closing&quot; aria-label=&quot;Anchor link for: closing&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Three pieces, all in the HTML spec, all shipped in every current browser. The only thing missing was awareness. So, awareness raised — we no longer have any excuse to keep reinventing component frameworks. :-)&lt;/p&gt;
</content>
        
    </entry>
</feed>
