<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="/feed.xml" rel="self" type="application/atom+xml" /><link href="/" rel="alternate" type="text/html" hreflang="en" /><updated>2026-10-16T23:12:39+00:00</updated><id>/feed.xml</id><title type="html">Martin Taylor</title><subtitle>Notes on software engineering, systems, and the craft of building things.</subtitle><author><name>Martin Taylor</name></author><entry><title type="html">A tour of the theme</title><link href="/2026/10/16/theme-tour/" rel="alternate" type="text/html" title="A tour of the theme" /><published>2026-10-16T00:00:00+00:00</published><updated>2026-10-16T00:00:00+00:00</updated><id>/2026/10/16/theme-tour</id><content type="html" xml:base="/2026/10/16/theme-tour/"><![CDATA[<p>This post exercises every part of the theme, so it doubles as a visual test page. Edit it, or delete it once you’ve seen how things look.</p>

<h2 id="backdrops">Backdrops</h2>

<p>The background is a WebGL shader with four scenes: <strong>aurora</strong>, <strong>topo</strong>, <strong>nebula</strong> and <strong>halftone</strong>. Set a site default with <code class="language-plaintext highlighter-rouge">theme_options.backdrop</code> in <code class="language-plaintext highlighter-rouge">_config.yml</code>, or pick one per post with <code class="language-plaintext highlighter-rouge">backdrop:</code> in the front matter (this post uses <code class="language-plaintext highlighter-rouge">nebula</code>). Visitors can choose their own, or turn it off, from the footer.</p>

<p>All scenes react to scrolling and the pointer. Click or tap anywhere to send a ripple through the field. With reduced motion turned on, the shader draws a single still frame.</p>

<h2 id="callouts">Callouts</h2>

<p>Use GitHub-style alert syntax in a blockquote. They’re rendered at build time, so they work without JavaScript.</p>

<aside class="callout callout--note" role="note" aria-label="Note"><p class="callout__title"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><circle cx="12" cy="12" r="9"/><path d="M12 11v5M12 8h.01"/></svg><span>Note</span></p><p>Useful information that readers should know, even when skimming.</p></aside>

<aside class="callout callout--tip" role="note" aria-label="Tip"><p class="callout__title"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M9 18h6M10 21h4M12 3a6 6 0 0 0-3.5 10.9c.6.5 1 1.2 1 2.1h5c0-.9.4-1.6 1-2.1A6 6 0 0 0 12 3z"/></svg><span>Tip</span></p><p>Helpful advice for doing things better or more easily.</p></aside>

<aside class="callout callout--important" role="note" aria-label="Important"><p class="callout__title"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/><path d="M12 7v4M12 14h.01"/></svg><span>Important</span></p><p>Key information readers need to succeed.</p></aside>

<aside class="callout callout--warning" role="note" aria-label="Warning"><p class="callout__title"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0z"/><path d="M12 9v4M12 17h.01"/></svg><span>Mind the cache</span></p><p>Urgent info that needs attention to avoid problems. The text after the marker becomes a custom title.</p></aside>

<aside class="callout callout--caution" role="note" aria-label="Caution"><p class="callout__title"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M7.9 2h8.2L22 7.9v8.2L16.1 22H7.9L2 16.1V7.9z"/><path d="M12 8v4M12 16h.01"/></svg><span>Caution</span></p><p>Advice about the risks or negative outcomes of certain actions.</p></aside>

<h2 id="badges">Badges</h2>

<p>Posts get badges automatically: <span class="badge badge--new badge--pulse">New</span> for recent posts, <span class="badge badge--accent">Featured</span>, and <span class="badge badge--info">Updated</span> when the front matter has an <code class="language-plaintext highlighter-rouge">updated:</code> date. Add your own with a <code class="language-plaintext highlighter-rouge">badges:</code> list.</p>

<p>You can also use them inline in Markdown: <span class="badge badge--success">stable</span> <span class="badge badge--warning">beta</span> <span class="badge badge--danger">deprecated</span> <span class="badge badge--neutral">v2.1</span> <span class="badge badge--solid">Pro</span></p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;span</span> <span class="na">class=</span><span class="s">"badge badge--success"</span><span class="nt">&gt;</span>stable<span class="nt">&lt;/span&gt;</span>
</code></pre></div></div>

<h2 id="banners-and-notifications">Banners and notifications</h2>

<ul>
  <li><strong>Post banners</strong> sit above the post body. Set <code class="language-plaintext highlighter-rouge">banner:</code> in the front matter, like the one at the top of this post.</li>
  <li><strong>Older posts</strong> get a notice automatically once they pass <code class="language-plaintext highlighter-rouge">theme_options.outdated_after_days</code>. Set <code class="language-plaintext highlighter-rouge">outdated: false</code> on a post to skip it.</li>
  <li><strong>Announcements</strong> live in <code class="language-plaintext highlighter-rouge">_data/announcements.yml</code>. Each one shows either as a bar above the header or as a toast. Visitors can dismiss them, and they can have start and end dates.</li>
  <li><strong>Toasts</strong> are also available from JavaScript: <code class="language-plaintext highlighter-rouge">toast({ title, message, variant, action })</code>. The button below tries it.</li>
</ul>

<p><button class="button button--ghost" type="button" onclick="window.toast &amp;&amp; window.toast({ title: 'Hello from a toast', message: 'Toasts pause while you hover over them.', variant: 'success', action: { label: 'Nice' } })">Show a toast</button></p>

<h2 id="code">Code</h2>

<p>Fenced code blocks get syntax highlighting from Rouge, plus a language label and a copy button.</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">type</span> <span class="nx">Result</span><span class="o">&lt;</span><span class="nx">T</span><span class="p">,</span> <span class="nx">E</span> <span class="o">=</span> <span class="nb">Error</span><span class="o">&gt;</span> <span class="o">=</span> <span class="p">{</span> <span class="na">ok</span><span class="p">:</span> <span class="kc">true</span><span class="p">;</span> <span class="nl">value</span><span class="p">:</span> <span class="nx">T</span> <span class="p">}</span> <span class="o">|</span> <span class="p">{</span> <span class="na">ok</span><span class="p">:</span> <span class="kc">false</span><span class="p">;</span> <span class="nl">error</span><span class="p">:</span> <span class="nx">E</span> <span class="p">};</span>

<span class="k">export</span> <span class="k">async</span> <span class="kd">function</span> <span class="nf">fetchJson</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="p">(</span><span class="nx">url</span><span class="p">:</span> <span class="kr">string</span><span class="p">):</span> <span class="nb">Promise</span><span class="o">&lt;</span><span class="nx">Result</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;&gt;</span> <span class="p">{</span>
  <span class="k">try</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">res</span> <span class="o">=</span> <span class="k">await</span> <span class="nf">fetch</span><span class="p">(</span><span class="nx">url</span><span class="p">,</span> <span class="p">{</span> <span class="na">headers</span><span class="p">:</span> <span class="p">{</span> <span class="na">Accept</span><span class="p">:</span> <span class="dl">"</span><span class="s2">application/json</span><span class="dl">"</span> <span class="p">}</span> <span class="p">});</span>
    <span class="k">if </span><span class="p">(</span><span class="o">!</span><span class="nx">res</span><span class="p">.</span><span class="nx">ok</span><span class="p">)</span> <span class="k">throw</span> <span class="k">new</span> <span class="nc">Error</span><span class="p">(</span><span class="s2">`HTTP </span><span class="p">${</span><span class="nx">res</span><span class="p">.</span><span class="nx">status</span><span class="p">}</span><span class="s2">`</span><span class="p">);</span>
    <span class="k">return</span> <span class="p">{</span> <span class="na">ok</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span> <span class="na">value</span><span class="p">:</span> <span class="p">(</span><span class="k">await</span> <span class="nx">res</span><span class="p">.</span><span class="nf">json</span><span class="p">())</span> <span class="kd">as </span><span class="nx">T</span> <span class="p">};</span>
  <span class="p">}</span> <span class="k">catch </span><span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="p">{</span> <span class="na">ok</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span> <span class="na">error</span><span class="p">:</span> <span class="nx">error</span> <span class="kd">as </span><span class="nb">Error</span> <span class="p">};</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">use</span> <span class="nn">std</span><span class="p">::</span><span class="nn">collections</span><span class="p">::</span><span class="n">HashMap</span><span class="p">;</span>

<span class="k">fn</span> <span class="nf">word_counts</span><span class="p">(</span><span class="n">text</span><span class="p">:</span> <span class="o">&amp;</span><span class="nb">str</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="n">HashMap</span><span class="o">&lt;&amp;</span><span class="nb">str</span><span class="p">,</span> <span class="nb">usize</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="k">let</span> <span class="k">mut</span> <span class="n">counts</span> <span class="o">=</span> <span class="nn">HashMap</span><span class="p">::</span><span class="nf">new</span><span class="p">();</span>
    <span class="k">for</span> <span class="n">word</span> <span class="k">in</span> <span class="n">text</span><span class="nf">.split_whitespace</span><span class="p">()</span> <span class="p">{</span>
        <span class="o">*</span><span class="n">counts</span><span class="nf">.entry</span><span class="p">(</span><span class="n">word</span><span class="p">)</span><span class="nf">.or_insert</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="n">counts</span>
<span class="p">}</span>
</code></pre></div></div>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="n">worker</span><span class="p">(</span><span class="n">ctx</span> <span class="n">context</span><span class="o">.</span><span class="n">Context</span><span class="p">,</span> <span class="n">jobs</span> <span class="o">&lt;-</span><span class="k">chan</span> <span class="n">Job</span><span class="p">,</span> <span class="n">results</span> <span class="k">chan</span><span class="o">&lt;-</span> <span class="n">Result</span><span class="p">)</span> <span class="p">{</span>
	<span class="k">for</span> <span class="p">{</span>
		<span class="k">select</span> <span class="p">{</span>
		<span class="k">case</span> <span class="o">&lt;-</span><span class="n">ctx</span><span class="o">.</span><span class="n">Done</span><span class="p">()</span><span class="o">:</span>
			<span class="k">return</span>
		<span class="k">case</span> <span class="n">job</span> <span class="o">:=</span> <span class="o">&lt;-</span><span class="n">jobs</span><span class="o">:</span>
			<span class="n">results</span> <span class="o">&lt;-</span> <span class="n">job</span><span class="o">.</span><span class="n">Run</span><span class="p">()</span>
		<span class="p">}</span>
	<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Deploy a preview build</span>
git switch <span class="nt">-c</span> my-feature
git commit <span class="nt">-am</span> <span class="s2">"Try something"</span> <span class="o">&amp;&amp;</span> git push <span class="nt">-u</span> origin my-feature
</code></pre></div></div>

<div class="language-diff highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gd">- const timeout = 1000;
</span><span class="gi">+ const timeout = 5_000; // allow slow cold starts
</span></code></pre></div></div>

<p>Inline code like <code class="language-plaintext highlighter-rouge">bundle exec jekyll serve</code> works too.</p>

<h2 id="diagrams">Diagrams</h2>

<p>Write Mermaid in a <code class="language-plaintext highlighter-rouge">mermaid</code> fenced block. Add <code class="language-plaintext highlighter-rouge">accTitle</code> and <code class="language-plaintext highlighter-rouge">accDescr</code> so screen readers get a description of the diagram.</p>

<pre><code class="language-mermaid">flowchart LR
  accTitle: Deployment pipeline
  accDescr: A git push triggers a Vercel build. Main deploys to production; other branches get a preview URL.
  push([git push]) --&gt; build[Vercel build]
  build --&gt; branch{Branch?}
  branch -- main --&gt; prod[Production]
  branch -- other --&gt; preview[Preview URL]
</code></pre>

<pre><code class="language-mermaid">sequenceDiagram
  accTitle: Request lifecycle
  accDescr: A browser requests a page from the Vercel edge, which serves the static file from its cache.
  participant B as Browser
  participant E as Vercel Edge
  participant S as Static files
  B-&gt;&gt;E: GET /2026/10/16/theme-tour/
  E-&gt;&gt;S: Cache miss
  S--&gt;&gt;E: index.html
  E--&gt;&gt;B: 200 OK
</code></pre>

<h2 id="gallery">Gallery</h2>

<p>Swiper powers galleries. Swipe, drag, or use the arrow keys when the gallery has focus.</p>

<figure class="gallery" data-carousel="">
  <div class="swiper" data-swiper="gallery" aria-label="Demo gallery">
    <div class="swiper-wrapper">
      <div class="swiper-slide"><img src="/assets/images/demo/grid.svg" alt="A glowing circle centred on a dark indigo grid." loading="lazy" decoding="async" width="1600" height="900" /></div>
      <div class="swiper-slide"><img src="/assets/images/demo/waves.svg" alt="Three violet sine waves flowing across a pale lavender background." loading="lazy" decoding="async" width="1600" height="900" /></div>
      <div class="swiper-slide"><img src="/assets/images/demo/nodes.svg" alt="Five gold nodes joined by pink edges on a deep purple background." loading="lazy" decoding="async" width="1600" height="900" /></div>
    </div>
  </div><div class="carousel-controls">
  <button class="carousel-btn" type="button" data-carousel-prev="" aria-label="Previous slide">
    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M15 18l-6-6 6-6" /></svg>
  </button>
  <div class="swiper-pagination" data-carousel-pagination=""></div>
  <button class="carousel-btn" type="button" data-carousel-next="" aria-label="Next slide">
    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M9 18l6-6-6-6" /></svg>
  </button>
</div>

  <figcaption>Abstract placeholder art. Swap in your own images.</figcaption>
</figure>

<h2 id="everything-else">Everything else</h2>

<blockquote>
  <p>Simplicity is prerequisite for reliability.
— Edsger W. Dijkstra</p>
</blockquote>

<table>
  <thead>
    <tr>
      <th>Layer</th>
      <th>Tool</th>
      <th>Notes</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Content</td>
      <td>Markdown</td>
      <td>Lives in <code class="language-plaintext highlighter-rouge">_posts/</code></td>
    </tr>
    <tr>
      <td>Build</td>
      <td>Jekyll 4</td>
      <td>Runs on Vercel</td>
    </tr>
    <tr>
      <td>Effects</td>
      <td>WebGL, GSAP</td>
      <td>Turned off for reduced motion</td>
    </tr>
    <tr>
      <td>Carousels</td>
      <td>Swiper</td>
      <td>Loaded only where it’s used</td>
    </tr>
  </tbody>
</table>

<ol>
  <li>Ordered lists</li>
  <li>With <strong>bold</strong>, <em>italic</em>, and <a href="https://jekyllrb.com">links</a></li>
  <li>And a footnote.<sup id="fnref:1"><a href="#fn:1" class="footnote" rel="footnote" role="doc-noteref">1</a></sup></li>
</ol>

<ul>
  <li>Unordered lists</li>
  <li>Nest nicely
    <ul>
      <li>Like this</li>
    </ul>
  </li>
</ul>

<hr />

<p>That’s the tour.</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1">
      <p>Footnotes collect at the bottom of the post. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩︎</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Martin Taylor</name></author><category term="meta" /><category term="jekyll" /><category term="design" /><summary type="html"><![CDATA[Syntax highlighting, Mermaid diagrams, Swiper galleries, and everything else this blog can render.]]></summary></entry><entry><title type="html">Hello, world</title><link href="/2026/10/15/hello-world/" rel="alternate" type="text/html" title="Hello, world" /><published>2026-10-15T00:00:00+00:00</published><updated>2026-10-15T00:00:00+00:00</updated><id>/2026/10/15/hello-world</id><content type="html" xml:base="/2026/10/15/hello-world/"><![CDATA[<p>The blog is live. It’s built with Jekyll and deploys to Vercel on every push to <code class="language-plaintext highlighter-rouge">main</code>. Branches get their own preview URLs.</p>

<p>New posts go in <code class="language-plaintext highlighter-rouge">_posts/</code> as <code class="language-plaintext highlighter-rouge">YYYY-MM-DD-title.md</code>. Front matter can include <code class="language-plaintext highlighter-rouge">description</code>, <code class="language-plaintext highlighter-rouge">tags</code>, and <code class="language-plaintext highlighter-rouge">featured: true</code> to show the post in the carousel on the home page.</p>]]></content><author><name>Martin Taylor</name></author><category term="meta" /><summary type="html"><![CDATA[First post. The blog is live on Jekyll and Vercel.]]></summary></entry></feed>