<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>Colas.Nahaboo.net - zola</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/zola/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/zola/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>
    <entry xml:lang="en">
        <title>Migration of this site to the new Zola Tera2</title>
        <published>2026-08-06T00:00:00+00:00</published>
        <updated>2026-08-06T00:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://colas.nahaboo.net/blog/migration-of-this-site-to-the-new-zola-tera2/"/>
        <id>https://colas.nahaboo.net/blog/migration-of-this-site-to-the-new-zola-tera2/</id>
        
        <content type="html" xml:base="https://colas.nahaboo.net/blog/migration-of-this-site-to-the-new-zola-tera2/">&lt;p&gt;I migrated this site to &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://www.getzola.org/&quot;&gt;Zola&lt;/a&gt; 0.23, released yesterday with the new &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://keats.github.io/tera/&quot;&gt;Tera 2&lt;/a&gt; template engine (Well, 0.23.1 from today actually). When I &lt;a href=&quot;/blog/a-new-version-of-the-site-for-2026-in-zola/&quot;&gt;chose Zola&lt;/a&gt; last March, I praised its creator for not chasing the latest fad. I stand by it: 0.23 breaks my templates, but it does it in a cleaner way.&lt;/p&gt;
&lt;p&gt;Tera 2 removes the old macros and imports, and Zola 0.23 removes the shortcodes. Everything is now a namespaced &lt;strong&gt;component&lt;/strong&gt;. A macro that I used to import and call like this:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #4C4F69; background-color: #EFF1F5;&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{% import &amp;quot;macros.html&amp;quot; as utils %}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{% macro show_link(url) %}&amp;lt;a href=&amp;quot;{{ url }}&amp;quot;&amp;gt;{{ url }}&amp;lt;/a&amp;gt;{% endmacro %}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{{ utils::show_link(url=link) }}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;is now a global component with no import at all:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #4C4F69; background-color: #EFF1F5;&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{% component site.show_link(url) %}&amp;lt;a href=&amp;quot;{{ url }}&amp;quot;&amp;gt;{{ url }}&amp;lt;/a&amp;gt;{% endcomponent %}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{{&amp;lt;site.show_link url={link} /&amp;gt;}}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The big philosophical change: components are global and no longer inherit the caller context. Every variable — &lt;code&gt;page&lt;/code&gt;, &lt;code&gt;section&lt;/code&gt;, &lt;code&gt;config&lt;/code&gt;, &lt;code&gt;current_path&lt;/code&gt; — must be passed explicitly as an argument. More verbose, but it makes dependencies visible. I like that more and more: one way to do it, and it is visible.&lt;/p&gt;
&lt;p&gt;Tera 2 is also &lt;strong&gt;strict&lt;/strong&gt;: a missing variable is now a hard error. My templates use optional chaining (&lt;code&gt;?.&lt;/code&gt;) for anything optional, which catches typos at build time instead of silently rendering broken pages.&lt;/p&gt;
&lt;p&gt;What concretely changed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;concat&lt;/code&gt; and &lt;code&gt;slice&lt;/code&gt; filters are gone → array spread and slicing&lt;/li&gt;
&lt;li&gt;&lt;code&gt;get_env&lt;/code&gt; is gone → &lt;code&gt;config.mode == &quot;serve&quot;&lt;/code&gt; for local editor links&lt;/li&gt;
&lt;li&gt;&lt;code&gt;include ... ignore missing&lt;/code&gt; is invalid → empty hook templates&lt;/li&gt;
&lt;li&gt;&lt;code&gt;get_page&lt;/code&gt; with &lt;code&gt;fatal=false&lt;/code&gt; → canonical path with &lt;code&gt;lang&lt;/code&gt; and &lt;code&gt;allow_missing=true&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;get_taxonomy_url&lt;/code&gt; with &lt;code&gt;name&lt;/code&gt; → &lt;code&gt;term&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/keats/tera/blob/master/MIGRATION.md&quot;&gt;migration guide&lt;/a&gt; was my best map through all of this.&lt;/p&gt;
&lt;p&gt;I use the &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://www.getzola.org/themes/apollo/&quot;&gt;Apollo&lt;/a&gt; theme as a git submodule, and as it does not support Zola 0.23 yet, I keep a small local template-only patch of it until upstream migrates. The design, CSS, and JavaScript are untouched: this was a syntax migration, not a redesign, and I did not rewrite my post contents either.&lt;/p&gt;
&lt;h2 id=&quot;use-of-ai&quot;&gt;Use of AI&lt;a class=&quot;zola-anchor&quot; href=&quot;#use-of-ai&quot; aria-label=&quot;Anchor link for: use-of-ai&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I chose to have AI help me migrate my site. It worked flawlessly, so I encourage you to do the same. AI work especially well with Zola, as it is opinionated, and that means that there are clear ways of doing things so that AI agents do not get lost in rabbit holes trying to pursue all the possibilities of more flexible systems like Hugo.&lt;/p&gt;
&lt;p&gt;For reference here is the first prompt I gave it to prepare the plan for the migration:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #4C4F69; background-color: #EFF1F5;&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;I want you to only create a document for agents ai/agents/tera2.md to summarize&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;how tera2 of Zola 0.23 differs from tera1 of Zola 0.22, and the plan to migrate&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;my site from Zola 0.23 to 0.22.&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&gt;Examine the differences between Zola 0.22 and 0.23, not just tera.&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&gt;Docs:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Tera2 https://keats.github.io/tera/&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Migration guide Tera1 → Tera2 https://github.com/Keats/tera/blob/master/MIGRATION.md&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Zola 0.23 docs https://www.getzola.org/documentation/getting-started/overview/&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&gt;Take your time, be thourough, but do not modify yet any file of my site except&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;the ones in ai/agents/ and AGENTS.md&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&gt;Then, plan the migration of this site to zola 0.23&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;1. not modifying the contents of the posts unless necessary for tera2 changes&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;2. not changing the look and feel, the UI of the site&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;3. adapting the various templates&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;4. changing my local copy of the Apollo theme I use if needed.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   These changes are a stopgap measure until upstream apollo is migrated to Zola 0.23&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I use Deepseek v4 flash normally, but chose to use GPT-5.6 Luna for this migration as it is currently quite cheap and I trust it more to not go change unecessary files. And Deepseek as no vision, so it cannot check the actual visual results of changes. It helped me greatly for this migration, without any problems.&lt;/p&gt;
&lt;h3 id=&quot;summary-of-ai-actions&quot;&gt;Summary of AI actions:&lt;a class=&quot;zola-anchor&quot; href=&quot;#summary-of-ai-actions&quot; aria-label=&quot;Anchor link for: summary-of-ai-actions&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Replaced Tera 1 macros, imports, &lt;code&gt;self::&lt;/code&gt; calls, and macro namespaces with
namespaced Tera 2 components in the root templates and Apollo templates.&lt;/li&gt;
&lt;li&gt;Converted the local language-listing, page, navigation, debug, file
inclusion, and section-name helpers to components.&lt;/li&gt;
&lt;li&gt;Converted the site shortcode implementations to components in
&lt;code&gt;templates/macros/shortcodes.html&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Converted active post calls from the removed Zola shortcode syntax to Tera 2
component syntax. This includes YouTube, snapshots, attachments, Mermaid,
static-file inclusion, legacy overlays, obsolete notices, and SVG icons.&lt;/li&gt;
&lt;li&gt;Passed explicit &lt;code&gt;base_path&lt;/code&gt; values to components that used to receive
&lt;code&gt;page.path&lt;/code&gt; implicitly from Zola shortcodes.&lt;/li&gt;
&lt;li&gt;Updated &lt;code&gt;get_page&lt;/code&gt; calls to use the canonical path with &lt;code&gt;lang=&quot;fr&quot;&lt;/code&gt; and
&lt;code&gt;allow_missing=true&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Updated &lt;code&gt;get_taxonomy_url&lt;/code&gt; calls from &lt;code&gt;name&lt;/code&gt; to &lt;code&gt;term&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Replaced the removed &lt;code&gt;get_env&lt;/code&gt; call with &lt;code&gt;config.mode == &quot;serve&quot;&lt;/code&gt; for the
local editor links.&lt;/li&gt;
&lt;li&gt;Replaced the removed &lt;code&gt;concat&lt;/code&gt; and &lt;code&gt;slice&lt;/code&gt; filters with Tera 2 array spread
and slicing, and changed the &lt;code&gt;object&lt;/code&gt; test to &lt;code&gt;map&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Added empty Apollo hook templates because Tera 2 no longer accepts
&lt;code&gt;include ... ignore missing&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Updated &lt;code&gt;dev/bin/site-check&lt;/code&gt; to recognize the new legacy component syntax.&lt;/li&gt;
&lt;li&gt;Updated &lt;code&gt;static/ZOLA&lt;/code&gt; to &lt;code&gt;zola 0.23.0&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Converting Zola pages from TOML to YAML</title>
        <published>2026-04-07T00:00:00+00:00</published>
        <updated>2026-04-07T00:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://colas.nahaboo.net/code/converting-zola-pages-from-toml-to-yaml/"/>
        <id>https://colas.nahaboo.net/code/converting-zola-pages-from-toml-to-yaml/</id>
        
        <content type="html" xml:base="https://colas.nahaboo.net/code/converting-zola-pages-from-toml-to-yaml/">&lt;p&gt;Since &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://www.getzola.org/&quot;&gt;Zola&lt;/a&gt; now supports frontmatter in its markdown pages in the &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://yaml.org/&quot;&gt;YAML&lt;/a&gt; format in addition to the original &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://toml.io/&quot;&gt;TOML&lt;/a&gt;, I decided to migrate all my pages to YAML, as it is both easier to read and write for humans, and natively understood by a majority of tools (Emacs, marktext, obsidian, vs-code, typos, zettlt...)&lt;/p&gt;
