<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
  xmlns:content="http://purl.org/rss/1.0/modules/content/"
  xmlns:atom="http://www.w3.org/2005/Atom"><channel>
    <title>Lea&#39;s Blog</title>
    <atom:link href="https://lea.codes/rss.xml" rel="self" type="application/rss+xml" />
    <link>https://lea.codes</link>
    <description>This is the blog of Lea Rosema, frontend developer with accessibility focus, based in Hamburg</description>
    <pubDate>Wed, 21 Jan 2026 00:00:00 GMT</pubDate>
    <lastBuildDate>Wed, 21 Jan 2026 10:49:27 GMT</lastBuildDate>
    <language>en</language>
    <copyright>(c) 2026 Lea Rosema</copyright>
    <generator>Eleventy v3.1.2</generator>
    <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
    <managingEditor>lea@lea.lgbt (Lea Rosema)</managingEditor>
    <item>
      <title><![CDATA[Eleventy: Sorted Collections]]></title><link>https://lea.codes/posts/2026-01-21-eleventy-sorted-collections/</link>
      <guid>https://lea.codes/posts/2026-01-21-eleventy-sorted-collections/</guid>
      <author>lea@lea.lgbt (Lea Rosema)</author>
      <pubDate>Wed, 21 Jan 2026 00:00:00 GMT</pubDate>
      <description><![CDATA[]]></description>
      <category><![CDATA[post]]></category>
      <category><![CDATA[eleventy]]></category>
      <category><![CDATA[static-site]]></category>
      <content:encoded><![CDATA[<p>Ever wanted to have more control over how your Eleventy collections are sorted?
By default, Eleventy sorts collection items by date. This is a perfect default for
blog sites.</p>
<p>Right now, I'm building more of a knowledgebase/book kind of thing.
And <code>date</code> is not the most important criteria to sort all articles.
Though, it's still useful to display the most recent edited articles at some place.</p>
<p>Nevertheless, I wanted my collection to be the in the same
order as they occur in my table of content file.
Which happens to be the root <code>src/index.md</code>
in my case.</p>
<p>Also, I didn't want to maintain the order by frontmatter or additional json data.</p>
<p>This is why I built something that parses the file and
creates a sorted collection from <code>collection.all</code>,
sorted by occurrence in my TOC file:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">createSortedCollection</span><span class="token punctuation">(</span><span class="token parameter">eleventyConfig</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> src <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">readFile</span><span class="token punctuation">(</span><span class="token string">'src/index.md'</span><span class="token punctuation">,</span> <span class="token string">'utf8'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> linkRegex <span class="token operator">=</span> <span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">\[.*?\]\((.*?)\)</span><span class="token regex-delimiter">/</span><span class="token regex-flags">g</span></span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> hrefs <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">'./src/index.md'</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
  <span class="token keyword">let</span> m<span class="token punctuation">;</span>
  <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>m <span class="token operator">=</span> linkRegex<span class="token punctuation">.</span><span class="token function">exec</span><span class="token punctuation">(</span>src<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">!==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>m<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token operator">?.</span><span class="token function">startsWith</span><span class="token punctuation">(</span><span class="token string">'https://'</span><span class="token punctuation">)</span> <span class="token operator">===</span> <span class="token boolean">false</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">const</span> file <span class="token operator">=</span> <span class="token string">'./src/'</span> <span class="token operator">+</span> m<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
      hrefs<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>file<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>
  eleventyConfig<span class="token punctuation">.</span><span class="token function">addCollection</span><span class="token punctuation">(</span><span class="token string">"toc"</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">collectionsApi</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>

  <span class="token keyword">return</span> collectionsApi<span class="token punctuation">.</span><span class="token function">getAll</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token parameter">a</span> <span class="token operator">=></span> hrefs<span class="token punctuation">.</span><span class="token function">indexOf</span><span class="token punctuation">(</span>a<span class="token punctuation">.</span>inputPath<span class="token punctuation">)</span> <span class="token operator">>=</span> <span class="token number">0</span><span class="token punctuation">)</span>
    <span class="token punctuation">.</span><span class="token function">sort</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">a<span class="token punctuation">,</span> b</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">const</span> aIndex <span class="token operator">=</span> hrefs<span class="token punctuation">.</span><span class="token function">indexOf</span><span class="token punctuation">(</span>a<span class="token punctuation">.</span>inputPath<span class="token punctuation">)</span>
      <span class="token keyword">const</span> bIndex <span class="token operator">=</span> hrefs<span class="token punctuation">.</span><span class="token function">indexOf</span><span class="token punctuation">(</span>b<span class="token punctuation">.</span>inputPath<span class="token punctuation">)</span>
    
    <span class="token keyword">return</span> aIndex <span class="token operator">-</span> bIndex
  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
 <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<h2>How to use this?</h2>
<p>You can add this to the Eleventy config via <code>eleventyConfig.addPlugin(createSortedCollection)</code>.</p>
<p>Then, I can put &quot;previous&quot; and &quot;next&quot; links onto my pages.</p>
<p>I use this on a german study site where I prepare myself for a certification,
the Certified Professional User Experience, Foundation Level. I had the need to transfer it to a simpler-language version and provide it in HTML. There's a PDF for learning, but I failed learning from it, due to complex sentences and me being demotivated quickly when
dealing with PDFs.</p>
<p>The site is <a href="https://learosema.github.io/cpux-prep/">https://learosema.github.io/cpux-prep/</a>, in german. The little prev next links at the bottom of the page are driven by the above TOC collection:</p>
<p><a href="https://github.com/learosema/cpux-prep/blob/main/src/_layouts/default.njk#L35">https://github.com/learosema/cpux-prep/blob/main/src/_layouts/default.njk#L35</a></p>
<h2>Caveats</h2>
<p>This is not a one-fits-all solution and probably needs some work to make it one.
But that's fine for me, as of current. It doesn't need to be a one-fits-all-solution.
YAGNI maybe.</p>
<p>Also, one tricky part is getting from the hrefs to something that identifies the items in the collection correctly. I used the inputPath and prepended my base directory to the <code>href</code>. Also, my URLs in the TOC are all relative. I use a custom permalink setting where I just do relative links to markdown files which are post-processed from .md to .html afterwards :).</p>
<p>In the default eleventy config, this needs to be adjusted. You could
use the <code>filePathStem</code> property and prepend the base URL there.</p>
<p>It can only handle markdown and the path to the TOC file is hardcoded.</p>
<p>To make it more agnostic to the inut file format, you could process the input once and
then process the output html file via a DOM parser library.</p>
<p>Have I mentioned that I find <a href="https://github.com/WebReflection/linkedom">linkedom</a> pretty great
for server-side HTML processing tasks?</p>
<h2>Resources</h2>
<ul>
<li><a href="https://www.11ty.dev/docs/collections-api/">Eleventy documentation, Collections API</a></li>
</ul>

      ]]></content:encoded>
    </item><item>
      <title><![CDATA[Eleventy: Autopopulating the title variable from the template]]></title><link>https://lea.codes/posts/2026-01-16-eleventy-autopopulating-the-title-variable-from-the-template/</link>
      <guid>https://lea.codes/posts/2026-01-16-eleventy-autopopulating-the-title-variable-from-the-template/</guid>
      <author>lea@lea.lgbt (Lea Rosema)</author>
      <pubDate>Fri, 16 Jan 2026 00:00:00 GMT</pubDate>
      <description><![CDATA[]]></description>
      <category><![CDATA[post]]></category>
      <category><![CDATA[eleventy]]></category>
      <category><![CDATA[static-site]]></category>
      <content:encoded><![CDATA[<p>One very common way to provide a title in Eleventy is by using frontmatter data inside
the template.</p>
<p>For example, this looks like in the following code snippet:</p>
<pre class="language-md"><code class="language-md"><span class="token front-matter-block"><span class="token punctuation">---</span>
<span class="token front-matter yaml language-yaml"><span class="token key atrule">title</span><span class="token punctuation">:</span> Hello World</span>
<span class="token punctuation">---</span></span>
<span class="token title important"><span class="token punctuation">#</span> {{ title }}</span>

Lorem ipsum is a pretty cool dummy text dolor sit amet.</code></pre>
<p>Sometimes, I want to provide the title by just adding an h1 to my markdown.
It's less verbose:</p>
<pre class="language-md"><code class="language-md"><span class="token title important"><span class="token punctuation">#</span> Hello World!</span>

Lorem ipsum is a pretty cool dummy text dolor sit amet.</code></pre>
<p>This has the downside that there is no title variable
populated which I can reuse to fill my <code>&lt;title&gt;</code> tag in my
layout file.</p>
<h2>Using an eleventyComputed variable as title</h2>
<p>So, I found a way to make that work via an eleventyComputed.
Since version 3, eleventy provides the raw input in the
<a href="https://www.11ty.dev/docs/data-eleventy-supplied/">eleventy supplied data</a>.</p>
<p>The raw input is the unprocessed unparsed template body
minus the template's frontmatter.</p>
<p>So, you can provide a computed title variable.
When the input file format is markdown, it is parsed via a regular expression:</p>
<pre class="language-js"><code class="language-js">eleventyConfig<span class="token punctuation">.</span><span class="token function">addGlobalData</span><span class="token punctuation">(</span><span class="token string">"eleventyComputed.title"</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
  <span class="token keyword">return</span> <span class="token punctuation">(</span><span class="token parameter">data</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>data<span class="token punctuation">.</span>page<span class="token punctuation">.</span>inputPath<span class="token operator">?.</span><span class="token function">endsWith</span><span class="token punctuation">(</span><span class="token string">'.md'</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">const</span> titleRegex <span class="token operator">=</span> <span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">^\#\s(.*?)\r?\n</span><span class="token regex-delimiter">/</span></span><span class="token punctuation">;</span>
      <span class="token keyword">const</span> match <span class="token operator">=</span> data<span class="token punctuation">.</span>page<span class="token punctuation">.</span>rawInput<span class="token operator">?.</span><span class="token function">match</span><span class="token punctuation">(</span>titleRegex<span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token keyword">return</span> match <span class="token operator">?</span> match<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span> <span class="token operator">:</span> <span class="token string">''</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    <span class="token keyword">return</span> <span class="token string">''</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre>
<h2>Further considerations</h2>
<p>This is specific to markdown though. Also, it isn't perfect.
Further formatting inside the h1 isn't processed.</p>
<p>You could add that with a few further regular expressions, like I did with my
non-complete <a href="https://github.com/sissijs/sissi/blob/main/src/transforms/markdown.js">mini-markdown implementation</a>, or just use a complete markdown parser like
<code>markdown-it</code> at that point. Somehow I refuse to do that because
Eleventy already does that at the template processing point, as that could have a
bigger performance impact.</p>
<h2>Other formats and advanced use-cases</h2>
<p>For other formats and/or more advanced use-cases,
you could also combine it with an HTML parser,
like <a href="https://github.com/WebReflection/linkedom">linkedom</a>.
I'm in love with that library; it's great for
server-side HTML processing.</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">import</span> <span class="token punctuation">{</span> parseHTML <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'linkedom'</span>

<span class="token comment">/* ... */</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>data<span class="token punctuation">.</span>page<span class="token punctuation">.</span>inputPath<span class="token operator">?.</span><span class="token function">endsWith</span><span class="token punctuation">(</span><span class="token string">'.html'</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> <span class="token punctuation">{</span> document <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">parseHTML</span><span class="token punctuation">(</span>data<span class="token punctuation">.</span>page<span class="token punctuation">.</span>rawInput<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">return</span> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'title'</span><span class="token punctuation">)</span><span class="token punctuation">.</span>textContent<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>In my use-case, handling the markdown headline with the
above regex was sufficient.</p>
<h2>Why?</h2>
<p>This was a side-quest for my try to migrate a site from Jekyll to Eleventy which
provided the title via a single hash sign (<code>#</code>). While Jekyll populated the
title variable from it, Eleventy didn't do this.</p>
<h2>Drawback</h2>
<p>The drawback is performance.
It is an additional parsing step in the build for each markdown file.</p>
<p>This is probably fine for my smaller website.
But I guess this could quickly add up for the larger sites.</p>
<p>My long-term fix for this would be to update all
my files to the frontmatter format. But my brain hates chore tasks.</p>

      ]]></content:encoded>
    </item><item>
      <title><![CDATA[An eleventy config for moving from Jekyll]]></title><link>https://lea.codes/posts/2026-01-15-an-eleventy-config-for-moving-from-jekyll/</link>
      <guid>https://lea.codes/posts/2026-01-15-an-eleventy-config-for-moving-from-jekyll/</guid>
      <author>lea@lea.lgbt (Lea Rosema)</author>
      <pubDate>Thu, 15 Jan 2026 00:00:00 GMT</pubDate>
      <description><![CDATA[a drop-in eleventy configuration to migrate from Jekyll to Eleventy.]]></description>
      <category><![CDATA[post]]></category>
      <category><![CDATA[eleventy]]></category>
      <category><![CDATA[static-site]]></category>
      <content:encoded><![CDATA[<p>Sometimes I have a git repo just with documentation, consisting of markdown files.
Mostly on platforms like GitHub and alike.</p>
<p>A quick and dirty way to publish that to the web is to use <a href="https://jekyllrb.com/">Jekyll</a>.
Github supports Jekyll out of the box, via GitHub Pages.</p>
<p>So, if you have markdown in your repository, you can make Github transform
it automatically to a website, with (almost) zero configuration.</p>
<p>You can provide your own design by HTML layout templates,
by adding a <code>_default/layout.html</code> and additional assets like css, similar to
Eleventy.</p>
<p>The experience for Jekyll is pretty user-friendly, even for non-developers.
Markdown is automatically transformed to HTML with zero configuration.</p>
<p>Moving off jekyll to Eleventy brings a couple advantages: it's super fast and adds
more flexibility, like adding additional post-processing, using CSS-preprocessors
like lightningcss, and it could probably make you more independent from GitHub.</p>
<p>To be fair, you can host Jekyll anywhere else, but
GitHub made it pretty configuration-free and straightforward.</p>
<p>Yes, to be fair, eleventy can do markdown configuration-free, too.</p>
<p>But one thing I needed to change was the URL handling, so it is more like in
Jekyll. Eleventy uses extension-free URLs, which are pretty cool. They are the best
when it comes to end-user usability (shorter, human-readable URLs).
But that's not how Jekyll works.</p>
<p>In my markdown files, I often use several relative links to other markdown files.
Navigating these links from the github-repo-integrated file viewer works as well as
on the published jekyll site, where the links are rewritten as <code>.html</code> links.</p>
<p>I found this behaviour for my anchors pretty cool. I must admit this is
more a &quot;developer experience&quot; feature.</p>
<p>Anyways, a struggle to move off Jekyll to Eleventy could be a missing <code>eleventy.config.js</code>
which you can put into your repo and everything else works as before, without any changes.</p>
<p>So, this is what i did:</p>
<p>First, I changed the global permalink settings to use &quot;resource mode&quot;, like documented
in the <a href="https://www.11ty.dev/docs/permalinks/#remove-trailing-slashes">eleventy documentation</a></p>
<p>Disclaimer: this is usually not recommended, the extension-free URLs are the
user-friendlier ones.</p>
<p>So, maybe refactor later? 🙈</p>
<p>Then, I used <code>linkedom</code> to process my HTML output.
It's a lightweight DOM implementation which let's me parse HTML and manipulate it
with DOM APIs, just like in the browser's JavaScript environment.</p>
<p>So I can loop through all anchor tags, via <code>document.querySelectorAll</code> and handle all
relative links with a <code>.md</code> ending and replace it with <code>.html</code>, like Jekyll does it.</p>
<p>The eleventy setup:</p>
<pre class="language-sh"><code class="language-sh"><span class="token function">npm</span> init <span class="token parameter variable">-y</span>
<span class="token function">npm</span> i @11ty/eleventy <span class="token parameter variable">-D</span>
<span class="token function">npm</span> i linkedom</code></pre>
<p>The resulting <code>eleventy.config.js</code>:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">import</span> <span class="token punctuation">{</span> parseHTML <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'linkedom'</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">eleventyConfig</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>

  eleventyConfig<span class="token punctuation">.</span><span class="token function">addGlobalData</span><span class="token punctuation">(</span><span class="token string">'layout'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token string">'default.html'</span><span class="token punctuation">)</span>
  <span class="token comment">// Set global permalinks to resource.html style</span>
  eleventyConfig<span class="token punctuation">.</span><span class="token function">addGlobalData</span><span class="token punctuation">(</span><span class="token string">"permalink"</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token punctuation">(</span><span class="token parameter">data</span><span class="token punctuation">)</span> <span class="token operator">=></span>
      <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>data<span class="token punctuation">.</span>page<span class="token punctuation">.</span>filePathStem<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">.</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>data<span class="token punctuation">.</span>page<span class="token punctuation">.</span>outputFileExtension<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    eleventyConfig<span class="token punctuation">.</span><span class="token function">addTransform</span><span class="token punctuation">(</span><span class="token string">'remap-md-links'</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">content</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>page<span class="token punctuation">.</span>outputPath <span class="token operator">||</span> <span class="token string">""</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">endsWith</span><span class="token punctuation">(</span><span class="token string">".html"</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">===</span> <span class="token boolean">false</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">return</span> content<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    <span class="token keyword">const</span> <span class="token punctuation">{</span> document <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">parseHTML</span><span class="token punctuation">(</span>content<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">const</span> <span class="token function-variable function">isRelativeLink</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">href</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">^https?:\/\/</span><span class="token regex-delimiter">/</span></span><span class="token punctuation">.</span><span class="token function">test</span><span class="token punctuation">(</span>href<span class="token punctuation">)</span> <span class="token operator">===</span> <span class="token boolean">false</span><span class="token punctuation">;</span>
    <span class="token keyword">const</span> anchors <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelectorAll</span><span class="token punctuation">(</span><span class="token string">'a[href]'</span><span class="token punctuation">)</span>
    <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">const</span> anchor <span class="token keyword">of</span> anchors<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">const</span> href <span class="token operator">=</span> anchor<span class="token punctuation">.</span><span class="token function">getAttribute</span><span class="token punctuation">(</span><span class="token string">'href'</span><span class="token punctuation">)</span> <span class="token operator">||</span> <span class="token string">''</span>
      <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">isRelativeLink</span><span class="token punctuation">(</span>href<span class="token punctuation">)</span> <span class="token operator">&amp;&amp;</span> href<span class="token punctuation">.</span><span class="token function">endsWith</span><span class="token punctuation">(</span><span class="token string">'.md'</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        anchor<span class="token punctuation">.</span><span class="token function">setAttribute</span><span class="token punctuation">(</span><span class="token string">'href'</span><span class="token punctuation">,</span> href<span class="token punctuation">.</span><span class="token function">replace</span><span class="token punctuation">(</span><span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">\.md$</span><span class="token regex-delimiter">/</span></span><span class="token punctuation">,</span> <span class="token string">'.html'</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
      <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>
    <span class="token keyword">return</span> document<span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span>

  <span class="token keyword">return</span> <span class="token punctuation">{</span>
    <span class="token literal-property property">dir</span><span class="token operator">:</span> <span class="token punctuation">{</span>
      <span class="token literal-property property">input</span><span class="token operator">:</span> <span class="token string">'.'</span><span class="token punctuation">,</span>
      <span class="token literal-property property">includes</span><span class="token operator">:</span> <span class="token string">"_includes"</span><span class="token punctuation">,</span>
      <span class="token literal-property property">layouts</span><span class="token operator">:</span> <span class="token string">"_layouts"</span><span class="token punctuation">,</span>
      <span class="token literal-property property">output</span><span class="token operator">:</span> <span class="token string">'dist'</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>Then build with <code>npx eleventy</code> or watch and serve with <code>npx eleventy --serve</code>.</p>
<h2>Isn't that a horrible advice?</h2>
<p>Yes, this will make the URLs of your published site less readable.
At best, it's just an additional <code>.html</code> instead of a trailing
slash. At worst, it gets very long and sprinkled with
hexacdecimal code sequences.</p>
<p>But the usability point about relative markdown links in the git platform's file
viewer is also valid.</p>
<p>In the end, it depends on your and your users' needs.</p>
<h2>&quot;But I want the nicer URLs?&quot;</h2>
<p>If you want the best of both worlds, human-readable urls with trailing slash
in the published website, while keeping relative links to markdown documents, you can
do that, too.</p>
<p>You can keep the default permalink behaviour if you adjust the anchor manipulation post-processing accordingly.</p>
<p>Keep in mind that special characters are slugified by default. This means,
all non-alphabetic characters are replaced with hyphens. This avoids the URL to
look cryptic.</p>
<p>There's an NPM package for that. It's called slugify  (<code>slugify</code>).
But it's small enough to incorporate it into your build yourself, in case you struggle
to npm install it (because JavaScript dependency management is a mess).</p>
<p>A very minimal non-perfect slugify that works for plain english (ASCII characters only) is probably something like this:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">function</span> <span class="token function">slugify</span><span class="token punctuation">(</span><span class="token parameter">str</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">return</span> str<span class="token operator">?.</span><span class="token function">replace</span><span class="token punctuation">(</span><span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">[\W]+</span><span class="token regex-delimiter">/</span><span class="token regex-flags">g</span></span><span class="token punctuation">,</span><span class="token string">'-'</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span></code></pre>
<p>The anchor manipulation code then becomes like this. This splits the URL parts at the slashes and slugifies the path parts and then joins them together again.</p>
<pre class="language-js"><code class="language-js"><span class="token comment">// the anchor manipulation code:</span>
<span class="token comment">// Take the bigger code snippet from above and adjust the line with</span>
<span class="token comment">// anchor.setAttribute accordingly.</span>

anchor<span class="token punctuation">.</span><span class="token function">setAttribute</span><span class="token punctuation">(</span><span class="token string">'href'</span><span class="token punctuation">,</span> 
  href<span class="token punctuation">.</span><span class="token function">replace</span><span class="token punctuation">(</span><span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">\.md$</span><span class="token regex-delimiter">/</span></span><span class="token punctuation">,</span> <span class="token string">'/'</span><span class="token punctuation">)</span>
    <span class="token punctuation">.</span><span class="token function">split</span><span class="token punctuation">(</span><span class="token string">'/'</span><span class="token punctuation">)</span>
    <span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token parameter">str</span> <span class="token operator">=></span> <span class="token function">slugify</span><span class="token punctuation">(</span>str<span class="token punctuation">)</span><span class="token punctuation">)</span>
    <span class="token punctuation">.</span><span class="token function">join</span><span class="token punctuation">(</span><span class="token string">'/'</span><span class="token punctuation">)</span><span class="token punctuation">)</span></code></pre>
<h2>EDIT: Misleading Title</h2>
<p>For transparency: I originally named this
article &quot;an eleventy config for moving from GitHub Pages&quot; before.</p>
<p>This is a little misleading and baity,
as <a href="https://lea.lgbt/@kiko@indieweb.social">Kristof Zerbe</a> pointed out.
Also, as he mentioned, you can perfectly deploy an Eleventy site to GitHub.
So, this is more about Jekyll, not about moving away from GitHub.</p>
<p>I initially took this title because GitHub Pages integrated Jekyll so seamlessly.</p>
<p>People might resist changing to other platform where
they have to write an additional YAML file. So, this seamlessness could
be something that stops them for migrating away from GitHub.</p>

      ]]></content:encoded>
    </item><item>
      <title><![CDATA[Aborting async JavaScript]]></title><link>https://lea.codes/posts/2025-09-07-aborting-async-javascript/</link>
      <guid>https://lea.codes/posts/2025-09-07-aborting-async-javascript/</guid>
      <author>lea@lea.lgbt (Lea Rosema)</author>
      <pubDate>Sun, 07 Sep 2025 00:00:00 GMT</pubDate>
      <description><![CDATA[Mini-Tutorial about how to abort asynchronous JavaScript]]></description>
      <category><![CDATA[post]]></category>
      <category><![CDATA[javascript]]></category>
      <category><![CDATA[async]]></category>
      <content:encoded><![CDATA[<p>This is a follow-up post about something I wrote on dev.to, 5 years ago:
<a href="https://dev.to/learosema/aborting-a-fetch-request-4pmb">Aborting a fetch request</a>.</p>
<p>As I mentioned, aborting is not too intuitive. When using fetch, you can
provide a <code>signal</code> in the request options, which can then be used to abort
operations.</p>
<p>This can not only be used inside the fetch API but inside everything asynchronous.</p>
<h2>Wait, Vanilla JavaScript has signals?</h2>
<p>Yes. But not the Angular kind. It's solely for aborting
async operations and that's why it's called <a href="https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal"><code>AbortSignal</code></a>.</p>
<p>It works like this: you create a new <a href="https://developer.mozilla.org/en-US/docs/Web/API/AbortController"><code>AbortController</code></a>.</p>
<p>The controller exposes an <code>abort</code> method and a <code>signal</code> property.
The <code>signal</code> can be used to listen on <code>abort</code> events, via
<code>signal.addEventListener</code>.</p>
<h2>Code Example</h2>
<pre class="language-js"><code class="language-js"><span class="token keyword">let</span> abortController <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> counter <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span>

<span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">loop</span><span class="token punctuation">(</span><span class="token parameter">t <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">,</span> signal</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> timeout <span class="token operator">=</span> <span class="token function">setTimeout</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">t</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token function">loop</span><span class="token punctuation">(</span>t<span class="token punctuation">,</span> signal<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token number">1000</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

  $counter<span class="token punctuation">.</span>innerHTML <span class="token operator">=</span> counter<span class="token punctuation">;</span>
  counter<span class="token operator">++</span><span class="token punctuation">;</span>
  signal<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">"abort"</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
    <span class="token function">clearTimeout</span><span class="token punctuation">(</span>timeout<span class="token punctuation">)</span><span class="token punctuation">;</span>
    $counter<span class="token punctuation">.</span>innerHTML <span class="token operator">=</span> <span class="token string">"aborted :)"</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

abortButton<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">"click"</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span> abortController<span class="token punctuation">)</span> <span class="token keyword">return</span><span class="token punctuation">;</span>
  abortController<span class="token punctuation">.</span><span class="token function">abort</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  abortController <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

runButton<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">"click"</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
  
  <span class="token keyword">if</span> <span class="token punctuation">(</span>abortController<span class="token punctuation">)</span> <span class="token keyword">return</span><span class="token punctuation">;</span>
  abortController <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">AbortController</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  counter <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span>
  
  <span class="token comment">// The abort signal is a property of </span>
  <span class="token comment">// the AbortController, which we pass to the </span>
  <span class="token comment">// async loop function. </span>
  <span class="token comment">// We could even "await" it here, but as we</span>
  <span class="token comment">// don't do anything further afterwards, it can be a </span>
  <span class="token comment">// fire and forget.</span>
  <span class="token keyword">const</span> abortSignal <span class="token operator">=</span> abortController<span class="token punctuation">.</span>signal<span class="token punctuation">;</span>
  <span class="token function">loop</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> abortSignal<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<h2>Nested asynchronous functions</h2>
<p>When you have nested asynchronous functions, don't forget to pass the signal down.</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">function</span> <span class="token function">wait</span><span class="token punctuation">(</span><span class="token parameter">ms<span class="token punctuation">,</span> signal</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">Promise</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">resolve<span class="token punctuation">,</span> reject</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
    <span class="token keyword">const</span> timerId <span class="token operator">=</span> window<span class="token punctuation">.</span><span class="token function">setTimeout</span><span class="token punctuation">(</span>resolve<span class="token punctuation">,</span> ms<span class="token punctuation">)</span>
    signal<span class="token operator">?.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'abort'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
      window<span class="token punctuation">.</span><span class="token function">clearTimeout</span><span class="token punctuation">(</span>timerId<span class="token punctuation">)</span>
      <span class="token function">reject</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>

<span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">incrementTripleTimes</span><span class="token punctuation">(</span><span class="token parameter">signal</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  $counter2<span class="token punctuation">.</span>innerHTML <span class="token operator">=</span> counter2<span class="token punctuation">;</span>
  <span class="token keyword">await</span> <span class="token function">wait</span><span class="token punctuation">(</span><span class="token number">1000</span><span class="token punctuation">,</span> signal<span class="token punctuation">)</span>
  $counter2<span class="token punctuation">.</span>innerHTML <span class="token operator">=</span> <span class="token operator">++</span>counter2<span class="token punctuation">;</span>
  <span class="token keyword">await</span> <span class="token function">wait</span><span class="token punctuation">(</span><span class="token number">1000</span><span class="token punctuation">,</span> signal<span class="token punctuation">)</span>
  $counter2<span class="token punctuation">.</span>innerHTML <span class="token operator">=</span> <span class="token operator">++</span>counter2<span class="token punctuation">;</span>
  <span class="token keyword">await</span> <span class="token function">wait</span><span class="token punctuation">(</span><span class="token number">1000</span><span class="token punctuation">,</span> signal<span class="token punctuation">)</span>
  $counter2<span class="token punctuation">.</span>innerHTML <span class="token operator">=</span> <span class="token operator">++</span>counter2<span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">loop2</span><span class="token punctuation">(</span><span class="token parameter">t <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">,</span> signal</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token comment">// uncomment below and see what happens</span>
  <span class="token comment">// signal = null</span>
  signal<span class="token operator">?.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">"abort"</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
    $counter2<span class="token punctuation">.</span>innerHTML <span class="token operator">=</span> <span class="token string">"aborted :)"</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token keyword">await</span> <span class="token function">incrementTripleTimes</span><span class="token punctuation">(</span>signal<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">await</span> <span class="token function">incrementTripleTimes</span><span class="token punctuation">(</span>signal<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">await</span> <span class="token function">incrementTripleTimes</span><span class="token punctuation">(</span>signal<span class="token punctuation">)</span><span class="token punctuation">;</span>
  
  console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'restarting loop'</span><span class="token punctuation">)</span>
  window<span class="token punctuation">.</span><span class="token function">setTimeout</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">t</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token function">loop2</span><span class="token punctuation">(</span>t<span class="token punctuation">,</span> signal<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

runButton2<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">"click"</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span>abortController2<span class="token punctuation">)</span> <span class="token keyword">return</span><span class="token punctuation">;</span>
  abortController2 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">AbortController</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  counter2 <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span>
  <span class="token function">loop2</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> abortController2<span class="token punctuation">.</span>signal<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>

abortButton2<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">"click"</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span> abortController2<span class="token punctuation">)</span> <span class="token keyword">return</span><span class="token punctuation">;</span>
  abortController2<span class="token punctuation">.</span><span class="token function">abort</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  abortController2 <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre>
<p>Check the full demo on <a href="https://codepen.io/learosema/pen/ByoMVzx?editors=0011">CodePen</a>.</p>
<h2>Sources</h2>
<ul>
<li><a href="https://dom.spec.whatwg.org/#aborting-ongoing-activities">DOM spec: aborting ongoing activities</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/AbortController">MDN: AbortController</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal">MDN: AbortSignal</a></li>
</ul>

      ]]></content:encoded>
    </item><item>
      <title><![CDATA[My Retro coding journey]]></title><link>https://lea.codes/posts/2025-05-17-my-retro-coding-journey/</link>
      <guid>https://lea.codes/posts/2025-05-17-my-retro-coding-journey/</guid>
      <author>lea@lea.lgbt (Lea Rosema)</author>
      <pubDate>Sat, 17 May 2025 00:00:00 GMT</pubDate>
      <description><![CDATA[I&#39;m a newbie when it comes to retrocoding in C for DOS, and it&#39;s been quite a ride so far.]]></description>
      <category><![CDATA[post]]></category>
      <category><![CDATA[retrocoding]]></category>
      <category><![CDATA[dos]]></category>
      <category><![CDATA[fun]]></category>
      <content:encoded><![CDATA[<p>I'm a newbie when it comes to retrocoding in C for DOS, and it's been quite a ride so far.</p>
<p>It took me two weeks to debug a problem with my font-loading function, and it turned out I simply wasn't allocating enough memory. The culprit? My DPMI allocation function. I need to convert the memory size to paragraphs (1 paragraph = 16 bytes), but I accidentally divided by 16 twice. Facepalm.</p>
<p>To be fair, the two weeks is partly because I don't do DOS development full-time. That wouldn't pay my bills.</p>
<h2>Want Some Retro Trivia?</h2>
<p>Ever heard of DPMI? Wondering why I needed my own memory allocation function? Let's dig in.</p>
<h2>What is DPMI?</h2>
<p>DPMI stands for DOS Protected Mode Interface. It allows you to write 32-bit applications for DOS while still accessing BIOS functions. Pretty wild, huh? BIOS (Basic Input/Output System) is the low-level system that helps your machine boot up. On modern systems, BIOS has been mostly replaced by UEFI, but back in the day, it was the gatekeeper for hardware control.</p>
<h2>Why Do We Need DPMI for Memory Allocation?</h2>
<p>Here's the deal: all the BIOS stuff lives in the lower memory block, the first 640KB of RAM. My C program, which is a 32-bit application, is loaded into the upper memory block. But there's a catch: BIOS is still 16-bit. If I want it to read or write to memory, it can only access that lower chunk of RAM.</p>
<p>This is where DPMI steps in. Normal malloc() from C won't cut it here—I need to allocate memory in the lower block and create a descriptor. Think of it as a bridge that lets my 32-bit code talk to 16-bit BIOS routines.</p>
<pre class="language-c"><code class="language-c"><span class="token comment">// Data structure holding my DOS lower memory block</span>
<span class="token keyword">typedef</span> <span class="token keyword">struct</span> <span class="token class-name">dos_block_s</span> <span class="token punctuation">{</span>
    <span class="token class-name">uint16_t</span> segment<span class="token punctuation">;</span>   <span class="token comment">// Real mode segment for ES</span>
    <span class="token class-name">uint16_t</span> selector<span class="token punctuation">;</span>  <span class="token comment">// Protected mode selector for direct access</span>
<span class="token punctuation">}</span> <span class="token class-name">dos_block_t</span><span class="token punctuation">;</span>

<span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">if</span> <span class="token expression">defined __DOS__ <span class="token operator">&amp;&amp;</span> defined __386__</span></span>

<span class="token class-name">dos_block_t</span> <span class="token function">dpmi_alloc_dos_block</span><span class="token punctuation">(</span><span class="token class-name">uint32_t</span> size<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    <span class="token class-name">dos_block_t</span> dblk <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
    <span class="token keyword">union</span> REGS regs<span class="token punctuation">;</span>

    regs<span class="token punctuation">.</span>x<span class="token punctuation">.</span>eax <span class="token operator">=</span> <span class="token number">0x0100</span><span class="token punctuation">;</span> <span class="token comment">// DPMI function: Allocate DOS memory block</span>
    regs<span class="token punctuation">.</span>x<span class="token punctuation">.</span>ebx <span class="token operator">=</span> <span class="token punctuation">(</span>size <span class="token operator">+</span> <span class="token number">15</span><span class="token punctuation">)</span> <span class="token operator">>></span> <span class="token number">4</span><span class="token punctuation">;</span> <span class="token comment">// convert to paragraphs (1 paragraph = 16 bytes)</span>

    <span class="token function">int386</span><span class="token punctuation">(</span><span class="token number">0x31</span><span class="token punctuation">,</span> <span class="token operator">&amp;</span>regs<span class="token punctuation">,</span> <span class="token operator">&amp;</span>regs<span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">if</span> <span class="token punctuation">(</span>regs<span class="token punctuation">.</span>x<span class="token punctuation">.</span>cflag<span class="token punctuation">)</span>
        <span class="token keyword">return</span> dblk<span class="token punctuation">;</span> <span class="token comment">// Failure, return {0, 0}</span>

    dblk<span class="token punctuation">.</span>segment <span class="token operator">=</span> regs<span class="token punctuation">.</span>w<span class="token punctuation">.</span>ax<span class="token punctuation">;</span>
    dblk<span class="token punctuation">.</span>selector <span class="token operator">=</span> regs<span class="token punctuation">.</span>w<span class="token punctuation">.</span>dx<span class="token punctuation">;</span>
    <span class="token keyword">return</span> dblk<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">else</span></span>
<span class="token comment">// Stub for platforms other than dos, </span>
<span class="token comment">// basically to make VSCode happy.</span>
<span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">define</span> <span class="token macro-name function">dpmi_alloc_dos_block</span><span class="token expression"><span class="token punctuation">(</span>size<span class="token punctuation">)</span></span></span>
<span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">endif</span></span>
</code></pre>
<h2>Why Go Through All This Trouble?</h2>
<p>For fonts! I wanted to use a custom font for my text-mode application. I designed a sans-serif 8x8 raster font, which really changes the look and feel of the classic DOS screen.</p>
<p>The cool part: text-mode graphics and raster fonts are still a huge deal in the indie gamedev scene. There's something incredibly satisfying about pushing chunky pixels directly and seeing the result instantly on the screen.</p>
<p>To feature a decent example, check out <a href="https://datagoblin.itch.io/monogram">Monogram</a> and <a href="https://datagoblin.itch.io/dungeonmode">DungeonMode</a> by <a href="https://datagoblin.itch.io/">datagoblin</a>.</p>
<p>Beautiful 8x8 raster fonts that makes you want to start building a roguelike RPG from scratch.</p>
<h2>What's the big picture?</h2>
<p>The big picture is to build a basic music application in textmode that runs everywhere, using the old OPL2 chip that was used in Ad Lib and soundblaster cards.</p>
<p>Why? Because my elder brother built a music application in the 90s
in Pascal. It had MIDI support and everything!</p>
<p>He has always been a role model to me.</p>
<p>Why DOS? It's easier to start with DOS for me. It's a limiting choice. But this way, I won't feel overwhelmed by the drive to build it for Windows, Linux, Mac and the Web simultaneously. Maybe just to get a foot in the door about non-web UI development. Or, to keep it short: for fun.</p>
<p>The code for it is still in an early stage. I'm putting together puzzle pieces here:</p>
<ul>
<li><a href="https://github.com/learosema/muzimake/">https://github.com/learosema/muzimake/</a></li>
<li>The special font loading experiment I talked about: <a href="https://github.com/learosema/muzimake/blob/main/tests/test_fnt.cpp">https://github.com/learosema/muzimake/blob/main/tests/test_fnt.cpp</a></li>
</ul>
<h2>Wrapping Up</h2>
<p>This journey into DPMI and custom fonts has been a massive learning experience. It might not be the smoothest path, but debugging this low-level stuff really makes you appreciate the magic of modern OS memory handling.</p>
<p>Next on my list? Building a proper text-mode UI for my application. Let's see how deep this rabbit hole goes...</p>

      ]]></content:encoded>
    </item><item>
      <title><![CDATA[Abandoning the accessibility consultant career path]]></title><link>https://lea.codes/posts/2025-04-25-abandoning-the-accessibility-consultant-path/</link>
      <guid>https://lea.codes/posts/2025-04-25-abandoning-the-accessibility-consultant-path/</guid>
      <author>lea@lea.lgbt (Lea Rosema)</author>
      <pubDate>Fri, 25 Apr 2025 00:00:00 GMT</pubDate>
      <description><![CDATA[I still see accessibility as a crucial part for building digital products, but it&#39;s not my career path anymore to become an accessibility specialist.]]></description>
      <category><![CDATA[post]]></category>
      <category><![CDATA[personal career]]></category>
      <category><![CDATA[a11y]]></category>
      <content:encoded><![CDATA[<h2>Why</h2>
<p>I still see accessibility as a crucial part for building digital products, but it's not my career path anymore to become an accessibility specialist.</p>
<p>I'm still very enthusiastic about accessibility, but I'm not happy about how things are in the industry at the moment.</p>
<h2>What frustrates me about Accessibility?</h2>
<p>IAAP certifications are broken and need to be fixed. One thing that is problematic is the certificate expiry. I get the point, people working in accessibility should know their stuff.</p>
<p>Still, the required certificate renewal lets it look like a money pit. Also, I dislike the gatekeeping. There shouldn't be any requirements to get the certificates IMHO.</p>
<p>Furthermore, these certificates shouldn't be necessary to work in accessibility, and some companies misconcept it like that; which can make it hard to get a foot in the door. You have a perfect vicious circle when certificates require work experience and the clients require certified people.</p>
<p>Another thing I was thinking about is that Accessibility audits are often pointless. Even easily fixable bugs are not addressed and rot in the backlog. Instead, a relauch in the future is promised, with a more accessible version of the product. But without further information on when this will be.</p>
<p>Additionally, you probably won't get to building software anymore as an accessibility specialist. Your main task is going to be to create accessibility audits. This feels like a step down, like being degraded from a Software developer down to a WCAG compliance tester.</p>
<p>In client work and enterprise contexts, accessibility is often about WCAG compliance, but not about really making the product as accessible as possible.</p>
<p>A further point I really dislike the Fear marketing regarding the European Accessibility Act. This is a strategy some accessibility consultants run and it quite tells that it's about compliance rather than real accessibility improvement efforts.</p>
<h2>Quo vadis Lea?</h2>
<p>As I said in the opener, I'm still very enthusiastic about accessibility, but I'm not happy about how things currently are in the industry.</p>
<p>My focus is still on building user interfaces with accessibility in mind. But I want to build the things. I'm a lousy tester, I guess.</p>
<p>Also, I would like to get a more T-shaped skillset. I love to explore the Software Architecture field, while still keeping my strong focus on UI development, usability and accessibility.</p>
<p>Following that path, I hope I can have more impact and bring in my accessibility knowledge, from the position of a developer or maybe of a Software architect in the future.</p>

      ]]></content:encoded>
    </item><item>
      <title><![CDATA[Meetups are for everyone]]></title><link>https://lea.codes/posts/2025-01-15-meetups-are-for-everyone/</link>
      <guid>https://lea.codes/posts/2025-01-15-meetups-are-for-everyone/</guid>
      <author>lea@lea.lgbt (Lea Rosema)</author>
      <pubDate>Wed, 15 Jan 2025 00:00:00 GMT</pubDate>
      <description><![CDATA[This is an article about underrepresented groups in tech and gatekeeping in communities.]]></description>
      <category><![CDATA[post]]></category>
      <category><![CDATA[communities]]></category>
      <category><![CDATA[diversity]]></category>
      <content:encoded><![CDATA[<p>There is one thing I really like to do: going to and organizing meetups.</p>
<p>But it turns me off when such a space is exclusively open to &quot;experts only&quot;.
There are events that accept speaker proposals only when they are &quot;experts&quot;.</p>
<p>Some events even limit the attendees to &quot;expert-only&quot; as well.</p>
<p>In either way, only one or a few people are in power to decide who is &quot;expert enough&quot; in order to be part of it. And I have very mixed feelings about it.</p>
<h2>&quot;Maintain Quality&quot;</h2>
<p>One reasoning behind creating &quot;experts-only&quot; spaces is to maintain quality. This way,
organizers can (or think they can) ensure a topic is covered in a high quality.
Also, (they think) discussions can go in depth, and beginner questions in complex topics aren't covered over and over.</p>
<p>It's perfectly fine and valid to communicate a target audience upfront for certain advanced-level sessions. But I wouldn't systematically exclude people from a meetup based on the perceived level of expertise.</p>
<h2>The problem</h2>
<p>Perceived level of expertise. This perfectly describes the core problem: deciding who qualifies as an &quot;expert&quot; is rarely an objective process.</p>
<p>Often, it depends on criteria that favor individuals who have had greater access to education, mentorship, and professional opportunities.</p>
<p>For example, women are often seen as less competent than their male counterparts, even when they have the same qualifications. In &quot;expert-only&quot; spaces, this bias might result in women being overlooked or undervalued.</p>
<h2>The vicious cycle of &quot;Experts only&quot;</h2>
<p>For marginalized groups, gaining expertise often involves breaking through barriers like limited networking opportunities, implicit bias, or outright discrimination.</p>
<p>If advanced meetups exclude these individuals based on their perceived lack of experience, it creates a vicious cycle:</p>
<ul>
<li>They can’t enter &quot;expert-only&quot; spaces</li>
<li>Without access to these spaces, they can't advance</li>
<li>They miss network opportunities</li>
<li>They’re perpetually excluded from opportunities that could help them advance.</li>
</ul>
<p>This structural problem disproportionately affects those who already face hurdles in accessing resources and recognition.</p>
<h2>Reinforcing Toxic Cultures</h2>
<p>The exclusion often overlaps with other problematic practices, such as creating unwelcoming environments for women, LGBTQ+ folks, or people of color.</p>
<p>&quot;Bro-culture&quot; events (like beer-driven meetups) can amplify this issue.</p>
<h2>What are meetups for?</h2>
<p>Meetups are for networking and gaining new perspectives on certain topics.</p>
<p>The time for a meetup is already very limited (I learned that from facilitating <a href="https://hhtml.de/">HHTML</a>, where having 4 talks on a single evening turns out as a bit too much).</p>
<p>Mostly, when you want to cover a topic more in depth, the best way to do so is to link to more detailed resources. For example, refer to an in-depth article or book on the slides.</p>
<p>If you run an &quot;experts-only&quot; space, you – and everyone else involved – are going to miss out the opportunity to advance.</p>
<p>Creating a space for all levels allows cross-level communication, where everyone, beginners as well as more advanced foks, can gain new perspectives.</p>
<p>The tech industry already struggles with inclusivity. Let's not reinforce this  structural discrimination further.</p>

      ]]></content:encoded>
    </item><item>
      <title><![CDATA[Type-Annotations in JavaScript]]></title><link>https://lea.codes/posts/2025-01-08-type-annotations-in-javascript/</link>
      <guid>https://lea.codes/posts/2025-01-08-type-annotations-in-javascript/</guid>
      <author>lea@lea.lgbt (Lea Rosema)</author>
      <pubDate>Wed, 08 Jan 2025 00:00:00 GMT</pubDate>
      <description><![CDATA[Sprinkle your JavaScript codebase with type annotations in JSDoc, run checks against it and auto-generate documentation.]]></description>
      <category><![CDATA[post]]></category>
      <category><![CDATA[javascript]]></category>
      <category><![CDATA[typescript]]></category>
      <category><![CDATA[jsdoc]]></category>
      <category><![CDATA[automation]]></category>
      <content:encoded><![CDATA[<p>The next hot shit in node.js is being able to run typescript directly in node.js by a feature called &quot;type stripping&quot;.
When running a <code>.ts</code> file in combination with the flag <code>--experimental-strip-types</code>, all syntax that is relevant to
typescript is stripped when the file is parsed. This is still a bit experimental as of current.</p>
<p>In a hobby project, I tried a slightly less hot thing. Less hot, but pretty stable to use and available in TypeScript from the beginnings:
have it the other way round.</p>
<p>Write JavaScript code without all the type juggling, sprinkle it with a couple JSDoc comments.
JSDoc can be used to describe your declarations. This will help autocomplete feature (aka IntelliSense for Microsoft-style IDEs).</p>
<p>You can annotate JSDoc with TypeScript types. These are also used to display type information in the autocompletion feature.
You can additionally check the JavaScript code base by the typescript compiler.</p>
<p>How does JSDoc-annotated code look like?</p>
<pre class="language-jsdoc"><code class="language-jsdoc">/**
 * Poor girl's handlebars
 *
 * <span class="token keyword">@param</span> <span class="token class-name"><span class="token punctuation">{</span>string<span class="token punctuation">}</span></span> <span class="token parameter">content</span> the template content
 * <span class="token keyword">@param</span> <span class="token class-name"><span class="token punctuation">{</span>Record<span class="token punctuation">&lt;</span>string<span class="token punctuation">,</span> string<span class="token punctuation">></span><span class="token punctuation">}</span></span> <span class="token optional-parameter"><span class="token punctuation">[</span><span class="token parameter">data</span><span class="token punctuation">]</span></span> the data object
 * <span class="token keyword">@param</span> <span class="token class-name"><span class="token punctuation">{</span>TemplateConfig<span class="token punctuation">}</span></span> <span class="token optional-parameter"><span class="token punctuation">[</span><span class="token parameter">config</span><span class="token punctuation">]</span></span> the template configuration, 
 * where you can specify additional filters available inside the template
 * <span class="token keyword">@returns</span> <span class="token class-name"><span class="token punctuation">{</span>Promise<span class="token punctuation">&lt;</span>string<span class="token punctuation">></span><span class="token punctuation">}</span></span> the template result string
 */
export async function template(content, data, config) <span class="token punctuation">{</span><span class="token punctuation">}</span></code></pre>
<p>You can place a little comment block above your function (or whatever else you want to document). In curly braces, you can additionally provide the types used in
parameters or returns. Whenever you have an asynchronous function, remember to return a <code>Promise&lt;returnType&gt;</code> of your return type. Parameters can be
specified as optional via square brackets. Your code editor may auto-insert such a block whenever you write <code>/**</code> and hit enter above a declaration.</p>
<p>Every built-in type you know from TypeScript is available. This includes primitives such as <code>string</code>, <code>number</code> <code>boolean</code> but also more complex object types.
Object types can be defined using the <code>@typedef</code> directive. The definition of the <code>TemplateConfig</code> object looks like this:</p>
<pre class="language-jsdoc"><code class="language-jsdoc">/**
 * <span class="token keyword">@typedef</span> <span class="token class-name">TemplateConfig</span>
 * Configuration object for the template() function.
 * <span class="token keyword">@property</span> <span class="token class-name"><span class="token punctuation">{</span>Map<span class="token punctuation">&lt;</span>string<span class="token punctuation">,</span> Function<span class="token punctuation">></span><span class="token punctuation">}</span></span> <span class="token optional-parameter"><span class="token punctuation">[</span><span class="token parameter">filters</span><span class="token punctuation">]</span></span> map of additional filters to be used
 */</code></pre>
<h2>Interfaces?</h2>
<p>A thing loved by software architects and design pattern evangelists in programming languages are <code>interface</code>s, wich are often used in
Software Design concepts such as <a href="https://builtin.com/articles/dependency-injection#:~:text=Dependency%20injection%20is%20about%20injecting,and%20embracing%20loosely%20coupled%20code.">Dependency Injection</a>.</p>
<p>But JavaScript lacks interfaces. And JavaScript is often criticized due to that.</p>
<p>That can also be represented as a <code>@typedef</code> in JSDoc.</p>
<p>As an example, look at the following TypeScript:</p>
<pre class="language-ts"><code class="language-ts"><span class="token keyword">interface</span> <span class="token class-name">FileResolver</span> <span class="token punctuation">{</span>
    <span class="token keyword">async</span> <span class="token function">loadFile</span><span class="token punctuation">(</span>fileName<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">Promise</span><span class="token operator">&lt;</span><span class="token builtin">string</span><span class="token operator">|</span>Buffer<span class="token operator">></span>
<span class="token punctuation">}</span></code></pre>
<p>It can be represented via JSDoc:</p>
<pre class="language-jsdoc"><code class="language-jsdoc">/**
 * <span class="token keyword">@typeDef</span> FileResolver
 * <span class="token keyword">@property</span> <span class="token class-name"><span class="token punctuation">{</span><span class="token punctuation">(</span>fileName<span class="token operator">:</span> string<span class="token punctuation">)</span> <span class="token operator">=></span> Promise<span class="token punctuation">&lt;</span>string<span class="token operator">|</span>Buffer<span class="token punctuation">></span><span class="token punctuation">}</span></span> <span class="token parameter">loadFile</span>
 */</code></pre>
<h2>Using types from other files</h2>
<p>Using types from other files is a bit tedious though.
How would you reference a type from another file?</p>
<p>Microsoft IntelliSense in Visual Studio and Visual Studio Code have a special syntax for it.
So called triple-slash-references:</p>
<pre class="language-js"><code class="language-js"><span class="token comment">///&lt;reference path="typedefs.js"/></span></code></pre>
<p>But jsdoc requires another syntax, which looks like this</p>
<pre class="language-js"><code class="language-js"><span class="token doc-comment comment">/**
 * My setup function 
 * <span class="token keyword">@param</span> <span class="token class-name"><span class="token punctuation">{</span><span class="token keyword">import</span><span class="token punctuation">(</span><span class="token string">"./typedefs.js"</span><span class="token punctuation">)</span><span class="token punctuation">.</span>Configuration<span class="token punctuation">}</span></span> <span class="token optional-parameter"><span class="token punctuation">[</span><span class="token parameter">config</span><span class="token punctuation">]</span></span> 
 */</span>
<span class="token keyword">function</span> <span class="token function">setup</span><span class="token punctuation">(</span><span class="token parameter">config</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token punctuation">}</span></code></pre>
<p>This works for me, but it's a bit verbose. You can use a combination of both: The triple-slash reference and the inline type imports.
Unfortunately, I haven't found a simpler way yet.</p>
<h2>Setting up TypeScript</h2>
<p>Let's have a look how you can use TypeScript in your project without writing TypeScript but JavaScript with JSDoc.</p>
<p>First, install typescript as a devDependency.</p>
<pre><code>npm install typescript -D
</code></pre>
<p>In the tsconfig, you can specify <code>allowJs: true</code> and <code>checkJs: true</code>. This allows you to use JavaScript in TypeScript and let it check by the typescript Compiler.</p>
<p>Regarding my rules, I've set strict to true, but noImplicitAny set to false. With some effort, I could get it to work with noImplicitAny set to true, but this would have required me to add more typings with inline comments. In general, that would also be a good starter ruleset to migrate a JavaScript codebase to TypeScript.</p>
<p>Having tsc to check the JavaScript still helped me find certain problems where stuff could possibly be undefined.</p>
<p>For my js-only codebase, I went with this <code>tsconfig.json</code>, extending from a node22-preset. At the time of this writing, node 22 was the LTS version.</p>
<pre class="language-sh"><code class="language-sh"><span class="token function">npm</span> <span class="token function">install</span> @tsconfig/node22 <span class="token parameter variable">-D</span></code></pre>
<pre class="language-js"><code class="language-js"><span class="token punctuation">{</span>
  <span class="token string-property property">"extends"</span><span class="token operator">:</span> <span class="token string">"@tsconfig/node22/tsconfig.json"</span><span class="token punctuation">,</span>
  <span class="token string-property property">"compilerOptions"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
    <span class="token string-property property">"rootDir"</span><span class="token operator">:</span> <span class="token string">"./src"</span><span class="token punctuation">,</span>
    <span class="token string-property property">"allowJs"</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
    <span class="token string-property property">"checkJs"</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
    <span class="token string-property property">"declaration"</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
    <span class="token string-property property">"declarationMap"</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
    <span class="token string-property property">"emitDeclarationOnly"</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
    <span class="token string-property property">"outDir"</span><span class="token operator">:</span> <span class="token string">"./dist"</span><span class="token punctuation">,</span>
    <span class="token string-property property">"forceConsistentCasingInFileNames"</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
    <span class="token string-property property">"strict"</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
    <span class="token string-property property">"noImplicitAny"</span><span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span>
    <span class="token string-property property">"skipLibCheck"</span><span class="token operator">:</span> <span class="token boolean">true</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token string-property property">"include"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">"src"</span><span class="token punctuation">]</span>
<span class="token punctuation">}</span></code></pre>
<p>When I run <code>npx tsc</code> now, it would generate type declarations for all files in <code>src</code> and save them to <code>dist</code>.</p>
<h2>Auto-generate documentation</h2>
<p>Additionally, I even could run <code>typedoc</code> which generates a nice website with the API for me.
I found this super helpful, so I built a Github action that builds and deploys it:</p>
<pre class="language-sh"><code class="language-sh"><span class="token function">npm</span> <span class="token function">install</span> typedoc <span class="token parameter variable">-D</span></code></pre>
<pre class="language-yaml"><code class="language-yaml"><span class="token comment"># .github/workflows/documentation.yaml:</span>
<span class="token key atrule">name</span><span class="token punctuation">:</span> Deploy API Documentation to GitHub Pages

<span class="token key atrule">on</span><span class="token punctuation">:</span>
  <span class="token comment"># Runs on pushes targeting the default branch</span>
  <span class="token key atrule">push</span><span class="token punctuation">:</span>
    <span class="token key atrule">branches</span><span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token string">"main"</span><span class="token punctuation">]</span>

  <span class="token comment"># Allows you to run this workflow manually from the Actions tab</span>
  <span class="token key atrule">workflow_dispatch</span><span class="token punctuation">:</span>

<span class="token comment"># Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages</span>
<span class="token key atrule">permissions</span><span class="token punctuation">:</span>
  <span class="token key atrule">contents</span><span class="token punctuation">:</span> read
  <span class="token key atrule">pages</span><span class="token punctuation">:</span> write
  <span class="token key atrule">id-token</span><span class="token punctuation">:</span> write

<span class="token comment"># Allow only one concurrent deployment,</span>
<span class="token comment"># skipping runs queued between the run in-progress and latest queued.</span>
<span class="token comment"># However, do not cancel in-progress runs.</span>
<span class="token key atrule">concurrency</span><span class="token punctuation">:</span>
  <span class="token key atrule">group</span><span class="token punctuation">:</span> <span class="token string">"pages"</span>
  <span class="token key atrule">cancel-in-progress</span><span class="token punctuation">:</span> <span class="token boolean important">false</span>

<span class="token key atrule">jobs</span><span class="token punctuation">:</span>
  <span class="token key atrule">deploy</span><span class="token punctuation">:</span>
    <span class="token key atrule">environment</span><span class="token punctuation">:</span>
      <span class="token key atrule">name</span><span class="token punctuation">:</span> github<span class="token punctuation">-</span>pages
      <span class="token key atrule">url</span><span class="token punctuation">:</span> $
    <span class="token key atrule">runs-on</span><span class="token punctuation">:</span> ubuntu<span class="token punctuation">-</span>latest
    <span class="token key atrule">steps</span><span class="token punctuation">:</span>
      <span class="token punctuation">-</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> Checkout
        <span class="token key atrule">uses</span><span class="token punctuation">:</span> actions/checkout@v4
      <span class="token punctuation">-</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> Setup Node
        <span class="token key atrule">uses</span><span class="token punctuation">:</span> actions/setup<span class="token punctuation">-</span>node@v4
        <span class="token key atrule">with</span><span class="token punctuation">:</span>
          <span class="token key atrule">node-version</span><span class="token punctuation">:</span> <span class="token number">22</span>
      <span class="token punctuation">-</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> Build API documentation
        <span class="token key atrule">run</span><span class="token punctuation">:</span> <span class="token punctuation">|</span><span class="token scalar string">
          npm ci
          npm run docs
          touch docs/.nojekyll</span>
      <span class="token punctuation">-</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> Setup Pages
        <span class="token key atrule">uses</span><span class="token punctuation">:</span> actions/configure<span class="token punctuation">-</span>pages@v5
      <span class="token punctuation">-</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> Upload artifact
        <span class="token key atrule">uses</span><span class="token punctuation">:</span> actions/upload<span class="token punctuation">-</span>pages<span class="token punctuation">-</span>artifact@v3
        <span class="token key atrule">with</span><span class="token punctuation">:</span>
          <span class="token key atrule">path</span><span class="token punctuation">:</span> <span class="token string">'docs'</span>
      <span class="token punctuation">-</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> Deploy to GitHub Pages
        <span class="token key atrule">id</span><span class="token punctuation">:</span> deployment
        <span class="token key atrule">uses</span><span class="token punctuation">:</span> actions/deploy<span class="token punctuation">-</span>pages@v4</code></pre>
<h2>Further Resources</h2>
<ul>
<li><a href="https://jsdoc.app/">official JSDoc website</a></li>
<li><a href="https://typedoc.org/">typedoc</a></li>
<li><a href="https://www.linkedin.com/posts/stefan-judis_todays-a-big-day-in-nodejs-land-v2360-activity-7282673654943911936-H3KU">LinkedIn article about <code>--experimental-strip-types</code></a>, by <a href="https://stefanjudis.com/">Stefan Judis</a>, which inspired me writing this article</li>
</ul>

      ]]></content:encoded>
    </item><item>
      <title><![CDATA[Punkt BAT]]></title><link>https://lea.codes/posts/2024-12-26-punkt-bat/</link>
      <guid>https://lea.codes/posts/2024-12-26-punkt-bat/</guid>
      <author>lea@lea.lgbt (Lea Rosema)</author>
      <pubDate>Thu, 26 Dec 2024 00:00:00 GMT</pubDate>
      <description><![CDATA[Dealing with Batch-Files in non-Windows environments]]></description>
      <category><![CDATA[post]]></category>
      <category><![CDATA[npm]]></category>
      <category><![CDATA[shell]]></category>
      <category><![CDATA[devexperience]]></category>
      <content:encoded><![CDATA[<p>I work in several client projects as a web developer. Usually, projects are mostly cross-platform. Most web development tools are available on any platform.
However, sometimes, projects require to run Windows. I had this situation recently.</p>
<p>So, I was wondering, why do I have to run Windows for development tasks in the project?
I might get the point. Maybe the team doesn't want to care too much about cross-platform compatibility.
But most of the tools were cross-platform anyway. So I was asking myself again, why?</p>
<p>The machine I work with is a Mac with a Silicon Chip. Usually, this will do fine for most for web development tasks.</p>
<p>I learned more about why the project requires Windows for development.
Most of the tools used (eg. Visual Studio Code, Azure Data Studio, Docker, Node, etc) were in fact perfectly available for Mac.
But the database server running in a Docker image doesn't support the Apple Silicon chip.</p>
<p>So, an option proposed to me was to hand me a second machine that runs Windows. Another option would be to use a VM. I wanted to avoid both options if possible. Working inside a VM is pretty cumbersome, and having to carry two laptops around every day in the Office would be quite exhausting. it would require me to always switch between the two computers all the time (operative tasks on Windows, administrative tasks on the Mac).</p>
<p>The database server inside Docker was the first hurdle. But all I had to do was to install the x86 CPU emulation layer on my mac, via:</p>
<pre class="language-sh"><code class="language-sh">softwareupdate --install-rosetta</code></pre>
<p>Far from optimal, but this made it run on my Silicon Mac. I'm not sure (and not a backend dev), but the backend was using an Object-relational Mapper (ORM). It might have been possible to configure the project with a something more cross-platform-compatible database engine like PostgreSQL. But we went with the emulation and it worked just fine.</p>
<p>The second thing was a question of comfort. The whole project had a lot of good old DOS-style Batch files for running repetitive tasks during development.
For example, there was a <code>RUN.BAT</code> for running frontends, backends, etc.</p>
<p>Simplified and anonymized, it looked something like this:</p>
<pre class="language-bat"><code class="language-bat">@echo off

if "%1" === "frontend" goto frontend
if "%1" === "backend" goto backend
if "%1" === "migrate" goto migrations

echo Usage: RUN [frontend|backend|migrations]
goto :end

:frontend
cd frontend
npm start
goto :end

:backend
dotnet run backend\backend.vspjoj 
goto :end

:migrations
CALL db migrate %2
goto :end

:end</code></pre>
<p>Most of the commands inside that batch file also run just fine on the Mac. So the quick solution was to always do a <code>cat RUN.BAT</code> and copy the code from the appropriate jump marks.</p>
<p>I did this to get the thing running on day 1. On day 2, I got tired of this.</p>
<p>First thought was to introduce a top-level <code>package.json</code> and use the scripts section for running tasks:</p>
<pre class="language-sh"><code class="language-sh"><span class="token function">npm</span> init <span class="token parameter variable">-y</span></code></pre>
<p>As a result, you get a json file. Then, you can edit the scripts section accordingly, so you can invoke the tasks via <code>npm run frontend</code> and so on:</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span>
  <span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"foldername"</span><span class="token punctuation">,</span>
  <span class="token property">"version"</span><span class="token operator">:</span> <span class="token string">"1.0.0"</span><span class="token punctuation">,</span>
  <span class="token property">"main"</span><span class="token operator">:</span> <span class="token string">"index.js"</span><span class="token punctuation">,</span>
  <span class="token property">"scripts"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
    <span class="token property">"frontend"</span><span class="token operator">:</span> <span class="token string">"cd frontend &amp;&amp; npm start"</span><span class="token punctuation">,</span>
    <span class="token property">"backend"</span><span class="token operator">:</span> <span class="token string">"cd backend &amp;&amp; command_to_run_backend"</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token property">"keywords"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
  <span class="token property">"author"</span><span class="token operator">:</span> <span class="token string">""</span><span class="token punctuation">,</span>
  <span class="token property">"license"</span><span class="token operator">:</span> <span class="token string">"ISC"</span><span class="token punctuation">,</span>
  <span class="token property">"description"</span><span class="token operator">:</span> <span class="token string">""</span>
<span class="token punctuation">}</span></code></pre>
<p>That was declined. Second thought was to install <a href="https://www.winehq.org/">WINE</a> which is a Windows environment wrapper for UNIX-style OSses, including Mac. Actually, it does come with <code>winecmd</code> which enables me to run batch files on my Mac. But I didn't manage to make it work with tools like <code>node</code> installed on my machine.</p>
<p>My solution in the end was to port the Batch files to UNIX-style shell scripts. I'm not a Shell Guru. But that turned out to be straightforward:</p>
<pre class="language-sh"><code class="language-sh"><span class="token shebang important">#!/bin/sh</span>

<span class="token function-name function">frontend</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token builtin class-name">echo</span> running frontend
    <span class="token comment"># frontend tasks here</span>
<span class="token punctuation">}</span>

<span class="token function-name function">backend</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token builtin class-name">echo</span> running backend
    <span class="token comment"># backend tasks here</span>
<span class="token punctuation">}</span>

<span class="token keyword">if</span> <span class="token punctuation">[</span> <span class="token string">"<span class="token variable">$1</span>"</span> <span class="token operator">==</span> <span class="token string">"frontend"</span> <span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">then</span> frontend<span class="token punctuation">;</span> <span class="token keyword">fi</span>
<span class="token keyword">if</span> <span class="token punctuation">[</span> <span class="token string">"<span class="token variable">$1</span>"</span> <span class="token operator">==</span> <span class="token string">"backend"</span> <span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">then</span> backend<span class="token punctuation">;</span> <span class="token keyword">fi</span></code></pre>
<p>Whatever you use, I would recommend avoiding Batch files in web development codebases. The same for proprietary database engines with limited CPU support. This would make the project unnecessarily dependant on propietary platforms like Windows.</p>
<p>Yeah, to be fair, unix-style shell scripts aren't available in Windows by default, but you can install MSYS (aka Minimalist GNU for Windows) when you are on Windows. Often, you don't even have to install it separately, as <code>git</code>, the version system which is most commonly used nowadays comes with a rudimentary bash shell which you can use.</p>
<p>I wanted to share this. Maybe it helps you in case you encounter a project like this.</p>

      ]]></content:encoded>
    </item><item>
      <title><![CDATA[JSX under the hood]]></title><link>https://lea.codes/posts/2024-02-05-jsx-under-the-hood/</link>
      <guid>https://lea.codes/posts/2024-02-05-jsx-under-the-hood/</guid>
      <author>lea@lea.lgbt (Lea Rosema)</author>
      <pubDate>Mon, 05 Feb 2024 00:00:00 GMT</pubDate>
      <description><![CDATA[JSX is quite popular. It gained popularity even beyond React, despite not being part of the JavaScript specification. So that made me write an article about how JSX works.]]></description>
      <category><![CDATA[post]]></category>
      <category><![CDATA[javascript]]></category>
      <category><![CDATA[jsx]]></category>
      <content:encoded><![CDATA[<p>In my last article, I wrote about how it is possible to use JSX beyond React just with JavaScript and the DOM API. It is quite popular, as other frameworks adopted it. SolidJS, Astro and Preact, to name a few.</p>
<p>But I didn't really cover how it works under the hood, which also isn't covered in the official React documentation. So this article tries to catch that up.</p>
<h2>What's JSX at all?</h2>
<p>It's an extension of the JavaScript specification (ECMA-262). Efforts to get this integrated into the official specification weren't successful so far, apparently.</p>
<h2>Transpiling JSX to JavaScript</h2>
<p>As JSX is not part of JavaScript, it requires an additional transpile step in order to support it. This is done via a so-called transpiler. Here, you have a couple of options:</p>
<ul>
<li><a href="https://www.typescriptlang.org/docs/handbook/jsx.html">TypeScript</a></li>
<li><a href="https://esbuild.github.io/content-types/#jsx">ebuild</a></li>
<li><a href="https://vitejs.dev/guide/features#jsx">vite</a> (out of the box)</li>
</ul>
<p>JSX is an extension which adds support for writing XML-like snippets directly into JavaScript. When it was introduced by Facebook and React, there were mixed feelings coming from the JavaScript community.</p>
<p>On the one side, mixing markup into your JavaScript inline was considered a very bad practice, violating separation of concerns. But on the other side, it apparently seemed to make sense in component-style JavaScript frontend frameworks. I remember an old article dated year 2015 on that by Eric Elliot back then, which quite shows the mixed feelings of it when it was introduced: <a href="https://medium.com/javascript-scene/jsx-looks-like-an-abomination-1c1ec351a918">JSX looks like an abomination</a></p>
<p>JSX gained a lot of popularity and so other frameworks adopted it.</p>
<h2>JSX changed over time</h2>
<p>One thing that left me confused when I had a first look at React 17.</p>
<p>The team decided to introduce a breaking change to the JSX factory so now there is a React-17 way and a React-pre-17 way to transpile JSX. TypeScript provides different transpile options, <code>react</code> for the pre-17 transpile and <code>react-jsx</code> for the transpile for versions 17 and up. That also is confusing and I always have to look up which is which.</p>
<h2>Pre-17-JSX</h2>
<p>In React until version 16, JSX code used a createElement factory.
Imagine the following code:</p>
<pre class="language-jsx"><code class="language-jsx"><span class="token keyword">function</span> <span class="token function">GreeterComponent</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span>name<span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">return</span> <span class="token punctuation">(</span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span><span class="token punctuation">></span></span><span class="token plain-text">
      </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span><span class="token punctuation">></span></span><span class="token plain-text">Hello </span><span class="token punctuation">{</span>name<span class="token punctuation">}</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">></span></span><span class="token plain-text">
      </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span><span class="token plain-text">Have a nice day!</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><span class="token plain-text">
    </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span>
  <span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>In React until version 16, it gets transpiled to</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">function</span> <span class="token function">GreeterComponent</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span>name<span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">return</span> React<span class="token punctuation">.</span><span class="token function">createElement</span><span class="token punctuation">(</span><span class="token string">'div'</span><span class="token punctuation">,</span> <span class="token keyword">null</span><span class="token punctuation">,</span> 
    React<span class="token punctuation">.</span><span class="token function">createElement</span><span class="token punctuation">(</span><span class="token string">'h1'</span><span class="token punctuation">,</span> <span class="token keyword">null</span><span class="token punctuation">,</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">Hello </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>name<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    React<span class="token punctuation">.</span><span class="token function">createElement</span><span class="token punctuation">(</span><span class="token string">'p'</span><span class="token punctuation">,</span> <span class="token keyword">null</span><span class="token punctuation">,</span> <span class="token string">'Have a nice day!'</span><span class="token punctuation">)</span>
  <span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>In order to work with frameworks other than React, you can configure to use a JSX factory other than <code>React.createElement</code>. Commonly, <code>h</code> is used as a name:</p>
<pre class="language-js"><code class="language-js"><span class="token comment">/* Transpilers like Babel support setting the JSX factory via an inline comment: */</span>
<span class="token comment">/* @jsx h */</span>

<span class="token keyword">function</span> <span class="token function">GreeterComponent</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span>name<span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">return</span> <span class="token function">h</span><span class="token punctuation">(</span><span class="token string">'div'</span><span class="token punctuation">,</span> <span class="token keyword">null</span><span class="token punctuation">,</span> 
    <span class="token function">h</span><span class="token punctuation">(</span><span class="token string">'h1'</span><span class="token punctuation">,</span> <span class="token keyword">null</span><span class="token punctuation">,</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">Hello </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>name<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    <span class="token function">h</span><span class="token punctuation">(</span><span class="token string">'p'</span><span class="token punctuation">,</span> <span class="token keyword">null</span><span class="token punctuation">,</span> <span class="token string">'Have a nice day!'</span><span class="token punctuation">)</span>
  <span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<h2>React 17 and up</h2>
<p>As mentioned, React 17 had a breaking change regarding JSX.</p>
<p>The same <code>GreeterComponent</code> now relies on an external <code>_jsx</code> helper. In theory, this can help decoupling JSX components from React so you don't need to import React anymore as a peer dependency anymore when building a component library (I'd appreciate that if that's the overall Roadmap but I'm not a React core team member).</p>
<p>The same GreeterComponent transpiled looks like this:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">function</span> <span class="token function">GreeterComponent</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span>name<span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">return</span> <span class="token function">_jsx</span><span class="token punctuation">(</span><span class="token string">'div'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span><span class="token literal-property property">children</span><span class="token operator">:</span> <span class="token punctuation">[</span>
    <span class="token function">_jsx</span><span class="token punctuation">(</span><span class="token string">'h1'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span><span class="token literal-property property">children</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">Hello </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>name<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">]</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    <span class="token function">_jsx</span><span class="token punctuation">(</span><span class="token string">'p'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span><span class="token literal-property property">children</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">'Have a nice day!'</span><span class="token punctuation">]</span><span class="token punctuation">}</span><span class="token punctuation">)</span>
  <span class="token punctuation">]</span><span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span></code></pre>
<p>One advantage of the breaking change is that it is more decoupled from React, as it doesn't rely on <code>React.createElement</code> by default anymore. Maybe it is as a try by the team to get this adopted into the ECMA-262 specification. One disadvantage is the breaking change, I'm not sure whether that would have been necessary or what the advantages are.</p>
<p>Maybe it's to make the syntax with the children property more similar to React's API.</p>
<p>The disadvantages of the breaking changes dominate my opinion about it. Codepen for example still has the React 16 transpilers in place. Also, there are libraries which didn't consider the breaking change and now you need glue code to make it work. So my feelings about it are quite mixed.</p>
<h3>Creating a data structure from JSX</h3>
<p>In my last article, I used a very plain JSX factory which converts the JSX code into a basic JavaScript tree structure:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">function</span> <span class="token function">h</span><span class="token punctuation">(</span><span class="token parameter">tagName<span class="token punctuation">,</span> attributes<span class="token punctuation">,</span> <span class="token operator">...</span>children</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">return</span> <span class="token punctuation">{</span> tagName<span class="token punctuation">,</span> attributes<span class="token punctuation">,</span> children <span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Alternatively, you could output JSDON. This could directly work with <a href="https://github.com/WebReflection/linkedom">linkedom</a>, a DOM API which could be used server-side. It is very performance-optimized and uses a linked-list data structure rather than a tree.</p>
<p>I like the idea of just having some JS objects describing components. However, one thought that did come to my mind was: it's over-engineered. I'm using a transpiler to convert an XML-like syntax into JavaScript just to output HTML. I could just use <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template?retiredLocale=de"><code>&lt;template&gt;</code></a> instead.</p>
<h2>Further Resources</h2>
<p>Paul Everitt has built an <a href="https://github.com/pauleveritt/eleventy-tsx">Eleventy plugin for JSX</a> which looks pretty cool. I need to look into it.</p>
<p>Also, the official <a href="https://react.dev/learn/writing-markup-with-jsx">React documentation</a> is worth a look.</p>

      ]]></content:encoded>
    </item><item>
      <title><![CDATA[Custom JSX in TypeScript]]></title><link>https://lea.codes/posts/2024-01-17-custom-jsx-in-typescript/</link>
      <guid>https://lea.codes/posts/2024-01-17-custom-jsx-in-typescript/</guid>
      <author>lea@lea.lgbt (Lea Rosema)</author>
      <pubDate>Wed, 17 Jan 2024 00:00:00 GMT</pubDate>
      <description><![CDATA[JSX can be used without React, here&#39;s how it can be used with the DOM API and TypeScript&#39;s built-in JSX transform feature]]></description>
      <category><![CDATA[post]]></category>
      <category><![CDATA[typescript]]></category>
      <category><![CDATA[jsx]]></category>
      <content:encoded><![CDATA[<p>I'm in a love-hate relationship with React. In my day-to-day work, I work a lot with React. In my personal projects, I prefer working with more lightweight stacks, keeping the client-side JavaScript load in the browser as lean and tiny as possible.</p>
<p>Still, there are certain use-cases where I need to generate markup on the client-side from time to time, usually everytime where I need JavaScript anyway for a certain functionality.</p>
<h2>Code Example</h2>
<p>An example where I recently used JSX was for modal dialogs in my <a href="https://boulders.netlify.app/">Boulder Dash clone</a>.</p>
<p>In that project, I used JSX together with <a href="https://www.webcomponents.org/">web components</a>. This way, web components start looking very similar to React class-level components, see my <a href="https://github.com/learosema/boulders/blob/main/src/game/components/game-menu.tsx">game menu component</a>.</p>
<p>A shortened version of this component is below:</p>
<pre class="language-tsx"><code class="language-tsx"><span class="token keyword">import</span> <span class="token punctuation">{</span> h<span class="token punctuation">,</span> fragment<span class="token punctuation">,</span> renderTree <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'../utils/jsx-factory'</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">class</span> <span class="token class-name">GameMenu</span> <span class="token keyword">extends</span> <span class="token class-name">HTMLElement</span> <span class="token punctuation">{</span>

  <span class="token keyword">static</span> <span class="token function">register</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    customElements<span class="token punctuation">.</span><span class="token function">define</span><span class="token punctuation">(</span><span class="token string">'game-menu'</span><span class="token punctuation">,</span> GameMenu<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>

  gameMenu<span class="token operator">:</span> HTMLDialogElement<span class="token operator">|</span><span class="token keyword">null</span> <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span>
  menuButton<span class="token operator">:</span> HTMLButtonElement<span class="token operator">|</span><span class="token keyword">null</span> <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span>

  <span class="token function">render</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">const</span> currentURL <span class="token operator">=</span> document<span class="token punctuation">.</span>location<span class="token punctuation">.</span>href<span class="token punctuation">;</span>
    <span class="token function">renderTree</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">,</span>
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span></span><span class="token punctuation">></span></span><span class="token plain-text">
        </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>button</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>burger<span class="token punctuation">"</span></span> <span class="token attr-name">aria-controls</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>gameMenu<span class="token punctuation">"</span></span> <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>open menu<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token plain-text"> 
          </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>svg</span> <span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0 0 16 16<span class="token punctuation">"</span></span> <span class="token attr-name">fill</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>currentColor<span class="token punctuation">"</span></span> <span class="token attr-name">aria-hidden</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token plain-text">
            </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>rect</span> <span class="token attr-name">x</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>1<span class="token punctuation">"</span></span> <span class="token attr-name">y</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>1<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>14<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>3<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span><span class="token plain-text">
            </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>rect</span> <span class="token attr-name">x</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>1<span class="token punctuation">"</span></span> <span class="token attr-name">y</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>6<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>14<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>3<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span><span class="token plain-text">
            </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>rect</span> <span class="token attr-name">x</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>1<span class="token punctuation">"</span></span> <span class="token attr-name">y</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>11<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>14<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>3<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span><span class="token plain-text">
          </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>svg</span><span class="token punctuation">></span></span><span class="token plain-text">
        </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</span><span class="token punctuation">></span></span><span class="token plain-text">
        </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>dialog</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>game-menu flow<span class="token punctuation">"</span></span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>gameMenu<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token plain-text">
          </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h2</span><span class="token punctuation">></span></span><span class="token plain-text">Menu</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h2</span><span class="token punctuation">></span></span><span class="token plain-text">
          </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>form</span> <span class="token attr-name">method</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>dialog<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token plain-text">
            </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>button</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>buttonReturnToGame<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>button<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token plain-text">Return to game</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</span><span class="token punctuation">></span></span><span class="token plain-text">
          </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>form</span><span class="token punctuation">></span></span><span class="token plain-text">
          </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>currentURL<span class="token punctuation">}</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>button<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token plain-text">Restart game</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span><span class="token plain-text">
          </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>button<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token plain-text">Back to main menu</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span><span class="token plain-text">
        </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>dialog</span><span class="token punctuation">></span></span><span class="token plain-text">  
      </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span></span><span class="token punctuation">></span></span>
    <span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>

  <span class="token function">connectedCallback</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">render</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>menuButton <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token generic-function"><span class="token function">querySelector</span><span class="token generic class-name"><span class="token operator">&lt;</span>HTMLButtonElement<span class="token operator">></span></span></span><span class="token punctuation">(</span><span class="token string">'.burger'</span><span class="token punctuation">)</span><span class="token operator">!</span><span class="token punctuation">;</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>gameMenu <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token generic-function"><span class="token function">querySelector</span><span class="token generic class-name"><span class="token operator">&lt;</span>HTMLDialogElement<span class="token operator">></span></span></span><span class="token punctuation">(</span><span class="token string">'.game-menu'</span><span class="token punctuation">)</span><span class="token operator">!</span><span class="token punctuation">;</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>menuButton<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'click'</span><span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">.</span>onClickButton<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>

  <span class="token function">disconnectedCallback</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>menuButton<span class="token operator">?.</span><span class="token function">removeEventListener</span><span class="token punctuation">(</span><span class="token string">'click'</span><span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">.</span>onClickButton<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>innerHTML <span class="token operator">=</span> <span class="token string">''</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>

  <span class="token function-variable function">onClickButton</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token keyword">this</span><span class="token punctuation">.</span>gameMenu<span class="token operator">?.</span><span class="token function">showModal</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>The methods <code>connectedCallback</code> and <code>disconnectedCallback</code> are quite similar to the React lifecycle methods <code>componentDidMount</code> and <code>componentWillUnmount</code>.</p>
<p>One major difference to React is the fact I'm using <code>class</code> attributes rather than <code>className</code>. React chose to do so as <code>class</code> and <code>for</code> are reserved JavaScript keywords. Apparently, it works fine with <code>class</code> attributes so I prefer to stick to HTML as close as possible.</p>
<p>You could add certain transformations in your own JSX implementation to make both <code>class</code> and <code>className</code> work, but I decided to not do that.</p>
<p>I also kept the event handling separate and just went with using <code>addEventListener</code>. With some additional effort, we could also add support for adding event handlers in a declarative way, like React does.</p>
<h2>Setting up JSX for TypeScript</h2>
<p>You can enable JSX for TypeScript by editing the <code>tsconfig.json</code> by setting a couple of options.</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span>
  <span class="token property">"jsx"</span><span class="token operator">:</span> <span class="token string">"react"</span><span class="token punctuation">,</span>
  <span class="token property">"jsxFactory"</span><span class="token operator">:</span> <span class="token string">"h"</span><span class="token punctuation">,</span>
  <span class="token property">"jsxFragmentFactory"</span><span class="token operator">:</span> <span class="token string">"fragment"</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span></code></pre>
<p>There are multiple different types of JSX implementations. I'm using the legacy JSX factory implementation for now which was used in React up to version 16.</p>
<p>React version 17 introduced a new kind of JSX factory which I don't use yet. I will cover the differences of the old and new JSX in a follow-up article.</p>
<h2>Providing an implementation for the JSX factory</h2>
<p>In order to make the JSX factory work, we need to provide implementations for the <code>h()</code> function and also for <code>fragment</code>.</p>
<p>Im my case, <code>h()</code> returns an object using a recursive <code>DOMTree</code> interface. It describes the structure of the generated JSX element.</p>
<pre class="language-ts"><code class="language-ts"><span class="token keyword">export</span> <span class="token keyword">interface</span> <span class="token class-name">DOMTree</span> <span class="token punctuation">{</span>
  tagName<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span>
  attribs<span class="token operator">:</span> Record<span class="token operator">&lt;</span><span class="token builtin">string</span><span class="token punctuation">,</span> <span class="token builtin">string</span><span class="token operator">></span><span class="token punctuation">,</span>
  children<span class="token operator">:</span> DOMTree<span class="token punctuation">[</span><span class="token punctuation">]</span>
<span class="token punctuation">}</span></code></pre>
<p>This way, the implementation for <code>h()</code> and <code>fragment</code> is pretty straightforward:</p>
<pre class="language-ts"><code class="language-ts"><span class="token keyword">const</span> svgNS <span class="token operator">=</span> <span class="token string">'http://www.w3.org/2000/svg'</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">const</span> fragment <span class="token operator">=</span> <span class="token string">'fragment'</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token function">h</span><span class="token punctuation">(</span>
  tagName<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">,</span> 
  attribs<span class="token operator">:</span> Record<span class="token operator">&lt;</span><span class="token builtin">string</span><span class="token punctuation">,</span> <span class="token builtin">string</span><span class="token operator">></span><span class="token punctuation">,</span> 
  <span class="token operator">...</span>children<span class="token operator">:</span> DOMTree<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token operator">:</span> DOMTree <span class="token punctuation">{</span>
  <span class="token keyword">return</span> <span class="token punctuation">{</span>
    tagName<span class="token punctuation">,</span> attribs<span class="token punctuation">,</span> children
  <span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>In a former version of my <code>h()</code> implementation, I created actual dom nodes directly via <code>document.createElement</code> instead of returning a data structure.</p>
<p>But in order to make inline SVG code work, it is important to switch to the XML namespace pointing to <code>http://www.w3.org/2000/svg</code> as soon as there is an <code>&lt;svg&gt;</code> tag, so my nodes need to know about their parent elements.</p>
<p>As a basic solution, I created a recursive <code>renderTree</code> function that takes care of that, using the <code>element.namespaceURI</code> property to retrieve the current XML namespace.</p>
<p>Additionally, support of fragments (using <code>&lt;&gt;&lt;/&gt;</code> in JSX) are made possible via the <a href="https://developer.mozilla.org/en-US/docs/Web/API/DocumentFragment"><code>DocumentFragment</code></a>-API.</p>
<pre class="language-ts"><code class="language-ts"><span class="token keyword">const</span> svgNS <span class="token operator">=</span> <span class="token string">'http://www.w3.org/2000/svg'</span><span class="token punctuation">;</span>

<span class="token comment">/**
 * Render a DOM structure
 * 
 * @param node the container element where the DOM tree is appended to.
 * @param tree the DOM structure to be created
 */</span>
<span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token function">renderTree</span><span class="token punctuation">(</span>node<span class="token operator">:</span> Element<span class="token punctuation">,</span> tree<span class="token operator">:</span> DOMTree<span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> namespace <span class="token operator">=</span> tree<span class="token punctuation">.</span>tagName <span class="token operator">===</span> <span class="token string">'svg'</span> <span class="token operator">?</span> svgNS <span class="token operator">:</span> node<span class="token punctuation">.</span>namespaceURI<span class="token punctuation">;</span>
  <span class="token keyword">let</span> el<span class="token operator">:</span> Element<span class="token operator">|</span>DocumentFragment<span class="token punctuation">;</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span>tree<span class="token punctuation">.</span>tagName <span class="token operator">===</span> fragment<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    el <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">DocumentFragment</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
    el <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">createElementNS</span><span class="token punctuation">(</span>namespace<span class="token punctuation">,</span> tree<span class="token punctuation">.</span>tagName<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">const</span> <span class="token punctuation">[</span>attrib<span class="token punctuation">,</span> value<span class="token punctuation">]</span> <span class="token keyword">of</span> Object<span class="token punctuation">.</span><span class="token function">entries</span><span class="token punctuation">(</span>tree<span class="token punctuation">.</span>attribs <span class="token operator">||</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      el<span class="token punctuation">.</span><span class="token function">setAttribute</span><span class="token punctuation">(</span>attrib<span class="token punctuation">,</span> value<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>
  <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">const</span> child <span class="token keyword">of</span> tree<span class="token punctuation">.</span>children<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">typeof</span> child <span class="token operator">===</span> <span class="token string">"string"</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      el<span class="token punctuation">.</span><span class="token function">appendChild</span><span class="token punctuation">(</span>document<span class="token punctuation">.</span><span class="token function">createTextNode</span><span class="token punctuation">(</span>child<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token keyword">continue</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    <span class="token function">renderTree</span><span class="token punctuation">(</span>el <span class="token keyword">instanceof</span> <span class="token class-name">DocumentFragment</span> <span class="token operator">?</span> node <span class="token operator">:</span> el<span class="token punctuation">,</span> child<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>

  node<span class="token punctuation">.</span><span class="token function">appendChild</span><span class="token punctuation">(</span>el<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<h2>Providing JSX type definitions</h2>
<p>Finally, you need to declare a JSX namespace in a type declaration file, suffixed <code>.d.ts</code>.</p>
<p>Be careful with the naming of the files. When the factory is in <code>jsx.ts</code>, don't name the type definition file <code>jsx.d.ts</code> as there may be conflicts.</p>
<pre class="language-ts"><code class="language-ts"><span class="token keyword">declare</span> <span class="token keyword">interface</span> <span class="token class-name">DOMTree</span> <span class="token punctuation">{</span>
  tagName<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span>
  attribs<span class="token operator">:</span> Record<span class="token operator">&lt;</span><span class="token builtin">string</span><span class="token punctuation">,</span> <span class="token builtin">string</span><span class="token operator">></span><span class="token punctuation">;</span>
  children<span class="token operator">:</span> DOMTree<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">declare</span> <span class="token keyword">namespace</span> <span class="token constant">JSX</span> <span class="token punctuation">{</span>
  <span class="token keyword">interface</span> <span class="token class-name">Element</span> <span class="token keyword">extends</span> <span class="token class-name">DOMTree</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
  <span class="token keyword">interface</span> <span class="token class-name">Attributes</span> <span class="token punctuation">{</span>
    <span class="token punctuation">[</span>attrib<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">]</span><span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>

  <span class="token keyword">interface</span> <span class="token class-name">IntrinsicElements</span> <span class="token punctuation">{</span>
    <span class="token punctuation">[</span>elem<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">]</span><span class="token operator">:</span> Attributes<span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<h2>Final thought</h2>
<p>A final thought I had when I was about to finish up this article was: can we also use it server-side?</p>
<p>You can make use of your web component code node.js by using ts-node. This way, you can use it in an express application or in a static site generator such as Eleventy. A nice buzzword for that would be &quot;isomorphic typescript&quot; 🥳.</p>
<p>When it comes to node applications, you often still work a lot with the <code>require()</code> notation for importing dependencies, aka the CommonJS module system (including Eleventy stable as of current). Importing a `.tsx`` file inside node is a bit trickier:</p>
<pre class="language-js"><code class="language-js"><span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'ts-node'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">register</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token literal-property property">lazy</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> <span class="token literal-property property">esm</span><span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span> <span class="token literal-property property">moduleTypes</span><span class="token operator">:</span> <span class="token punctuation">{</span>
  <span class="token string-property property">'src/**/*.{ts,tsx}'</span><span class="token operator">:</span> <span class="token string">'cjs'</span>
<span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// Then, you can use require with your typescript files:</span>
<span class="token keyword">const</span> <span class="token punctuation">{</span> h<span class="token punctuation">,</span> fragment <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'./src/utils/jsx.ts'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> <span class="token punctuation">{</span> MyComponent <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'./src/components/my-component.tsx'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>For rendering the component server-side, you will need a DOM implementation. <a href="https://github.com/jsdom/jsdom">JSDOM</a> or <a href="https://github.com/WebReflection/linkedom">LinkeDOM</a> will do. I feel I should also write an in-depth article about this.</p>

      ]]></content:encoded>
    </item><item>
      <title><![CDATA[Testing Web Components]]></title><link>https://lea.codes/posts/2023-12-18-testing-web-components/</link>
      <guid>https://lea.codes/posts/2023-12-18-testing-web-components/</guid>
      <author>lea@lea.lgbt (Lea Rosema)</author>
      <pubDate>Mon, 18 Dec 2023 00:00:00 GMT</pubDate>
      <description><![CDATA[A write-up on testing web components.]]></description>
      <category><![CDATA[post]]></category>
      <category><![CDATA[javascript]]></category>
      <content:encoded><![CDATA[<p>With the help of the <code>customElements</code> API, developers for the web platform can now build their own custom html elements, bringing a zero-footprint approach to a component-based archtitecture to the web.</p>
<h2>But what about testing?</h2>
<p>Testing might still be one of the reasons why people choose a framework like React rather than working with the native web components.</p>
<p>But it turns out testing web components is not too complex. All you need is a test runner of your choice and a  DOM API. There you have several options:</p>
<ul>
<li>run the tests in a real browser, or a headless one</li>
<li>use a console-based test runner with a (fake) DOM library</li>
</ul>
<h2>Using the node.js test runner</h2>
<p>As I learned via the talk by <a href="https://github.com/hamburg-js/proposals/issues/26">Sebastian Schürmann</a>, node.js comes with its own integrated test runner. By default, <code>node --test</code> runs all files in the project which are suffixed with <code>.test.js</code>, <code>.test.cjs</code> and <code>.test.mjs</code>.</p>
<p>For this article, I've created a test project with a password-input web component as a demo: <a href="https://github.com/learosema/wctest">wctest</a>.</p>
<p>The test is using the <code>describe</code> and <code>it</code>-syntax which can be imported from <code>&quot;node:test&quot;</code>.</p>
<p>In order to be able to run DOM APIs inside my test, I have to emulate it for my test environment. I'm using <a href="https://github.com/WebReflection/linkedom">linkedom</a> which is a fast implementation based on <a href="https://github.com/WebReflection/jsdon#readme">JSDON</a> and linked lists.</p>
<p>It works like this:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">import</span> <span class="token punctuation">{</span> parseHTML <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'linkedom'</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> <span class="token punctuation">{</span>
  window<span class="token punctuation">,</span> document<span class="token punctuation">,</span> customElements<span class="token punctuation">,</span>
  HTMLElement<span class="token punctuation">,</span> Event<span class="token punctuation">,</span> CustomEvent
<span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">parseHTML</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">
  &lt;!doctype html>
  &lt;html lang="en">
    &lt;body>
    &lt;/body>
  &lt;/html>
</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>It even comes with the <code>customElements</code> API so this works nearly out of the box.
The only tricky thing might be the fact I won't get global namespace pollution, which is normally a good thing, but my modules to be tested need them in the global namespace.</p>
<p>So, the workaround I'm using is to put the APIs consumed into the global namespace and then do a dynamic import of my module in the test setup:</p>
<pre class="language-js"><code class="language-js"><span class="token function">describe</span><span class="token punctuation">(</span><span class="token string">'password-input component'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">=></span> <span class="token punctuation">{</span>
  <span class="token keyword">let</span> window<span class="token punctuation">,</span> document<span class="token punctuation">,</span> customElements<span class="token punctuation">,</span> HTMLElement<span class="token punctuation">,</span> DocumentFragment<span class="token punctuation">,</span> PasswordInput<span class="token punctuation">,</span> Event<span class="token punctuation">;</span>

  <span class="token function">before</span><span class="token punctuation">(</span><span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
    window <span class="token operator">=</span> global<span class="token punctuation">.</span>window <span class="token operator">=</span> <span class="token function">parseHTML</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">&lt;!DOCTYPE html>&lt;html>&lt;head>&lt;/head>&lt;body>&lt;/body>&lt;/html></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    DocumentFragment <span class="token operator">=</span> global<span class="token punctuation">.</span>DocumentFragment <span class="token operator">=</span> window<span class="token punctuation">.</span>DocumentFragment<span class="token punctuation">;</span>
    document <span class="token operator">=</span> global<span class="token punctuation">.</span>document <span class="token operator">=</span> window<span class="token punctuation">.</span>document<span class="token punctuation">;</span>
    customElements <span class="token operator">=</span> global<span class="token punctuation">.</span>customElements <span class="token operator">=</span> window<span class="token punctuation">.</span>customElements<span class="token punctuation">;</span>
    Event <span class="token operator">=</span> global<span class="token punctuation">.</span>Event <span class="token operator">=</span> window<span class="token punctuation">.</span>Event<span class="token punctuation">;</span>
    HTMLElement <span class="token operator">=</span> global<span class="token punctuation">.</span>HTMLElement <span class="token operator">=</span> window<span class="token punctuation">.</span>HTMLElement<span class="token punctuation">;</span>

    <span class="token keyword">const</span> module <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token keyword">import</span><span class="token punctuation">(</span><span class="token string">'./password-input.js'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    
    PasswordInput <span class="token operator">=</span> module<span class="token punctuation">.</span>PasswordInput<span class="token punctuation">;</span>
    PasswordInput<span class="token punctuation">.</span><span class="token function">register</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<h2>Alternative: jest</h2>
<p>A more common choice to test web components is to use jest. I haven't prepared a special demo for this article, but I did this in the past with my <a href="https://github.com/shader-art/shader-art/">shader-art</a> web component, which is for a lightweight embedding of shader animations into websites while respecting &quot;reduced motion&quot; settings of your operating system.</p>
<p>You can configure jest to use JSDOM which allows you to use DOM-APIs. One essential configuration option you have to specify in your <code>jest.config.js</code> is <code>testEnvironment: 'jsdom'</code>.</p>
<p>Additionally, you can also specify how jest would handle further things, which could also impact your test execution time. For testing canvas2d graphics, you can npm install <code>canvas</code> and jsdom will use that to emulate the canvas API. That would even allow you to do some visual regression testing.</p>
<p>When emulating the canvas API in your test, you may also want to work with images. So, another setting that comes into play is <code>resources: 'usable'</code>, which is configured in the <code>testEnvironmentOptions</code> inside the <code>jest.config.js</code>.</p>
<pre class="language-js"><code class="language-js">module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span>
  <span class="token literal-property property">testEnvironment</span><span class="token operator">:</span> <span class="token string">'jsdom'</span><span class="token punctuation">,</span>
  <span class="token literal-property property">testEnvironmentOptions</span><span class="token operator">:</span> <span class="token punctuation">{</span>
    <span class="token literal-property property">resources</span><span class="token operator">:</span> <span class="token string">'usable'</span><span class="token punctuation">,</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span></code></pre>
<p>For my shader-art component, I chose to use a webgl mock for jest which you can install via NPM and specify it via <code>setupFiles: ['jest-webgl-canvas-mock']</code> inside the <code>jest.config.js</code>.</p>
<h2>Further approaches?</h2>
<p>For now, I decided for me that those above two approaches are enough for me for now. I'm would be excited to explore other approaches which may involve using a headless browser and/or cypress. If you have a demo project or want to talk about it with me, feel free to reach out. 💖</p>

      ]]></content:encoded>
    </item><item>
      <title><![CDATA[WebGL Shaders Tutorial]]></title><link>https://lea.codes/posts/2023-06-04-webgl-shaders-tutorial/</link>
      <guid>https://lea.codes/posts/2023-06-04-webgl-shaders-tutorial/</guid>
      <author>lea@lea.lgbt (Lea Rosema)</author>
      <pubDate>Sun, 04 Jun 2023 00:00:00 GMT</pubDate>
      <description><![CDATA[A short tutorial on WebGL and Shaders]]></description>
      <category><![CDATA[post]]></category>
      <category><![CDATA[javascript]]></category>
      <category><![CDATA[webgl]]></category>
      <category><![CDATA[glsl]]></category>
      <content:encoded><![CDATA[<p>In this article, I would like to give a short introduction on WebGL, a low level graphics engine running in the browser.</p>
<p>A common misconception is that people often think of WebGL as a 3D engine, but it is in fact a rasterization engine for drawing points, lines and triangles.</p>
<p>This can be used as building bricks for creating a 3D engine. Actually there are a number of 3D engines implemented on top of WebGL, like <a href="https://threejs.org/">three.js</a> or <a href="https://babylonjs.com/">babylon.js</a>.</p>
<h2>WebGL and OpenGL</h2>
<p>WebGL is based on OpenGL ES, providing a thin JavaScript layer over the OpenGL ES API. It comes in different versions, basically WebGL 1 (based on OpenGL ES 2.0) and WebGL 2 (based on OpenGL ES 3.0).</p>
<p>On Safari 14 and older or Internet Explorer, only WebGL 1 is available. WebGL 2 removes some limitations of WebGL 1.</p>
<p>The tech people said to me &quot;you don't need to support WebGL 1 anymore&quot; but Lea is more into making things as inclusive as possible and so I think I should stick to WebGL 1 as I don't really touch the features of WebGL 2 in this article anyway. Also, there is a great in-depth site on <a href="https://webgl2fundamentals.org/">WebGL 2</a>. Besides that, there also is a great site on <a href="https://webglfundamentals.org/">WebGL 1</a> which goes more in-depth than this article.</p>
<h2>It runs on the GPU</h2>
<p>WebGL is a low-level graphics library, which provides an API for running programs on the GPU.</p>
<ul>
<li>Programs consist of a pair of functions, called shaders.</li>
<li>Shaders are written in a C-like language, called GLSL (GL Shader language).</li>
<li>There is a Vertex Shader and a Fragment Shader.</li>
<li>Most of the WebGL API is about setting up the state for the shaders and passing data from JavaScript to WebGL</li>
</ul>
<h2>Vertex Shaders</h2>
<p>The job of the Vertex shader is to receive data from a buffer and to calculate vertex positions based on that data.
Based on the positions it returns, WebGL can then rasterize certain primitives, like points, lines or triangles.</p>
<p>You can imagine the vertex shader as a function that takes a data record and returns a position. A common use-case of the vertex shader is to take a 3D coordinate and apply a perspective projection, for example.</p>
<h2>Fragment shaders</h2>
<p>When rasterizing these shapes (filling triangles with pixels), the fragment shader is run. This is done in a highly parallelized manner.
For each fragment(=pixel) of the shape, the fragment shader is run in parallel.</p>
<p>The fragment shader is like a function that takes a number of arguments like the fragment position (where inside the shape am I?) and returns a color based on that.</p>
<p>It works a lot like <a href="https://tixy.land/">tixy.land</a>, where you have a function that is executed for each dot in a dot matrix, taking parameters like the x,y coordinate, the time value and the pixel index and return a value representing the dot size and color in a dot matrix.</p>
<h2>Drawing a triangle</h2>
<p>One of the most straightforward WebGL programs is drawing a triangle using a constant color.</p>
<p>We'll provide buffer data for 3 points, and the vertex shader will be doing nothing else but directly returning the data without any further calculation. For each record in the data, the vertex shader is run to calculate a vertex position, spanning up a triangle.</p>
<p>There are several draw modes available in WebGL: shall we draw points, lines, triangles or a strip of triangles? We'll focus on triangles in ths article.</p>
<h3>Code for the Vertex Shader</h3>
<pre class="language-glsl"><code class="language-glsl"><span class="token keyword">precision</span> <span class="token keyword">highp</span> <span class="token keyword">float</span><span class="token punctuation">;</span>
<span class="token keyword">attribute</span> <span class="token keyword">vec4</span> position<span class="token punctuation">;</span>

<span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  gl_Position <span class="token operator">=</span> position<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>The vertex shader takes a record from buffers through an <code>attribute</code>. In this case, we are only using a <code>position</code> attribute, which is directly passed to the global <code>gl_Position</code>.</p>
<p>The coordinates passed to <code>gl_Position</code> are like in a carthesian coordinate system with <code>(0, 0)</code> in the center, <code>(-1, -1)</code> on the bottom left corner and <code>(1, 1)</code> on the top right corner.</p>
<h3>Code for the Fragment Shader</h3>
<pre class="language-glsl"><code class="language-glsl"><span class="token keyword">precision</span> <span class="token keyword">highp</span> <span class="token keyword">float</span><span class="token punctuation">;</span>

<span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token comment">// return red. 100% red, 0% green, 0% blue, 100% alpha</span>
  gl_FragColor <span class="token operator">=</span> <span class="token keyword">vec4</span><span class="token punctuation">(</span><span class="token number">1.0</span><span class="token punctuation">,</span> <span class="token number">0.0</span><span class="token punctuation">,</span> <span class="token number">0.0</span><span class="token punctuation">,</span> <span class="token number">1.0</span><span class="token punctuation">)</span><span class="token punctuation">;</span> 
<span class="token punctuation">}</span></code></pre>
<p>The fragment shader sets the global <code>gl_FragColor</code> to a constant vec4 value. The components of the vec4 vector represent red, green, blue and alpha values.</p>
<p>Another global variable available is <code>gl_FragCoord</code>, a vec2 value pointing to the current coordinate of the pixel being processed.</p>
<h3>GL Shader Language</h3>
<p>As you may have noticed, glsl looks like a C-like programming language. It is. Most primitive types as in C are available (most importantly int, float, double). In addition to that, we have additional types for vectors (<code>vec2</code>, <code>vec3</code>, <code>vec4</code>, or their integer companions <code>ivec2</code>, <code>ivec3</code>, <code>ivec4</code>) and matrices (<code>mat2</code>, <code>mat3</code>, <code>mat4</code>, ...). One nice perk of GLSL is: things like matrix multiplication is built-in, just use the <code>*</code> operator.</p>
<p>On my website, I have a little GLSL overview which should help you getting started:
<a href="https://lea.codes/webgl/glsl-overview/">https://lea.codes/webgl/glsl-overview/</a></p>
<p>In WebGL 1, you may face the limitation that the break condition of for-loops only work when a constant condition is used, as in <code>for (int i = 0; i &lt; 10; i++) {}</code>. Using another dynamic variable for the break condition (as in <code>i &lt; j</code>) is not allowed. This limitation is removed in WebGL 2.</p>
<h3>The full working demo</h3>
<p>Here's the full code. I'm using a <a href="https://github.com/shader-art/shader-art">shader-art</a> web component to abstract away WebGL a bit so we can focus on the shaders.</p>
<figure class="demo-figure">
  <div class="demo-figure__link">Demo: <a href="https://lea.codes/demos/shader-1">Red triangle</a></div>
  <figcaption>
    <cite>A red triangle spanning between the bottom left, top left and top right corners of the viewport.</cite>
    <a href="https://lea.codes/demos/shader-1/code/">view code</a>
  </figcaption>
</figure>
<h2>Passing data from the vertex shader to the fragment shader</h2>
<p>In the next demo, we'll draw two triangles filling the whole viewport of the canvas. Alongside with the position buffer, we'll also provide a UV buffer, which is often used for texture coordinates. So, the top left corner is at <code>(0, 0)</code> and the bottom right is at <code>(1, 1)</code>.</p>
<p>The UV coordinates are received in the vertex shader as a 2D, vector: <code>attribute vec2 uv</code>. These can be passed over to the fragment shader through a <code>varying</code> variable. A common convention is to prefix varying variables with <code>v</code>, like <code>vUV</code>.</p>
<p>Additionally, these values are not only just passed over from the vertex to the fragment shader, but also interpolated between the edges of the triangle shapes.</p>
<p>This way, we can create a gradient. In the example, the fragment shader calculates a color based on the interpolated UV coordinates, using red as a constant 100% value, green as the <code>uv.x</code> value, blue as the <code>uv.y</code> value.</p>
<figure class="demo-figure">
  <div class="demo-figure__link">Demo: <a href="https://lea.codes/demos/shader-2">Gradient</a></div>
  <figcaption>
    <cite>A gradient in with the colors red in the top left, purple in the bottom left, yellow in the top right and white in the bottom right.</cite>
    <a href="https://lea.codes/demos/shader-2/code/">view code</a>
  </figcaption>
</figure>
<p>In this case, it would also be possible to calculate the uv coordinates from the <code>position</code>, but that's only trivial when dealing with a rectangle. With having additional UV coordinates, we could shift the positions in the vertex shader a bit (in a way there are no right angles anymore, imagine a perspective projection) and the uv vector still provides us with correct values.</p>
<h2>Passing data from JavaScript to shaders</h2>
<p>You can use <code>uniform</code> variables to pass data from JavaScript to your shaders. The <code>&lt;shader-art&gt;</code> web component passes a <code>uniform vec2 resolution</code> uniform and a <code>uniform float time</code> to your shaders which you can use both in the vertex shader as well as in the fragment shader. The <code>resolution</code> uniform holds a 2d vector containing the absolute pixel resolution of the canvas and is useful for aspect ratio correction, eg. whenever you want to draw perfect squares and circles within your shaders. The <code>time</code> uniform holds the amount of microseconds passed and can be used for simple animations.</p>
<p>With the time value, you can start to get create animations similar to in <a href="https://tixy.land/">tixy.land</a>.</p>
<p>The demo below shows an animation, feeding a noise function with xy-coordinates and a time value, with the noise function is being combination of sine and cosine functions. This already creates a beautiful effect.</p>
<figure class="demo-figure">
  <div class="demo-figure__link">Demo: <a href="https://lea.codes/demos/shader-3">Noise Animation</a></div>
  <figcaption>
    <cite>Animation of noise created from sine functions, using rainbow colors</cite>
    <a href="https://lea.codes/demos/shader-3/code/">view code</a>
  </figcaption>
</figure>
<h2>Migrating to WebGL2</h2>
<p>From the shader-side, it's basically this:</p>
<ul>
<li>Prefix your shaders with <code>#version 300 es</code></li>
<li><code>attribute</code> variables in the vertex shader are declared as <code>in</code>, eg. <code>in vec4 position;</code></li>
<li><code>varying</code> variables in the vertex shader are declared as <code>out</code> instead, eg. <code>out vec2 vUv;</code></li>
<li><code>varying</code> variables in the fragment shader are declared as <code>in</code> instead, eg. <code>in vec2 vUv;</code></li>
<li>there's no <code>gl_FragColor</code> anymore in the fragment shader, declare an <code>out vec4 fragColor;</code> and use that (name it as you like)</li>
</ul>
<p>From the JavaScript-side, you will call canvas.getContext('webgl2') instead of canvas.getContext('webgl') and you will receive a <code>WebGL2RenderingContext</code> which is mainly backwards-compatible with <code>WebGLRenderingContext</code>. The <code>&lt;shader-art&gt;</code> component takes care of that for you as soon as it sees the <code>#version 300 es</code> pragma.</p>
<h2>Where to go from here</h2>
<p>Feel free to take the code and play around with it. All code is licensed under the MIT license (see <a href="https://github.com/lea-lgbt/blog/blob/main/LICENSE">LICENSE</a>).</p>
<p>You are not tied to the <code>&lt;shader-art&gt;</code> web component I created for educational purposes. You can re-use this knowledge in any webgl framework like THREE.js, PIXI.js, P5.js and more. I've created a repository with a couple of webgl templates using different frameworks: <a href="https://github.com/learosema/webgl-templates">https://github.com/learosema/webgl-templates</a>.</p>
<p>If you like to learn all the things about shaders, have a look at <a href="https://thebookofshaders.com/">https://thebookofshaders.com/</a>.
Also, check out <a href="https://iquilezles.org/">https://iquilezles.org/</a>.</p>
<p>If you love to learn all the details about WebGL, have a look at <a href="https://webglfundamentals.org/">webglfundamentals.org</a>. Also, I shared a lot of information about webgl on my personal website, <a href="https://lea.codes/">lea.codes</a>.</p>
<p>Also, a definite recommend is the course <a href="https://threejs-journey.com/">threejs-journey.com</a> by Bruno Simon, especially if you prefer not to dive too deep into WebGL internals and shaders and focus on working with a fully fledged 3D engine and doing content creation.</p>

      ]]></content:encoded>
    </item><item>
      <title><![CDATA[Interactive demos inside articles]]></title><link>https://lea.codes/posts/2023-05-17-interactive-demos-inside-articles/</link>
      <guid>https://lea.codes/posts/2023-05-17-interactive-demos-inside-articles/</guid>
      <author>lea@lea.lgbt (Lea Rosema)</author>
      <pubDate>Thu, 01 Jun 2023 00:00:00 GMT</pubDate>
      <description><![CDATA[This article touches how I embed interactive code demos into my blog articles.]]></description>
      <category><![CDATA[post]]></category>
      <category><![CDATA[eleventy]]></category>
      <category><![CDATA[javascript]]></category>
      <category><![CDATA[meta]]></category>
      <content:encoded><![CDATA[<p>I wanted to embed demo snippets of html/css/js into my articles, so I added a <code>{% demo &quot;demo-name&quot; %}</code> shortcode to my Eleventy blog. Those demos are just plain html files located in <code>src/demos</code>, with everything inline, with the html skeleton provided by Eleventy.</p>
<p>As <code>&lt;style&gt;</code> tags would then be put into the body, I added a html transform rule which moves those <code>&lt;style&gt;</code> tags from <code>&lt;body&gt;</code> to <code>&lt;head&gt;</code>. Although browsers seem to tolerate style tags inside <code>&lt;body&gt;</code>, it would not be valid HTML.</p>
<p>Those html transforms are processed in my Eleventy project at build time using <a href="https://github.com/WebReflection/linkedom">linkedom</a>, which is a lightweight and fast DOM implementation. The <a href="https://github.com/lea-lgbt/blog/blob/main/config/plugins/html-transform.js">code</a> for it is put into a separate eleventy config file which can be loaded into the main configuration file via <code>eleventyConfig.addPlugin</code>.</p>
<p>Using LinkeDOM is pretty straightforward, you have a <code>parseHTML</code> function where you pass-in a string of HTML code and you will get a DOM API back to work with, containing <code>{ document, customElements }</code> and more.</p>
<p>So, moving a style tag from head to body on the server-side looks similar to how you would do it in browser-side JavaScript:</p>
<pre class="language-js"><code class="language-js"><span class="token comment">// Moves style tags from body up to the head.</span>
module<span class="token punctuation">.</span><span class="token function-variable function">exports</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span>document<span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  document<span class="token punctuation">.</span>body<span class="token punctuation">.</span><span class="token function">querySelectorAll</span><span class="token punctuation">(</span><span class="token string">'style'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token parameter">style</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
    style<span class="token punctuation">.</span><span class="token function">remove</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    document<span class="token punctuation">.</span>head<span class="token punctuation">.</span><span class="token function">appendChild</span><span class="token punctuation">(</span>style<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<p>For embedding the demos into my articles, I used a tab widget inside a <code>&lt;figure&gt;</code> element, but that widget shouldn't be included into the rss feed, so I only put links to the demos and added a transform rule which adds the tab widget into the blog articles on the website.</p>
<p>The tab widget follows the pattern of the <a href="https://www.w3.org/WAI/ARIA/apg/patterns/tabs/examples/tabs-manual/">Example of Tabs with Manual Activation</a> of the ARIA Authoring Practices Guide (APG).</p>
<p>Below is an example of such a demo embed.</p>
<figure class="demo-figure">
  <div class="demo-figure__link">Demo: <a href="https://lea.codes/demos/hello-world">Hello World</a></div>
  <figcaption>
    <cite>This is my first test of embedding demos into my blog articles.</cite>
    <a href="https://lea.codes/demos/hello-world/code/">view code</a>
  </figcaption>
</figure>
<p>In a next iteration, I could maybe separate the code into HTML/CSS/JS tabs, similar to what you know from embeds like Codepen or jsfiddle.</p>
<h2>The code</h2>
<p>This needs some clean up, I could imagine putting that into an Eleventy plugin. Right now it's sprinkled over all of the code base, but I coded that while I was in hospital during my awake times, only having my iPad with me, laying most of the time in a sickbed.</p>
<p><picture><source type="image/avif" srcset="https://lea.codes/assets/images/sickbed-coding-x200.avif 200w, https://lea.codes/assets/images/sickbed-coding-x400.avif 400w, https://lea.codes/assets/images/sickbed-coding-x600.avif 600w, https://lea.codes/assets/images/sickbed-coding-x800.avif 800w, https://lea.codes/assets/images/sickbed-coding-x1000.avif 1000w" sizes="(min-width: 888px) 862px, calc(100vw - 24px)" /><source type="image/webp" srcset="https://lea.codes/assets/images/sickbed-coding-x200.webp 200w, https://lea.codes/assets/images/sickbed-coding-x400.webp 400w, https://lea.codes/assets/images/sickbed-coding-x600.webp 600w, https://lea.codes/assets/images/sickbed-coding-x800.webp 800w, https://lea.codes/assets/images/sickbed-coding-x1000.webp 1000w" sizes="(min-width: 888px) 862px, calc(100vw - 24px)" /><img src="https://lea.codes/assets/images/sickbed-coding-x200.jpeg" alt="A sickbed in a hospital with dim lighting. There is a bed-table with an iPad and some headphones on top. The app Working Copy is opened, which is a git client with an integrated text editor with syntax highlighting for iOS." loading="lazy" decoding="async" width="1000" height="473" srcset="https://lea.codes/assets/images/sickbed-coding-x200.jpeg 200w, https://lea.codes/assets/images/sickbed-coding-x400.jpeg 400w, https://lea.codes/assets/images/sickbed-coding-x600.jpeg 600w, https://lea.codes/assets/images/sickbed-coding-x800.jpeg 800w, https://lea.codes/assets/images/sickbed-coding-x1000.jpeg 1000w" sizes="(min-width: 888px) 862px, calc(100vw - 24px)" /></picture></p>
<p>Links to the code:</p>
<ul>
<li><a href="https://github.com/lea-lgbt/blog/blob/main/src/_layouts/demo.njk">Layout file for demos</a></li>
<li><a href="https://github.com/lea-lgbt/blog/blob/main/config/plugins/demo.js">Eleventy demo-Shortcode for inserting figures with link and description</a></li>
<li><a href="https://github.com/lea-lgbt/blog/blob/main/config/transforms/demo-embeds.js">Inserting tab widgets into the figures at Eleventy build time</a></li>
<li><a href="https://github.com/lea-lgbt/blog/blob/main/config/events/demo-codeviews.js">Creating Source Code views (mainly in order to have something like view-source links that also work on iPad)</a></li>
<li><a href="https://github.com/lea-lgbt/blog/blob/main/src/assets/js/tabs.js">Clientside JS for the tab widget</a></li>
</ul>

      ]]></content:encoded>
    </item><item>
      <title><![CDATA[Gender Recognition Act in Germany]]></title><link>https://lea.codes/posts/2023-04-29-german-gender-recognition-act/</link>
      <guid>https://lea.codes/posts/2023-04-29-german-gender-recognition-act/</guid>
      <author>lea@lea.lgbt (Lea Rosema)</author>
      <pubDate>Sat, 29 Apr 2023 00:00:00 GMT</pubDate>
      <description><![CDATA[CW negative, german politics, trans hostility. Yesterday, the law draft for the promised gender recognition act (Selbstbestimmungsgesetz) was leaked. Sadly, the main focus weren&#39;t trans people but anti-trans talking points.]]></description>
      <category><![CDATA[post]]></category>
      <category><![CDATA[politics]]></category>
      <category><![CDATA[germany]]></category>
      <category><![CDATA[trans]]></category>
      <category><![CDATA[negative]]></category>
      <content:encoded><![CDATA[<p><strong>CONTENT WARNING for negative german politics and trans hostility.</strong></p><p>Yesterday, the law draft for the promised gender recognition act (aka Selbstbestimmungsgesetz or self-determination act) was leaked.
Sadly, the main focus weren't trans people but anti-trans talking points. TLDR it doesn't deserve to be called self-determination act.</p>
<p>The main concern of the gender recognition act reform in Germany (so called #selbstbestimmungsgesetz) should have been how to remove discrimination and make life easier for trans people. But in fact the law drafter's main concern was how to prevent potential abuse of the law through men, which is a talking point mostly driven by the far-right and anti-trans activists.</p>
<p>I won't share the far-right news site where the leak of the law appeared but instead a <a href="https://strangeobject.space/@lydiafacts/110277216813843841">friendly source</a>.</p>
<p>The fear of potential abuse is spread across the whole law text. Improvements for trans people were not the focus.
Formerly, 2 psychiatric assessments were necessary to change your name and gender marker, you had to answer very intimate questions and you have to pay about 2000€ or more for it. This is no longer necessary, and to be fair, this is a great improvement. But in return, the new law in its current form would worsen the rights of trans people drastically, affecting all areas of everyday life.</p>
<p>There is a waiting period of 3 months added to the law draft. After you've changed the name via the law, you have to wait 3 more months until the name and gender marker is corrected. It is incapacitating and in direct contrast to self-determination. It is also endangering, as conversion therapy during this period can forcibly drive trans people to revoke the declaration.</p>
<p>After the name and gender becomes valid, a further correction of the name and gender marker is blocked for a year. In the former law, there wasn't a waiting period. Intersex people who were able to skip the psychiatric assessments through the 45b law will also have to wait 3 months then, which is a deterioration to their current situation.</p>
<p>In case of war and a return of conscription, the new law wouldn't allow changing the name and gender as it could potentially be abused by men who want to escape from military service.</p>
<p>Another concerning part of the new law is §13, which states that the law should be re-evaluated in 5 years. We expect to get a conservative–right government in 5 years. There is an acute danger that the law could then be repealed by the next government.</p>
<p>A big anti-trans talking point that made it into the law text is the narrative about men abusing the law in order to invade women's spaces, such as changing rooms, showers and women's sauna. The new law references domiciliary rights. It's a guideline of how a sauna provider for example can deny a trans person entrance if they see the intimacy of other guests affected. Usually, the equity law (AGG) prevents denying people entrance just because of their look or their genitals.</p>
<p>Also, this point has nothing to do with reality but is mostly driven by far-right and anti-trans people. In fact, I haven't been in the sauna for many years, like many other trans women. Trans people usually don't bother other people, they just want to live their life. But it's often the case we have to fear harassment from anti-trans people. The last time I've been in a mixed sauna, I was harassed by men in the mixed changing room (unfortunately, there was a lack of single cabins for changing clothes).</p>
<h2>Conclusion</h2>
<p>In the end, it's sadly a very bad law draft that doesn't improve the situation for trans people but makes it worse, affecting all areas of life. Trans women are women and trans men are men, but only if they conform and only if they don't affect cis peoples lives in any way.
It may be even unsure if I can keep my name and gender after the law is re-evaluated by the conservatives.</p>
<p>Germany is a shit country. After 16 years of having a right-conservative government with the CDU, we finally had a government without the CDU (aka red-yellow-green ampel coalition). That is an opportunity to finally make necessary reforms possible after the CDU blocked it for 16 years. Especially abortion rights and trans rights.</p>
<p>Unfortunately, we have the FDP in the government and they are blocking everything progressive because they would love to form a coalition with the conservatives in the next legislation period. The conservatives still have a lot of influence through the federal countries and overall they are gaining popularity again, so it's expected we're going to get the conservatives in the government in the next legislation. Hopefully not for another 16 years.</p>
<p>But most importantly, it's better to stay optimistic. It's still a draft. I still hope the paragraphs about the waiting time, the reference to domiciliary rights and the paragraphs about potential abuse of the law get dropped, either while finalizing the law or afterwards through the constitutional court.</p>
<p>I would like to see trans organizations and also Sven Lehmann and others from the green party speaking up.</p>

      ]]></content:encoded>
    </item><item>
      <title><![CDATA[Pseudorandom numbers in Eleventy]]></title><link>https://lea.codes/posts/2023-04-25-pseudorandom-numbers-in-eleventy/</link>
      <guid>https://lea.codes/posts/2023-04-25-pseudorandom-numbers-in-eleventy/</guid>
      <author>lea@lea.lgbt (Lea Rosema)</author>
      <pubDate>Tue, 25 Apr 2023 00:00:00 GMT</pubDate>
      <description><![CDATA[Create deterministic series of random numbers for generative arts]]></description>
      <category><![CDATA[post]]></category>
      <category><![CDATA[eleventy]]></category>
      <category><![CDATA[numbers]]></category>
      <content:encoded><![CDATA[<p>This blog uses social preview images with some generative waves using the <a href="https://www.disabled-world.com/definitions/disability-pride.php">disability pride flag colors</a>. In order to always have the same wave art on every build, I am not using <code>Math.random()</code> to generate
random path coordinates but a pseudorandom number generator. It generates a deterministic series of numbers from a given seed value. This way it is ensured every build always generates the same image.</p>
<p>A common JavaScript implementation for pseudorandom numbers is available in a <a href="https://gist.github.com/blixt/f17b47c62508be59987b">gist by @blixt</a>.</p>
<p>I wanted to use that in Eleventy, so I added a <a href="https://github.com/lea-lgbt/blog/blob/main/src/_data/prng.js">prng.js</a> file inside my data directory.</p>
<p><code>prng</code> provides a <code>prng.init(12345)</code> function to provide a seed value, which you can use from Nunjucks/Liquid via double brackets.</p>
<p>To generate random numbers, prng.js provides a <code>random</code> function and also a <code>randInt</code> function.</p>
<ul>
<li><code>random</code> generates float values between 0 and 1 (exclusive 1).</li>
<li><code>randInt(a, b)</code> generated integer values between a and b (inclusive b)</li>
<li><code>curve()</code> is a method to generate a bunch of random coordinates for SVG curve paths.</li>
</ul>
<p>There is one little pitfall with this. <code>random</code> is an impure function, which means it is not purely dependant on function parameters but relies on a variable outside the function scope. Before I implemented <code>prng.init</code> as a function, it was just a property. But there's a special thing when using Eleventy as a dev server: it keeps the internal state of variables between builds. Using a function instead to initialize the seed value ensures the seed value is always initialized with the desired value on every build.</p>
<p>In order to provide the seed from frontmatter data, it is required to pass that also to the <code>prng.init</code> function.</p>
<p>The code for generating the waves is <a href="https://github.com/lea-lgbt/blog/blob/main/src/social-preview.njk">here</a>.</p>
<p>The result looks like this:</p>
<p><img src="https://blog.lea.lgbt/assets/images/social-preview/default.jpeg" alt="Social preview Image of my blog. It is titled &quot;Lea's Blog&quot; in bold white letter on a black background. In the bottom of the image, there is a waves art in disability pride colors." /></p>
<p>The process for auto-generating the preview images is described in detail by <a href="https://bnijenhuis.nl/notes/automatically-generate-open-graph-images-in-eleventy/">Bernard Nijenhuis</a> and also integrated into <a href="https://github.com/madrilene/eleventy-excellent">eleventy-excellent</a> by <a href="https://www.lenesaile.com/en/">Lene Saile</a>.</p>

      ]]></content:encoded>
    </item><item>
      <title><![CDATA[Automated workflows for websites]]></title><link>https://lea.codes/posts/2023-04-20-automated-workflows-for-websites/</link>
      <guid>https://lea.codes/posts/2023-04-20-automated-workflows-for-websites/</guid>
      <author>lea@lea.lgbt (Lea Rosema)</author>
      <pubDate>Thu, 20 Apr 2023 00:00:00 GMT</pubDate>
      <description><![CDATA[In this article, I&#39;m writing about automated workflows I added to this blog.]]></description>
      <category><![CDATA[post]]></category>
      <category><![CDATA[eleventy]]></category>
      <category><![CDATA[ci]]></category>
      <content:encoded><![CDATA[<p>A thing I did recently was adding some automated workflows to my blog so I can be sure my HTML, CSS, JS is valid and everything respects the editorconfig.</p>
<p>I also added linkedom for dom-like html transforms.</p>
<p>Credits for the automated checks and transforms go to <a href="https://pepelsbey.dev/">Vadim Makeev</a></p>
<h2>HTML Validator</h2>
<p>For validating HTML, I'm running the <a href="https://validator.w3.org/nu/">Nu HTML Validator</a> against the dist directory. Validating HTML helps catching mistakes I might have otherwise missed. It also catches some accessibility bugs, like an <code>aria-labelledby</code> pointing to an invalid id.</p>
<p>I installed it via <code>npm i -D vnu-jar</code> and added an npm script for checking the html:</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span>
  <span class="token property">"scripts"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
    <span class="token property">"lint:html"</span><span class="token operator">:</span> <span class="token string">"java -jar node_modules/vnu-jar/build/dist/vnu.jar --skip-non-html --filterfile .vnurc dist"</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>In the filter file, you can specify regular expressions to ignore certain errors. I added the role about adding the list role on unordered lists, because Safari removes the role it when you add <code>list-style: none</code> to the css.</p>
<pre class="language-txt"><code class="language-txt">The .list. role is unnecessary for element .ul.\.</code></pre>
<p>This <a href="https://github.com/lea-lgbt/blog/blob/main/.github/workflows/lint-html.yml">GitHub action</a> executes these check on every commit.</p>
<h2>Post-processing HTML</h2>
<p>This blog is built with <a href="https://11ty.dev/">eleventy</a>. Eleventy provides <a href="https://www.11ty.dev/docs/config/#transforms">transforms</a> as a way to post-process your output files.</p>
<p>In order to efficiently work with HTML, it would be cool to have a familiar API for manipulating the HTML structure, like a DOM API. A popular one for this use-case is JSDOM. I'm using <a href="https://webreflection.medium.com/linkedom-a-jsdom-alternative-53dd8f699311">Linkedom</a> which has basically the same API but is more lightweight and also faster. Additionally, it provides possibilities to serialize dom trees to JSON, which is nice.</p>
<p>The flow for post-processing the HTML is to parse the HTML output via Linkedom, which returns a dom object where I can do all the things I know from browserland-JavaScript like <code>dom.document.querySelector</code> or <code>dom.document.appendChild</code>. Afterwards, I can return it back as HTML via <code>document.toString()</code>.</p>
<p>The code for this is here: <a href="https://github.com/lea-lgbt/blog/blob/main/config/plugins/html-transform.js">HTML transform configuration for Eleventy</a></p>
<p>For now, I just added a transform that adds ids for headlines, making headlines and sub-headlines linkable. Another potential use case for this kind of transforms is to also add additional automated accessibility checks. Check the document structure, test if everything has accessible names and check image descriptions, for example.</p>
<p>Another missing feature I plan to add through this is the recognition of external links and maybe open them in a new tab (I'm a bit divided on this, but when I do, I should also convey the information the link opens a new tab).</p>
<h2>ESLint</h2>
<p>ESLint is a tool to check my JavaScript code for common pitfalls. The configuration is pretty straightforward.
All I did here was to <code>npm install eslint</code>, provide a <a href="https://github.com/lea-lgbt/blog/blob/main/.eslintrc.yml">basic configuration</a> file and add an npm script to my <code>package.json</code>:</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span>
  <span class="token property">"scripts"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
    <span class="token property">"lint:js"</span><span class="token operator">:</span> <span class="token string">"eslint eleventy.config.js src/assets/js"</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>Additionally, I've set up a <a href="https://github.com/lea-lgbt/blog/blob/main/.github/workflows/lint-js.yml">GitHub action</a> for it which is run on every pull request and push to main.</p>
<h2>Stylelint</h2>
<p>Same as above for stylelint. I'm using a minimalistic stylelint configuration to avoid the most common pitfalls. I have to find out for myself which configuration works best for me. For now I went with <code>stylelint-config-recommended</code> and I have also set up a <a href="https://github.com/lea-lgbt/blog/blob/main/.github/workflows/lint-css.yml">github action</a> for this.</p>
<h2>Semantic Release</h2>
<p>I'm using <a href="https://semantic-release.gitbook.io/">semantic-release</a> for version management. It automatically takes care of creating release download files and adding git tags.</p>
<p>To use it, it assumes a certain commit message structure. According to prefixes, version numbers are counted up and every release is shown on the GitHub repo page of this blog, including a Changelog in the description.</p>
<p>Fixes (all commits prefixed with <code>fix:</code>) lead to a minor version upcount. Features (all commits prefixed with <code>feat:</code>) lead to a middle version upcount. If you add &quot;BREAKING CHANGE&quot; to the commit description, this leads to a major version upcount. When doing a breaking change, also provide upgrade information in the breaking change.</p>
<p>For now, I just publish to GitHub, not to NPM (thus the <code>private</code> field in the <code>package.json</code>).</p>
<p>To set it up, all I had to do was to <code>npm i -D semantic-release</code>, setup the repository URL in the <code>package.json</code>, setup a <a href="https://github.com/lea-lgbt/blog/blob/main/.github/workflows/release.yml">github action</a> and set the permissions for actions inside my organization to read/write access.</p>
<p>I mainly used semantic-release for library code, not for websites yet. So this is more of an experiment, I don't know yet if it is too useful.</p>
<h2>Further improvement</h2>
<p>Of course, there is room for further improvement. One thing to look into are automated accessibility checks out there, like <a href="https://pa11y.org/">pa11y</a>.
There's a lot of stuff I have to experiment with and figure out what works best for me. Especially the html validation was very insightful and helped identifying some issues in the document structure, but I might not need it on every commit.</p>

      ]]></content:encoded>
    </item><item>
      <title><![CDATA[Hello World!]]></title><link>https://lea.codes/posts/2023-04-15-hello-world/</link>
      <guid>https://lea.codes/posts/2023-04-15-hello-world/</guid>
      <author>lea@lea.lgbt (Lea Rosema)</author>
      <pubDate>Sat, 15 Apr 2023 00:00:00 GMT</pubDate>
      <description><![CDATA[Hello world, this is my new blog, a place to write for me.]]></description>
      <category><![CDATA[post]]></category>
      <category><![CDATA[introduction]]></category>
      <category><![CDATA[eleventy]]></category>
      <content:encoded><![CDATA[<p>Hello world, this is my new blog, a place to write for me.</p>
<p>It is based on <a href="https://github.com/madrilene/eleventy-excellent">eleventy-excellent</a> by <a href="https://lenesaile.com/">Lene Saile</a>. I learned a lot of new things by studying her work. It's indeed excellent.</p>
<h2>Learnings</h2>
<ul>
<li>Kudos to <a href="https://andy-bell.co.uk/">Andy Bell</a> and his talk about <a href="https://buildexcellentwebsit.es/">building excellent websites that just work for everyone</a>.</li>
<li>You can organize your eleventy configurations by splitting them up in multiple config javascript files</li>
<li>CSS and JS as first-class citizens in Eleventy</li>
<li>All the SEO stuff, this is really great. It was also a bit overwhelming to me as I never really had in-depth touch points with SEO</li>
<li>Kudos also to the <a href="https://seo-cheat-sheet.9elements.com/">SEO Cheat sheet by 9elements</a>, it helped me a lot getting the basics.</li>
<li>Using pseudo random numbers in Eleventy where you provide a seed and always get the same sequence of random numbers.</li>
<li>Reset CSS by <a href="https://gist.github.com/EllyLoel/4ff8a6472247e6dd2315fd4038926522">Elly loel</a></li>
<li>lowering specificity with <code>:where</code></li>
<li>I learned about CSS Layers, making lowering specificity with <code>:where</code> not necessary anymore 😅</li>
<li>There's this new rust-based <a href="https://lightningcss.dev/">lightningcss</a> which can minify, autoprefix according to a browserslist :)</li>
</ul>
<h2>Things left to improve</h2>
<ol>
<li>
<p>The theme switcher is based on JavaScript, the fallback for now is to not provide theme options but respect the user's color scheme preferences. Also, this can cause a flash in the first contentful paint so a server-side solution might be better (there's an Eleventy Edge example I have to look into).</p>
</li>
<li>
<p>Automatic accessibility checks. There's a netlify plugin I didn't manage to get working yet.</p>
</li>
<li>
<p>I didn't get too comfy with Tailwind and using that as design tokens yet.</p>
</li>
</ol>

      ]]></content:encoded>
    </item>
  </channel>
</rss>