&lt;p&gt;So I wrote a small bash script for the conversion, &lt;code&gt;site-toml2yaml&lt;/code&gt; (all my personal site management tools start with the prefix &lt;code&gt;site-&lt;/code&gt; for taking advantage of the tab-completion).
It should run on any Linux or Unix system, and it requires &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/TomWright/dasel&quot;&gt;dasel&lt;/a&gt; and the usual gang: grep, sed, head...&lt;/p&gt;
&lt;p&gt;Note that you could also use &lt;strong&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://gohugo.io/commands/hugo_convert/&quot;&gt;hugo convert&lt;/a&gt;&lt;/strong&gt;, but I haven&#39;t tested it.&lt;/p&gt;
&lt;p&gt;You can grab it at my &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ColasNahaboo/colas-zola-tools&quot;&gt;zola-tools repository&lt;/a&gt;. This repository is quite empty for now, but I will use it to publish the various small tools I use to maintain this public site.&lt;/p&gt;
&lt;h2 id=&quot;usage&quot;&gt;Usage&lt;a class=&quot;zola-anchor&quot; href=&quot;#usage&quot; aria-label=&quot;Anchor link for: usage&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;site-toml2yaml&lt;/strong&gt; &lt;em&gt;md-files or directories...&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Converts in place the frontmatter of Markdown files from TOML to YAML.&lt;br /&gt;
It recurses in directories, but looking only for &lt;code&gt;*.md&lt;/code&gt; files and sub-dirs.&lt;br /&gt;
Without argument, run on &lt;code&gt;${ZOLA_ROOT}/content/&lt;/code&gt; &lt;br /&gt;
Should work on any Markdown files with TOML frontmatter.&lt;/p&gt;
&lt;p&gt;You should only run it on your content/ dir normally. For instance running it
on your themes/ dir will risk messing your imported themes&lt;/p&gt;
&lt;p&gt;WARNING: it modifies the files in place, so MAKE A BACKUP before running it!&lt;/p&gt;
&lt;h2 id=&quot;why-converting-to-yaml&quot;&gt;Why converting to YAML?&lt;a class=&quot;zola-anchor&quot; href=&quot;#why-converting-to-yaml&quot; aria-label=&quot;Anchor link for: why-converting-to-yaml&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;TOML is arguably a &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://npf.io/2014/08/intro-to-toml/&quot;&gt;better configuration file format&lt;/a&gt; than YAML, but only if you want to use it as a simpler JSON, i.e. a well-specified, unambiguous way to represent any data.&lt;/p&gt;
&lt;p&gt;However, I feel that for the simple and limited use case of Zola pages frontmatter, it is overkill and thus a real pain to read and edit by hand. Let&#39;s take an example:&lt;/p&gt;
&lt;div class=&#39;tables-horiz meta&#39;&gt;
&lt;div&gt;
&lt;h3&gt;TOML&lt;/h3&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #4C4F69; background-color: #EFF1F5;&quot; &gt;&lt;code data-lang=&quot;toml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;[&lt;/span&gt;&lt;span&gt;table1&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;    foo&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt; &amp;quot;bar&amp;quot;&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: #7C7F93;&quot;&gt;    [&lt;/span&gt;&lt;span&gt;table1.nested_table&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;        baz&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt; &amp;quot;bat&amp;quot;&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: #7C7F93;&quot;&gt;[[&lt;/span&gt;&lt;span&gt;comments&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;author&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt; &amp;quot;Nate&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;text&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt; &amp;quot;Great Article!&amp;quot;&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: #7C7F93;&quot;&gt;[[&lt;/span&gt;&lt;span&gt;comments&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;author&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt; &amp;quot;Anonymous&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;text&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt; &amp;quot;Love it!&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div&gt;
&lt;h3&gt;YAML&lt;/h3&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #4C4F69; background-color: #EFF1F5;&quot; &gt;&lt;code data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #1E66F5;&quot;&gt;comments&lt;/span&gt;&lt;span style=&quot;color: #179299;&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 style=&quot;color: #1E66F5;&quot;&gt; author&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt; Nate&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #1E66F5;&quot;&gt;    text&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt; Great Article!&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 style=&quot;color: #1E66F5;&quot;&gt; author&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt; Anonymous&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #1E66F5;&quot;&gt;    text&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt; Love it!&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #1E66F5;&quot;&gt;table1&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #1E66F5;&quot;&gt;  foo&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt; bar&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #1E66F5;&quot;&gt;  nested_table&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #1E66F5;&quot;&gt;    baz&lt;/span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #40A02B;&quot;&gt; bat&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;See? Especially for people like me with a Unix culture, the YAML format seems both more natural (resembling the format of email or HTTP headers, the use of indentation à la Python), and TOML has a repulsive Windows flavor. Add to this that most of the tools I use understand natively YAML frontmatter but not TOML, and the decision is easy.&lt;/p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>A new version of the site for 2026, in Zola</title>
        <published>2026-03-19T00:00:00+00:00</published>
        <updated>2026-03-22T00:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://colas.nahaboo.net/blog/a-new-version-of-the-site-for-2026-in-zola/"/>
        <id>https://colas.nahaboo.net/blog/a-new-version-of-the-site-for-2026-in-zola/</id>
        
        <content type="html" xml:base="https://colas.nahaboo.net/blog/a-new-version-of-the-site-for-2026-in-zola/">&lt;p&gt;I &lt;a href=&quot;/blog/a-new-version-of-the-site-for-2021&quot;&gt;said&lt;/a&gt; that I wanted to migrate this web site to a &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://en.wikipedia.org/wiki/Static_site_generator&quot;&gt;SSG&lt;/a&gt; (Static Site Generator).
And, after 5 years of procrastination, Ta-Da! here is the new site!&lt;/p&gt;
&lt;p&gt;Initially, I was planning to use &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://gohugo.io/&quot;&gt;Hugo&lt;/a&gt;, since I love coding in Go, and was not planning to learn Rust (Hugo is coded in Go and Zola in Rust), but I finally chose &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://www.getzola.org/&quot;&gt;Zola&lt;/a&gt; over Hugo for many reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Zola has basically all the performance advantages of Hugo, and is also a single binary, with no need for databases, docker, snap, flatpak...&lt;/li&gt;
&lt;li&gt;Zola is &lt;strong&gt;opinionated&lt;/strong&gt;. This is very important for me, I love more and more the systems were there is &quot;only one way to do it&quot;. Needless to say, I hate perl, and I love Go.&lt;/li&gt;
&lt;li&gt;Zola is stable, and its creator (&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/keats&quot;&gt;&quot;Keats&quot;&lt;/a&gt;) very committed to not chasing the latest fad. This means less risk of an upgrade breaking things.&lt;/li&gt;
&lt;li&gt;Zola template engine, Tera, is both more powerful and more readable than the standard Go template engine used by Hugo.&lt;/li&gt;
&lt;li&gt;Zola comes with more useful built-in features, but no plugins that in my opinion brings complexity, especially in docs, and upgrade hell.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As for the theme, I was looking for a clean responsive modern simple theme, with high scores in the various web performance benchmarks, with search and comments, and some support for multilingual (the site is in English with a few pages also translated in French). I tried the two leaders in the Zola theme ecosystem:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://www.getzola.org/themes/tabi/&quot;&gt;Tabi&lt;/a&gt; I liked the looks but it felt a bit too complex for my needs: as a Zola novice I was afraid to end up relying too much on Tabi-specific feature instead of pure Zola and CSS ones.&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://www.getzola.org/themes/abridge/&quot;&gt;ABridge&lt;/a&gt; I liked the architecture, not so much the design, but I struggled to make it work, it seems the doc still advise settings that break on recent Zola versions.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I thus looked for a simpler theme, and chose:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://www.getzola.org/themes/apollo/&quot;&gt;Apollo&lt;/a&gt; which was perfect for my needs, and simple enough to be easily modified for any of my future needs. I also enabled its support for the  &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://giscus.app/&quot;&gt;Giscus&lt;/a&gt; commenting system.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Porting my contents was made easy because I had anticipated this migration, and used Markdown for my posts in the Foswiki engine, instead of the native Foswiki syntax. So converting was basically adding a frontmatter to the pages, and fixing some links. I converted older posts with pandoc.&lt;/p&gt;
&lt;p&gt;I have made a quick first migration. I will continue working on the CSS architecture and the looks in the following weeks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt; on 2026-03-22: I now am satisfied of the result. I plan to work a bit more to add a search for only the French pages (now the search ignores them), autodetection of the browser light/dark preferences, sort the tags alphabetically in the page info line.&lt;/p&gt;
</content>
        
    </entry>
</feed>
