<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://craigary.net</id>
    <title>Craig Hart</title>
    <updated>2026-11-08T12:27:58.121Z</updated>
    <generator>https://github.com/jpmonette/feed</generator>
    <author>
        <name>Craig Hart</name>
        <uri>https://craigary.net/</uri>
    </author>
    <link rel="alternate" href="https://craigary.net/"/>
    <link rel="self" href="https://craigary.net/atom.xml"/>
    <subtitle>Just a human sitting in front of glowing glass.</subtitle>
    <logo>https://craigary.net/icon.svg</logo>
    <icon>https://craigary.net/favicon.ico</icon>
    <rights>All rights reserved 2026, Craig Hart</rights>
    <entry>
        <title type="html"><![CDATA[Modern Web Development in 2026]]></title>
        <id>https://craigary.net/writings/modern-web-development-2026</id>
        <link href="https://craigary.net/writings/modern-web-development-2026"/>
        <updated>2026-08-28T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Reflections on the state of full-stack TypeScript, React Server Components, and native tooling.]]></summary>
        <content type="html"><![CDATA[<div class="comark-content article-body"><p>The web landscape in 2026 has settled into a fascinating balance. After years of rapid framework churn, the focus has shifted towards native speed, fine-grained reactivity, and seamless full-stack primitives.</p><h2 id="the-convergence-of-tooling">The Convergence of Tooling</h2><p>Modern build chains like <a href="https://vite.dev" rel="noreferrer" target="_blank" class="inline not-prose px-2 py-1 rounded-full text-[0.8em] font-mono bg-neutral-200/50 text-neutral-800 dark:bg-neutral-800/80 dark:text-neutral-100 mx-0.5 !no-underline !border-none selection:bg-transparent selection:text-inherit box-decoration-clone align-baseline hover:bg-neutral-200/80 hover:text-neutral-900 dark:hover:bg-neutral-700 dark:hover:text-white cursor-pointer active:scale-[0.96] transition-[background-color,color,transform] duration-150 ease-out"><span class="leading-none">Vite</span></a> and unified toolchains have largely eliminated cold-start bottlenecks. Bundlers now operate almost imperceptibly fast thanks to Rust and native toolchains.</p><h3 id="the-convergence-of-tooling-what-changed">What Changed?</h3><ol><li><strong>Native Speed by Default</strong>: Everything from linters (Oxlint) to formatters and bundlers runs on compiled languages.</li><li><strong>Server Components Maturity</strong>: React Server Components (RSC) have transitioned from experimental novelty to standard architecture.</li><li><strong>Edge Deployment</strong>: Edge-native runtimes enable sub-millisecond cold starts across the globe.</li></ol><pre language="typescript" class="shiki shiki-themes night-owl-light vitesse-dark dark:vitesse-dark" style="background-color:#FBFBFB;color:#403f53;--shiki-dark-bg:#121212;--shiki-dark:#dbd7caee"><code class="language-typescript"><span class="line" style="display:inline"><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#4D9375;--shiki-dark-font-style:inherit">export</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#CB7676;--shiki-dark-font-style:inherit"> async</span><span style="color:#994CC3;--shiki-dark:#CB7676"> function</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit"> loadArticle</span><span style="color:#111111;--shiki-dark:#666666">(</span><span style="color:#403F53;--shiki-dark:#BD976A">slug</span><span style="color:#0C969B;--shiki-dark:#666666">:</span><span style="color:#4876D6;--shiki-dark:#5DA994"> string</span><span style="color:#111111;--shiki-dark:#666666">)</span><span style="color:#0C969B;--shiki-dark:#666666">:</span><span style="color:#111111;--shiki-dark:#5DA994"> Promise</span><span style="color:#403F53;--shiki-dark:#666666">&lt;</span><span style="color:#111111;--shiki-dark:#5DA994">Article</span><span style="color:#403F53;--shiki-dark:#666666">&gt;</span><span style="color:#403F53;--shiki-dark:#666666"> {</span></span>
<span class="line" style="display:inline"><span style="color:#994CC3;--shiki-dark:#CB7676">  const</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#BD976A;--shiki-dark-font-style:inherit"> content</span><span style="color:#994CC3;--shiki-dark:#666666"> =</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#4D9375;--shiki-dark-font-style:inherit"> await</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit"> fetchFromCache</span><span style="color:#403F53;--shiki-dark:#666666">(</span><span style="color:#403F53;--shiki-dark:#C98A7D77">`</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#C98A7D;--shiki-dark-font-style:inherit">article:</span><span style="color:#D3423E;--shiki-dark:#4D9375">${</span><span style="color:#403F53;--shiki-light-font-style:italic;--shiki-dark:#C98A7D;--shiki-dark-font-style:inherit">slug</span><span style="color:#D3423E;--shiki-dark:#4D9375">}</span><span style="color:#403F53;--shiki-dark:#C98A7D77">`</span><span style="color:#403F53;--shiki-dark:#666666">)</span></span>
<span class="line" style="display:inline"><span style="color:#994CC3;--shiki-dark:#4D9375">  if</span><span style="color:#403F53;--shiki-dark:#666666"> (</span><span style="color:#994CC3;--shiki-dark:#CB7676">!</span><span style="color:#403F53;--shiki-dark:#BD976A">content</span><span style="color:#403F53;--shiki-dark:#666666">)</span><span style="color:#403F53;--shiki-dark:#666666"> {</span></span>
<span class="line" style="display:inline"><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#4D9375;--shiki-dark-font-style:inherit">    throw</span><span style="color:#0C969B;--shiki-dark:#CB7676"> new</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit"> Error</span><span style="color:#403F53;--shiki-dark:#666666">(</span><span style="color:#111111;--shiki-dark:#C98A7D77">&#x27;</span><span style="color:#C96765;--shiki-dark:#C98A7D">Not found</span><span style="color:#111111;--shiki-dark:#C98A7D77">&#x27;</span><span style="color:#403F53;--shiki-dark:#666666">)</span></span>
<span class="line" style="display:inline"><span style="color:#403F53;--shiki-dark:#666666">  }</span></span>
<span class="line" style="display:inline"><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#4D9375;--shiki-dark-font-style:inherit">  return</span><span style="color:#403F53;--shiki-dark:#BD976A"> content</span></span>
<span class="line" style="display:inline"><span style="color:#403F53;--shiki-dark:#666666">}</span></span></code></pre><blockquote>“Simplicity is prerequisite for reliability.” — Edsger W. Dijkstra</blockquote><p>The future belongs to tooling that gets out of the developer’s way while maintaining rock-solid type safety.</p></div>]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[GitHub: Craigary Project]]></title>
        <id>https://github.com/craigary/craigary</id>
        <link href="https://github.com/craigary/craigary"/>
        <updated>2026-08-01T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[External link redirecting to the official project repository.]]></summary>
        <content type="html"><![CDATA[<div class="comark-content article-body"><p>Redirecting to the official GitHub repository…</p><p>If you are not automatically redirected, please <a href="https://github.com/craigary/craigary" rel="noreferrer" target="_blank">click here</a>.</p></div>]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[Deep Dive into TanStack Start & React Server Components]]></title>
        <id>https://craigary.net/writings/deep-dive-tanstack-start-rsc</id>
        <link href="https://craigary.net/writings/deep-dive-tanstack-start-rsc"/>
        <updated>2026-07-14T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[How TanStack Start leverages RSC for optimal streaming performance and developer ergonomics.]]></summary>
        <content type="html"><![CDATA[<div class="comark-content article-body"><p>TanStack Start represents a fresh take on full-stack React frameworks, built on top of the battle-tested TanStack Router.</p><h2 id="why-tanstack-start">Why TanStack Start?</h2><p>TanStack Start decouples routing and data fetching concerns, offering first-class SSR, streaming, and server actions.</p><h3 id="why-tanstack-start-core-architecture">Core Architecture</h3><ul><li><strong>Full-stack Type Safety</strong>: Route parameters, query parameters, and loader data are strictly typed end-to-end.</li><li><strong>RSC Integration</strong>: Server components can stream HTML directly to the client without client-side bundle overhead.</li></ul><pre language="tsx" class="shiki shiki-themes night-owl-light vitesse-dark dark:vitesse-dark" style="background-color:#FBFBFB;color:#403f53;--shiki-dark-bg:#121212;--shiki-dark:#dbd7caee"><code class="language-tsx"><span class="line" style="display:inline"><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#4D9375;--shiki-dark-font-style:inherit">import</span><span style="color:#403F53;--shiki-dark:#666666"> {</span><span style="color:#403F53;--shiki-dark:#BD976A"> createFileRoute</span><span style="color:#403F53;--shiki-dark:#666666"> }</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#4D9375;--shiki-dark-font-style:inherit"> from</span><span style="color:#111111;--shiki-dark:#C98A7D77"> &#x27;</span><span style="color:#C96765;--shiki-dark:#C98A7D">@tanstack/react-router</span><span style="color:#111111;--shiki-dark:#C98A7D77">&#x27;</span></span>
<span class="line" style="display:inline"><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#4D9375;--shiki-dark-font-style:inherit">import</span><span style="color:#403F53;--shiki-dark:#666666"> {</span><span style="color:#403F53;--shiki-dark:#BD976A"> createServerFn</span><span style="color:#403F53;--shiki-dark:#666666"> }</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#4D9375;--shiki-dark-font-style:inherit"> from</span><span style="color:#111111;--shiki-dark:#C98A7D77"> &#x27;</span><span style="color:#C96765;--shiki-dark:#C98A7D">@tanstack/react-start</span><span style="color:#111111;--shiki-dark:#C98A7D77">&#x27;</span></span>
<span class="line" style="display:inline"></span>
<span class="line" style="display:inline"><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#4D9375;--shiki-dark-font-style:inherit">export</span><span style="color:#994CC3;--shiki-dark:#CB7676"> const</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#BD976A;--shiki-dark-font-style:inherit"> getStats</span><span style="color:#994CC3;--shiki-dark:#666666"> =</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit"> createServerFn</span><span style="color:#403F53;--shiki-dark:#666666">(</span><span style="color:#994CC3;--shiki-dark:#666666">{</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#B8A965;--shiki-dark-font-style:inherit"> method</span><span style="color:#994CC3;--shiki-dark:#666666">:</span><span style="color:#111111;--shiki-dark:#C98A7D77"> &#x27;</span><span style="color:#C96765;--shiki-dark:#C98A7D">GET</span><span style="color:#111111;--shiki-dark:#C98A7D77">&#x27;</span><span style="color:#994CC3;--shiki-dark:#666666"> }</span><span style="color:#403F53;--shiki-dark:#666666">)</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#666666;--shiki-dark-font-style:inherit">.</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit">handler</span><span style="color:#403F53;--shiki-dark:#666666">(</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#CB7676;--shiki-dark-font-style:inherit">async</span><span style="color:#111111;--shiki-dark:#666666"> ()</span><span style="color:#994CC3;--shiki-dark:#666666"> =&gt;</span><span style="color:#994CC3;--shiki-dark:#666666"> {</span></span>
<span class="line" style="display:inline"><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#4D9375;--shiki-dark-font-style:inherit">  return</span><span style="color:#994CC3;--shiki-dark:#666666"> {</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#B8A965;--shiki-dark-font-style:inherit"> views</span><span style="color:#994CC3;--shiki-dark:#666666">:</span><span style="color:#AA0982;--shiki-dark:#4C9A91"> 42000</span><span style="color:#994CC3;--shiki-dark:#666666">,</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#B8A965;--shiki-dark-font-style:inherit"> stars</span><span style="color:#994CC3;--shiki-dark:#666666">:</span><span style="color:#AA0982;--shiki-dark:#4C9A91"> 1337</span><span style="color:#994CC3;--shiki-dark:#666666"> }</span></span>
<span class="line" style="display:inline"><span style="color:#994CC3;--shiki-dark:#666666">}</span><span style="color:#403F53;--shiki-dark:#666666">)</span></span>
<span class="line" style="display:inline"></span>
<span class="line" style="display:inline"><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#4D9375;--shiki-dark-font-style:inherit">export</span><span style="color:#994CC3;--shiki-dark:#CB7676"> const</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#BD976A;--shiki-dark-font-style:inherit"> Route</span><span style="color:#994CC3;--shiki-dark:#666666"> =</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit"> createFileRoute</span><span style="color:#403F53;--shiki-dark:#666666">(</span><span style="color:#111111;--shiki-dark:#C98A7D77">&#x27;</span><span style="color:#C96765;--shiki-dark:#C98A7D">/stats</span><span style="color:#111111;--shiki-dark:#C98A7D77">&#x27;</span><span style="color:#403F53;--shiki-dark:#666666">)(</span><span style="color:#994CC3;--shiki-dark:#666666">{</span></span>
<span class="line" style="display:inline"><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit">  loader</span><span style="color:#994CC3;--shiki-dark:#666666">:</span><span style="color:#111111;--shiki-dark:#666666"> ()</span><span style="color:#994CC3;--shiki-dark:#666666"> =&gt;</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit"> getStats</span><span style="color:#403F53;--shiki-dark:#666666">()</span><span style="color:#994CC3;--shiki-dark:#666666">,</span></span>
<span class="line" style="display:inline"><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#B8A965;--shiki-dark-font-style:inherit">  component</span><span style="color:#994CC3;--shiki-dark:#666666">:</span><span style="color:#403F53;--shiki-light-font-style:italic;--shiki-dark:#BD976A;--shiki-dark-font-style:inherit"> StatsPage</span><span style="color:#994CC3;--shiki-dark:#666666">,</span></span>
<span class="line" style="display:inline"><span style="color:#994CC3;--shiki-dark:#666666">}</span><span style="color:#403F53;--shiki-dark:#666666">)</span></span></code></pre><p>With granular control over server/client boundaries, apps remain snappy regardless of dataset size.</p></div>]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[Building Lightning Fast Apps with Vite+]]></title>
        <id>https://craigary.net/writings/building-with-vite-plus</id>
        <link href="https://craigary.net/writings/building-with-vite-plus"/>
        <updated>2026-06-02T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Exploring Vite+, the unified toolchain combining Vite, Rolldown, Vitest, and Oxlint.]]></summary>
        <content type="html"><![CDATA[<div class="comark-content article-body"><p>Toolchain fragmentation has long been a pain point in JavaScript development. Vite+ solves this by packaging the entire ecosystem into a coherent experience.</p><h2 id="the-vite-philosophy">The Vite+ Philosophy</h2><p>Instead of maintaining dozens of config files for eslint, prettier, tsconfig, vitest, and bundlers, Vite+ unifies them under <code>vp</code>.</p><table><thead><tr><th style="text-align:left">Tool</th><th style="text-align:left">Previous Approach</th><th style="text-align:left">Vite+ Integration</th></tr></thead><tbody><tr><td style="text-align:left">Bundler</td><td style="text-align:left">Rollup / esbuild</td><td style="text-align:left">Rolldown</td></tr><tr><td style="text-align:left">Linter</td><td style="text-align:left">ESLint</td><td style="text-align:left">Oxlint</td></tr><tr><td style="text-align:left">Formatter</td><td style="text-align:left">Prettier</td><td style="text-align:left">Oxfmt</td></tr><tr><td style="text-align:left">Test Runner</td><td style="text-align:left">Jest / Vitest</td><td style="text-align:left">Vitest Built-in</td></tr></tbody></table><pre language="bash" class="shiki shiki-themes night-owl-light vitesse-dark dark:vitesse-dark" style="background-color:#FBFBFB;color:#403f53;--shiki-dark-bg:#121212;--shiki-dark:#dbd7caee"><code class="language-bash"><span class="line" style="display:inline"><span style="color:#989FB1;--shiki-light-font-style:italic;--shiki-dark:#758575DD;--shiki-dark-font-style:inherit"># Lint, format, and typecheck in one go</span></span>
<span class="line" style="display:inline"><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit">vp</span><span style="color:#4876D6;--shiki-dark:#C98A7D"> check</span></span>
<span class="line" style="display:inline"></span>
<span class="line" style="display:inline"><span style="color:#989FB1;--shiki-light-font-style:italic;--shiki-dark:#758575DD;--shiki-dark-font-style:inherit"># Run your test suite with zero configuration</span></span>
<span class="line" style="display:inline"><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit">vp</span><span style="color:#4876D6;--shiki-dark:#C98A7D"> test</span></span></code></pre><p>The performance gains are especially obvious on medium to large codebases, reducing CI times by up to 70%.</p></div>]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[Migrating Large Codebases to Tailwind CSS v4]]></title>
        <id>https://craigary.net/writings/migrating-to-tailwind-v4</id>
        <link href="https://craigary.net/writings/migrating-to-tailwind-v4"/>
        <updated>2026-04-18T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Hands-on experience and gotchas when upgrading complex design systems to Tailwind CSS v4.]]></summary>
        <content type="html"><![CDATA[<div class="comark-content article-body"><p>Tailwind CSS v4 is a ground-up rewrite that redefines how utility-first CSS is authored and compiled.</p><h2 id="what-is-new-in-v4">What is New in v4?</h2><p>Gone is the JavaScript-based <code>tailwind.config.js</code>. Everything is now configured natively within CSS:</p><pre language="css" class="shiki shiki-themes night-owl-light vitesse-dark dark:vitesse-dark" style="background-color:#FBFBFB;color:#403f53;--shiki-dark-bg:#121212;--shiki-dark:#dbd7caee"><code class="language-css"><span class="line" style="display:inline"><span style="color:#994CC3;--shiki-dark:#666666">@</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#4D9375;--shiki-dark-font-style:inherit">import</span><span style="color:#111111;--shiki-dark:#C98A7D77"> &#x27;</span><span style="color:#C96765;--shiki-dark:#C98A7D">tailwindcss</span><span style="color:#111111;--shiki-dark:#C98A7D77">&#x27;</span><span style="color:#403F53;--shiki-dark:#666666">;</span></span>
<span class="line" style="display:inline"></span>
<span class="line" style="display:inline"><span style="color:#994CC3;--shiki-dark:#666666">@</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#4D9375;--shiki-dark-font-style:inherit">theme</span><span style="color:#403F53;--shiki-dark:#666666"> {</span></span>
<span class="line" style="display:inline"><span style="color:#4876D6;--shiki-dark:#BD976A">  --font-sans</span><span style="color:#403F53;--shiki-dark:#666666">:</span><span style="color:#111111;--shiki-dark:#C98A7D77"> &#x27;</span><span style="color:#C96765;--shiki-dark:#C98A7D">Inter</span><span style="color:#111111;--shiki-dark:#C98A7D77">&#x27;</span><span style="color:#403F53;--shiki-dark:#666666">,</span><span style="color:#0C969B;--shiki-dark:#C99076"> system-ui</span><span style="color:#403F53;--shiki-dark:#666666">,</span><span style="color:#0C969B;--shiki-dark:#C99076"> sans-serif</span><span style="color:#403F53;--shiki-dark:#666666">;</span></span>
<span class="line" style="display:inline"><span style="color:#4876D6;--shiki-dark:#BD976A">  --font-mono</span><span style="color:#403F53;--shiki-dark:#666666">:</span><span style="color:#111111;--shiki-dark:#C98A7D77"> &#x27;</span><span style="color:#C96765;--shiki-dark:#C98A7D">JetBrains Mono</span><span style="color:#111111;--shiki-dark:#C98A7D77">&#x27;</span><span style="color:#403F53;--shiki-dark:#666666">,</span><span style="color:#0C969B;--shiki-dark:#C99076"> monospace</span><span style="color:#403F53;--shiki-dark:#666666">;</span></span>
<span class="line" style="display:inline"><span style="color:#4876D6;--shiki-dark:#BD976A">  --color-brand-primary</span><span style="color:#403F53;--shiki-dark:#666666">:</span><span style="color:#AA0982;--shiki-dark:#666666"> #</span><span style="color:#AA0982;--shiki-dark:#C99076">3b82f6</span><span style="color:#403F53;--shiki-dark:#666666">;</span></span>
<span class="line" style="display:inline"><span style="color:#403F53;--shiki-dark:#666666">}</span></span>
<span class="line" style="display:inline"></span>
<span class="line" style="display:inline"><span style="color:#994CC3;--shiki-dark:#666666">@</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#4D9375;--shiki-dark-font-style:inherit">utility</span><span style="color:#403F53;--shiki-dark:#DBD7CAEE"> custom-badge </span><span style="color:#403F53;--shiki-dark:#666666">{</span></span>
<span class="line" style="display:inline"><span style="color:#994CC3;--shiki-dark:#666666">  @</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#4D9375;--shiki-dark-font-style:inherit">apply</span><span style="color:#403F53;--shiki-dark:#DBD7CAEE"> inline-flex items-center px-2 py-0.5 rounded text-xs font-medium</span><span style="color:#403F53;--shiki-dark:#666666">;</span></span>
<span class="line" style="display:inline"><span style="color:#403F53;--shiki-dark:#666666">}</span></span></code></pre><h3 id="what-is-new-in-v4-key-benefits">Key Benefits</h3><ul><li><strong>Zero JS config</strong>: Native CSS <code>@theme</code> directives make token sharing intuitive.</li><li><strong>Lightning Compilation</strong>: The Oxide engine builds stylesheet variations in single-digit milliseconds.</li><li><strong>Container Queries Built-in</strong>: Full support for container queries without separate plugins.</li></ul></div>]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[Shiki Syntax Highlighting at Scale]]></title>
        <id>https://craigary.net/writings/shiki-syntax-highlighting-at-scale</id>
        <link href="https://craigary.net/writings/shiki-syntax-highlighting-at-scale"/>
        <updated>2026-03-05T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[How to achieve ultra-fast server-side code highlighting with dual theme support and zero client JS.]]></summary>
        <content type="html"><![CDATA[<div class="comark-content article-body"><p><a href="https://shiki.style" rel="noreferrer" target="_blank" class="inline not-prose px-2 py-1 rounded-full text-[0.8em] font-mono bg-neutral-200/50 text-neutral-800 dark:bg-neutral-800/80 dark:text-neutral-100 mx-0.5 !no-underline !border-none selection:bg-transparent selection:text-inherit box-decoration-clone align-baseline hover:bg-neutral-200/80 hover:text-neutral-900 dark:hover:bg-neutral-700 dark:hover:text-white cursor-pointer active:scale-[0.96] transition-[background-color,color,transform] duration-150 ease-out"><span class="leading-none">Shiki</span></a> has become the gold standard for syntax highlighting on the modern web, powered by TextMate grammars.</p><h2 id="dual-themes-without-runtime-overhead">Dual Themes Without Runtime Overhead</h2><p>By rendering both light and dark theme colors as CSS variables, we can toggle themes instantly without flashing or hydration:</p><pre language="typescript" class="shiki shiki-themes night-owl-light vitesse-dark dark:vitesse-dark" style="background-color:#FBFBFB;color:#403f53;--shiki-dark-bg:#121212;--shiki-dark:#dbd7caee"><code class="language-typescript"><span class="line" style="display:inline"><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#4D9375;--shiki-dark-font-style:inherit">import</span><span style="color:#403F53;--shiki-dark:#BD976A"> rehypeShiki</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#4D9375;--shiki-dark-font-style:inherit"> from</span><span style="color:#111111;--shiki-dark:#C98A7D77"> &#x27;</span><span style="color:#C96765;--shiki-dark:#C98A7D">@shikijs/rehype</span><span style="color:#111111;--shiki-dark:#C98A7D77">&#x27;</span></span>
<span class="line" style="display:inline"></span>
<span class="line" style="display:inline"><span style="color:#994CC3;--shiki-dark:#CB7676">const</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#BD976A;--shiki-dark-font-style:inherit"> plugin</span><span style="color:#994CC3;--shiki-dark:#666666"> =</span><span style="color:#403F53;--shiki-dark:#666666"> [</span></span>
<span class="line" style="display:inline"><span style="color:#403F53;--shiki-light-font-style:italic;--shiki-dark:#BD976A;--shiki-dark-font-style:inherit">  rehypeShiki</span><span style="color:#403F53;--shiki-dark:#666666">,</span></span>
<span class="line" style="display:inline"><span style="color:#403F53;--shiki-dark:#666666">  {</span></span>
<span class="line" style="display:inline"><span style="color:#403F53;--shiki-light-font-style:italic;--shiki-dark:#B8A965;--shiki-dark-font-style:inherit">    themes</span><span style="color:#403F53;--shiki-dark:#666666">:</span><span style="color:#403F53;--shiki-dark:#666666"> {</span></span>
<span class="line" style="display:inline"><span style="color:#403F53;--shiki-light-font-style:italic;--shiki-dark:#B8A965;--shiki-dark-font-style:inherit">      light</span><span style="color:#403F53;--shiki-dark:#666666">:</span><span style="color:#111111;--shiki-dark:#C98A7D77"> &#x27;</span><span style="color:#C96765;--shiki-dark:#C98A7D">vitesse-light</span><span style="color:#111111;--shiki-dark:#C98A7D77">&#x27;</span><span style="color:#403F53;--shiki-dark:#666666">,</span></span>
<span class="line" style="display:inline"><span style="color:#403F53;--shiki-light-font-style:italic;--shiki-dark:#B8A965;--shiki-dark-font-style:inherit">      dark</span><span style="color:#403F53;--shiki-dark:#666666">:</span><span style="color:#111111;--shiki-dark:#C98A7D77"> &#x27;</span><span style="color:#C96765;--shiki-dark:#C98A7D">vitesse-dark</span><span style="color:#111111;--shiki-dark:#C98A7D77">&#x27;</span><span style="color:#403F53;--shiki-dark:#666666">,</span></span>
<span class="line" style="display:inline"><span style="color:#403F53;--shiki-dark:#666666">    },</span></span>
<span class="line" style="display:inline"><span style="color:#403F53;--shiki-light-font-style:italic;--shiki-dark:#B8A965;--shiki-dark-font-style:inherit">    defaultColor</span><span style="color:#403F53;--shiki-dark:#666666">:</span><span style="color:#BC5454;--shiki-light-font-style:italic;--shiki-dark:#4D9375;--shiki-dark-font-style:inherit"> false</span><span style="color:#403F53;--shiki-dark:#666666">,</span></span>
<span class="line" style="display:inline"><span style="color:#403F53;--shiki-dark:#666666">  },</span></span>
<span class="line" style="display:inline"><span style="color:#403F53;--shiki-dark:#666666">]</span></span></code></pre><pre language="css" class="shiki shiki-themes night-owl-light vitesse-dark dark:vitesse-dark" style="background-color:#FBFBFB;color:#403f53;--shiki-dark-bg:#121212;--shiki-dark:#dbd7caee"><code class="language-css"><span class="line" style="display:inline"><span style="color:#C96765;--shiki-dark:#4D9375">html</span><span style="color:#4876D6;--shiki-dark:#666666">.</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#BD976A;--shiki-dark-font-style:inherit">dark</span><span style="color:#4876D6;--shiki-dark:#666666"> .</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#BD976A;--shiki-dark-font-style:inherit">shiki</span><span style="color:#994CC3;--shiki-dark:#666666">,</span></span>
<span class="line" style="display:inline"><span style="color:#C96765;--shiki-dark:#4D9375">html</span><span style="color:#4876D6;--shiki-dark:#666666">.</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#BD976A;--shiki-dark-font-style:inherit">dark</span><span style="color:#4876D6;--shiki-dark:#666666"> .</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#BD976A;--shiki-dark-font-style:inherit">shiki</span><span style="color:#C96765;--shiki-dark:#4D9375"> span</span><span style="color:#403F53;--shiki-dark:#666666"> {</span></span>
<span class="line" style="display:inline"><span style="color:#0C969B;--shiki-dark:#B8A965">  color</span><span style="color:#403F53;--shiki-dark:#666666">:</span><span style="color:#4876D6;--shiki-dark:#B8A965"> var</span><span style="color:#403F53;--shiki-dark:#666666">(</span><span style="color:#4876D6;--shiki-dark:#BD976A">--shiki-dark</span><span style="color:#403F53;--shiki-dark:#666666">)</span><span style="color:#994CC3;--shiki-dark:#4D9375"> !important</span><span style="color:#403F53;--shiki-dark:#666666">;</span></span>
<span class="line" style="display:inline"><span style="color:#0C969B;--shiki-dark:#B8A965">  background-color</span><span style="color:#403F53;--shiki-dark:#666666">:</span><span style="color:#4876D6;--shiki-dark:#B8A965"> var</span><span style="color:#403F53;--shiki-dark:#666666">(</span><span style="color:#4876D6;--shiki-dark:#BD976A">--shiki-dark-bg</span><span style="color:#403F53;--shiki-dark:#666666">)</span><span style="color:#994CC3;--shiki-dark:#4D9375"> !important</span><span style="color:#403F53;--shiki-dark:#666666">;</span></span>
<span class="line" style="display:inline"><span style="color:#403F53;--shiki-dark:#666666">}</span></span></code></pre><p>This approach ensures zero layout shifts and immaculate aesthetics across all devices.</p></div>]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[The State of TypeScript in 2026]]></title>
        <id>https://craigary.net/writings/state-of-typescript-2026</id>
        <link href="https://craigary.net/writings/state-of-typescript-2026"/>
        <updated>2026-01-22T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[An overview of newer TypeScript features, type-level programming, and best practices.]]></summary>
        <content type="html"><![CDATA[<div class="comark-content article-body"><p>TypeScript continues to empower developers to write robust, maintainable codebases. Let us review some of the most impactful patterns that have matured over recent releases.</p><h2 id="the-power-of-the-satisfies-operator">The Power of the <code>satisfies</code> Operator</h2><p>The <code>satisfies</code> operator allows you to validate an expression matches a type without widening or erasing specific literals:</p><pre language="typescript" class="shiki shiki-themes night-owl-light vitesse-dark dark:vitesse-dark" style="background-color:#FBFBFB;color:#403f53;--shiki-dark-bg:#121212;--shiki-dark:#dbd7caee"><code class="language-typescript"><span class="line" style="display:inline"><span style="color:#994CC3;--shiki-dark:#CB7676">type</span><span style="color:#403F53;--shiki-dark:#5DA994"> Color</span><span style="color:#994CC3;--shiki-dark:#666666"> =</span><span style="color:#111111;--shiki-dark:#C98A7D77"> &#x27;</span><span style="color:#C96765;--shiki-dark:#C98A7D">red</span><span style="color:#111111;--shiki-dark:#C98A7D77">&#x27;</span><span style="color:#0C969B;--shiki-dark:#666666"> |</span><span style="color:#111111;--shiki-dark:#C98A7D77"> &#x27;</span><span style="color:#C96765;--shiki-dark:#C98A7D">green</span><span style="color:#111111;--shiki-dark:#C98A7D77">&#x27;</span><span style="color:#0C969B;--shiki-dark:#666666"> |</span><span style="color:#111111;--shiki-dark:#C98A7D77"> &#x27;</span><span style="color:#C96765;--shiki-dark:#C98A7D">blue</span><span style="color:#111111;--shiki-dark:#C98A7D77">&#x27;</span></span>
<span class="line" style="display:inline"><span style="color:#994CC3;--shiki-dark:#CB7676">type</span><span style="color:#403F53;--shiki-dark:#5DA994"> RGB</span><span style="color:#994CC3;--shiki-dark:#666666"> =</span><span style="color:#403F53;--shiki-dark:#666666"> [</span><span style="color:#403F53;--shiki-dark:#80A665">red</span><span style="color:#403F53;--shiki-dark:#666666">:</span><span style="color:#4876D6;--shiki-dark:#5DA994"> number</span><span style="color:#403F53;--shiki-dark:#666666">,</span><span style="color:#403F53;--shiki-dark:#80A665"> green</span><span style="color:#403F53;--shiki-dark:#666666">:</span><span style="color:#4876D6;--shiki-dark:#5DA994"> number</span><span style="color:#403F53;--shiki-dark:#666666">,</span><span style="color:#403F53;--shiki-dark:#80A665"> blue</span><span style="color:#403F53;--shiki-dark:#666666">:</span><span style="color:#4876D6;--shiki-dark:#5DA994"> number</span><span style="color:#403F53;--shiki-dark:#666666">]</span></span>
<span class="line" style="display:inline"></span>
<span class="line" style="display:inline"><span style="color:#994CC3;--shiki-dark:#CB7676">const</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#BD976A;--shiki-dark-font-style:inherit"> palette</span><span style="color:#994CC3;--shiki-dark:#666666"> =</span><span style="color:#994CC3;--shiki-dark:#666666"> {</span></span>
<span class="line" style="display:inline"><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#B8A965;--shiki-dark-font-style:inherit">  primary</span><span style="color:#994CC3;--shiki-dark:#666666">:</span><span style="color:#111111;--shiki-dark:#C98A7D77"> &#x27;</span><span style="color:#C96765;--shiki-dark:#C98A7D">red</span><span style="color:#111111;--shiki-dark:#C98A7D77">&#x27;</span><span style="color:#994CC3;--shiki-dark:#666666">,</span></span>
<span class="line" style="display:inline"><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#B8A965;--shiki-dark-font-style:inherit">  secondary</span><span style="color:#994CC3;--shiki-dark:#666666">:</span><span style="color:#403F53;--shiki-dark:#666666"> [</span><span style="color:#AA0982;--shiki-dark:#4C9A91">0</span><span style="color:#403F53;--shiki-dark:#666666">,</span><span style="color:#AA0982;--shiki-dark:#4C9A91"> 255</span><span style="color:#403F53;--shiki-dark:#666666">,</span><span style="color:#AA0982;--shiki-dark:#4C9A91"> 0</span><span style="color:#403F53;--shiki-dark:#666666">]</span><span style="color:#994CC3;--shiki-dark:#666666">,</span></span>
<span class="line" style="display:inline"><span style="color:#994CC3;--shiki-dark:#666666">}</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#4D9375;--shiki-dark-font-style:inherit"> satisfies</span><span style="color:#111111;--shiki-light-font-style:italic;--shiki-dark:#5DA994;--shiki-dark-font-style:inherit"> Record</span><span style="color:#994CC3;--shiki-dark:#666666">&lt;</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#5DA994;--shiki-dark-font-style:inherit">string</span><span style="color:#994CC3;--shiki-dark:#666666">,</span><span style="color:#111111;--shiki-light-font-style:italic;--shiki-dark:#5DA994;--shiki-dark-font-style:inherit"> Color</span><span style="color:#0C969B;--shiki-dark:#666666"> |</span><span style="color:#111111;--shiki-light-font-style:italic;--shiki-dark:#5DA994;--shiki-dark-font-style:inherit"> RGB</span><span style="color:#994CC3;--shiki-dark:#666666">&gt;</span></span>
<span class="line" style="display:inline"></span>
<span class="line" style="display:inline"><span style="color:#989FB1;--shiki-light-font-style:italic;--shiki-dark:#758575DD;--shiki-dark-font-style:inherit">//</span><span style="color:#939DBB;--shiki-light-font-style:italic;--shiki-dark:#758575DD;--shiki-dark-font-style:inherit"> palette.primary is statically known to be &#x27;red&#x27;, not string!</span></span>
<span class="line" style="display:inline"><span style="color:#994CC3;--shiki-dark:#CB7676">const</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#BD976A;--shiki-dark-font-style:inherit"> upper</span><span style="color:#994CC3;--shiki-dark:#666666"> =</span><span style="color:#403F53;--shiki-light-font-style:italic;--shiki-dark:#BD976A;--shiki-dark-font-style:inherit"> palette</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#666666;--shiki-dark-font-style:inherit">.</span><span style="color:#111111;--shiki-light-font-style:italic;--shiki-dark:#BD976A;--shiki-dark-font-style:inherit">primary</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#666666;--shiki-dark-font-style:inherit">.</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit">toUpperCase</span><span style="color:#403F53;--shiki-dark:#666666">()</span></span></code></pre><h2 id="type-safe-path-routing">Type-Safe Path Routing</h2><p>Recursive mapped types now power end-to-end routing libraries like TanStack Router, catching broken links at compile time.</p></div>]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[Type-Safe Content Pipelines with Content Collections]]></title>
        <id>https://craigary.net/writings/mastering-content-collections</id>
        <link href="https://craigary.net/writings/mastering-content-collections"/>
        <updated>2025-12-10T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[How Content Collections bridges frontmatter validation and full MDX support with Zod schemas.]]></summary>
        <content type="html"><![CDATA[<div class="comark-content article-body"><p>Content management in static sites used to be fragile. Typos in frontmatter keys often went unnoticed until production deployment.</p><h2 id="enter-content-collections">Enter Content Collections</h2><p>With <a href="https://content-collections.dev" rel="noreferrer" target="_blank" class="inline not-prose px-2 py-1 rounded-full text-[0.8em] font-mono bg-neutral-200/50 text-neutral-800 dark:bg-neutral-800/80 dark:text-neutral-100 mx-0.5 !no-underline !border-none selection:bg-transparent selection:text-inherit box-decoration-clone align-baseline hover:bg-neutral-200/80 hover:text-neutral-900 dark:hover:bg-neutral-700 dark:hover:text-white cursor-pointer active:scale-[0.96] transition-[background-color,color,transform] duration-150 ease-out"><span class="leading-none">Content Collections</span></a>, schemas are defined with Zod:</p><pre language="typescript" class="shiki shiki-themes night-owl-light vitesse-dark dark:vitesse-dark" style="background-color:#FBFBFB;color:#403f53;--shiki-dark-bg:#121212;--shiki-dark:#dbd7caee"><code class="language-typescript"><span class="line" style="display:inline"><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#4D9375;--shiki-dark-font-style:inherit">import</span><span style="color:#403F53;--shiki-dark:#666666"> {</span><span style="color:#403F53;--shiki-dark:#BD976A"> defineCollection</span><span style="color:#403F53;--shiki-dark:#666666">,</span><span style="color:#403F53;--shiki-dark:#BD976A"> defineConfig</span><span style="color:#403F53;--shiki-dark:#666666"> }</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#4D9375;--shiki-dark-font-style:inherit"> from</span><span style="color:#111111;--shiki-dark:#C98A7D77"> &#x27;</span><span style="color:#C96765;--shiki-dark:#C98A7D">@content-collections/core</span><span style="color:#111111;--shiki-dark:#C98A7D77">&#x27;</span></span>
<span class="line" style="display:inline"><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#4D9375;--shiki-dark-font-style:inherit">import</span><span style="color:#403F53;--shiki-dark:#666666"> {</span><span style="color:#403F53;--shiki-dark:#BD976A"> z</span><span style="color:#403F53;--shiki-dark:#666666"> }</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#4D9375;--shiki-dark-font-style:inherit"> from</span><span style="color:#111111;--shiki-dark:#C98A7D77"> &#x27;</span><span style="color:#C96765;--shiki-dark:#C98A7D">zod</span><span style="color:#111111;--shiki-dark:#C98A7D77">&#x27;</span></span>
<span class="line" style="display:inline"></span>
<span class="line" style="display:inline"><span style="color:#994CC3;--shiki-dark:#CB7676">const</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#BD976A;--shiki-dark-font-style:inherit"> posts</span><span style="color:#994CC3;--shiki-dark:#666666"> =</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit"> defineCollection</span><span style="color:#403F53;--shiki-dark:#666666">(</span><span style="color:#994CC3;--shiki-dark:#666666">{</span></span>
<span class="line" style="display:inline"><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#B8A965;--shiki-dark-font-style:inherit">  name</span><span style="color:#994CC3;--shiki-dark:#666666">:</span><span style="color:#111111;--shiki-dark:#C98A7D77"> &#x27;</span><span style="color:#C96765;--shiki-dark:#C98A7D">posts</span><span style="color:#111111;--shiki-dark:#C98A7D77">&#x27;</span><span style="color:#994CC3;--shiki-dark:#666666">,</span></span>
<span class="line" style="display:inline"><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#B8A965;--shiki-dark-font-style:inherit">  directory</span><span style="color:#994CC3;--shiki-dark:#666666">:</span><span style="color:#111111;--shiki-dark:#C98A7D77"> &#x27;</span><span style="color:#C96765;--shiki-dark:#C98A7D">./content/posts</span><span style="color:#111111;--shiki-dark:#C98A7D77">&#x27;</span><span style="color:#994CC3;--shiki-dark:#666666">,</span></span>
<span class="line" style="display:inline"><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#B8A965;--shiki-dark-font-style:inherit">  include</span><span style="color:#994CC3;--shiki-dark:#666666">:</span><span style="color:#111111;--shiki-dark:#C98A7D77"> &#x27;</span><span style="color:#C96765;--shiki-dark:#C98A7D">**/*.md</span><span style="color:#111111;--shiki-dark:#C98A7D77">&#x27;</span><span style="color:#994CC3;--shiki-dark:#666666">,</span></span>
<span class="line" style="display:inline"><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#B8A965;--shiki-dark-font-style:inherit">  schema</span><span style="color:#994CC3;--shiki-dark:#666666">:</span><span style="color:#403F53;--shiki-light-font-style:italic;--shiki-dark:#BD976A;--shiki-dark-font-style:inherit"> z</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#666666;--shiki-dark-font-style:inherit">.</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit">object</span><span style="color:#403F53;--shiki-dark:#666666">(</span><span style="color:#994CC3;--shiki-dark:#666666">{</span></span>
<span class="line" style="display:inline"><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#B8A965;--shiki-dark-font-style:inherit">    title</span><span style="color:#994CC3;--shiki-dark:#666666">:</span><span style="color:#403F53;--shiki-light-font-style:italic;--shiki-dark:#BD976A;--shiki-dark-font-style:inherit"> z</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#666666;--shiki-dark-font-style:inherit">.</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit">string</span><span style="color:#403F53;--shiki-dark:#666666">()</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#666666;--shiki-dark-font-style:inherit">.</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit">min</span><span style="color:#403F53;--shiki-dark:#666666">(</span><span style="color:#AA0982;--shiki-dark:#4C9A91">3</span><span style="color:#403F53;--shiki-dark:#666666">)</span><span style="color:#994CC3;--shiki-dark:#666666">,</span></span>
<span class="line" style="display:inline"><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#B8A965;--shiki-dark-font-style:inherit">    date</span><span style="color:#994CC3;--shiki-dark:#666666">:</span><span style="color:#403F53;--shiki-light-font-style:italic;--shiki-dark:#BD976A;--shiki-dark-font-style:inherit"> z</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#666666;--shiki-dark-font-style:inherit">.</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit">string</span><span style="color:#403F53;--shiki-dark:#666666">()</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#666666;--shiki-dark-font-style:inherit">.</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit">regex</span><span style="color:#403F53;--shiki-dark:#666666">(</span><span style="color:#111111;--shiki-dark:#C98A7D77">/</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#4D9375;--shiki-dark-font-style:inherit">^</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#6872AB;--shiki-dark-font-style:inherit">\d</span><span style="color:#0C969B;--shiki-dark:#4C9A91">{4}</span><span style="color:#5CA7E4;--shiki-light-font-style:italic;--shiki-dark:#C4704F;--shiki-dark-font-style:inherit">-</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#6872AB;--shiki-dark-font-style:inherit">\d</span><span style="color:#0C969B;--shiki-dark:#4C9A91">{2}</span><span style="color:#5CA7E4;--shiki-light-font-style:italic;--shiki-dark:#C4704F;--shiki-dark-font-style:inherit">-</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#6872AB;--shiki-dark-font-style:inherit">\d</span><span style="color:#0C969B;--shiki-dark:#4C9A91">{2}</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#4D9375;--shiki-dark-font-style:inherit">$</span><span style="color:#111111;--shiki-dark:#C98A7D77">/</span><span style="color:#403F53;--shiki-dark:#666666">)</span><span style="color:#994CC3;--shiki-dark:#666666">,</span></span>
<span class="line" style="display:inline"><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#B8A965;--shiki-dark-font-style:inherit">    draft</span><span style="color:#994CC3;--shiki-dark:#666666">:</span><span style="color:#403F53;--shiki-light-font-style:italic;--shiki-dark:#BD976A;--shiki-dark-font-style:inherit"> z</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#666666;--shiki-dark-font-style:inherit">.</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit">boolean</span><span style="color:#403F53;--shiki-dark:#666666">()</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#666666;--shiki-dark-font-style:inherit">.</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit">default</span><span style="color:#403F53;--shiki-dark:#666666">(</span><span style="color:#BC5454;--shiki-light-font-style:italic;--shiki-dark:#4D9375;--shiki-dark-font-style:inherit">false</span><span style="color:#403F53;--shiki-dark:#666666">)</span><span style="color:#994CC3;--shiki-dark:#666666">,</span></span>
<span class="line" style="display:inline"><span style="color:#994CC3;--shiki-dark:#666666">  }</span><span style="color:#403F53;--shiki-dark:#666666">)</span><span style="color:#994CC3;--shiki-dark:#666666">,</span></span>
<span class="line" style="display:inline"><span style="color:#994CC3;--shiki-dark:#666666">}</span><span style="color:#403F53;--shiki-dark:#666666">)</span></span>
<span class="line" style="display:inline"></span>
<span class="line" style="display:inline"><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#4D9375;--shiki-dark-font-style:inherit">export</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#4D9375;--shiki-dark-font-style:inherit"> default</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit"> defineConfig</span><span style="color:#403F53;--shiki-dark:#666666">({ </span><span style="color:#403F53;--shiki-dark:#B8A965">content</span><span style="color:#403F53;--shiki-dark:#666666">: [</span><span style="color:#403F53;--shiki-dark:#BD976A">posts</span><span style="color:#403F53;--shiki-dark:#666666">] })</span></span></code></pre><p>Any invalid post fails during the build phase with actionable error messages and line pointers.</p></div>]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[The Rise of Rust Tooling in the JavaScript Ecosystem]]></title>
        <id>https://craigary.net/writings/rust-tooling-for-javascript</id>
        <link href="https://craigary.net/writings/rust-tooling-for-javascript"/>
        <updated>2025-11-04T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Examining why Rust has become the engine of modern JavaScript tooling.]]></summary>
        <content type="html"><![CDATA[<div class="comark-content article-body"><p>The JavaScript ecosystem has undergone an unprecedented infrastructure shift. Tooling that was once written in JavaScript has been rewritten in Rust.</p><h2 id="why-rust">Why Rust?</h2><ul><li><strong>Zero-Cost Abstractions</strong>: Memory efficiency without garbage collection pauses.</li><li><strong>Fearless Concurrency</strong>: Multi-threaded AST transformations saturate all CPU cores.</li><li><strong>Portability</strong>: Native compilation targetable to WebAssembly or native binaries.</li></ul><pre language="rust" class="shiki shiki-themes night-owl-light vitesse-dark dark:vitesse-dark" style="background-color:#FBFBFB;color:#403f53;--shiki-dark-bg:#121212;--shiki-dark:#dbd7caee"><code class="language-rust"><span class="line" style="display:inline"><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#CB7676;--shiki-dark-font-style:inherit">pub</span><span style="color:#994CC3;--shiki-dark:#4D9375"> fn</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit"> parse_source</span><span style="color:#403F53;--shiki-dark:#666666">(</span><span style="color:#4876D6;--shiki-dark:#BD976A">code</span><span style="color:#0C969B;--shiki-dark:#CB7676">:</span><span style="color:#0C969B;--shiki-dark:#CB7676"> &amp;</span><span style="color:#403F53;--shiki-dark:#5DA994">str</span><span style="color:#403F53;--shiki-dark:#666666">)</span><span style="color:#0C969B;--shiki-dark:#CB7676"> -&gt;</span><span style="color:#403F53;--shiki-dark:#5DA994"> Result</span><span style="color:#403F53;--shiki-dark:#666666">&lt;</span><span style="color:#403F53;--shiki-dark:#5DA994">Program</span><span style="color:#403F53;--shiki-dark:#666666">,</span><span style="color:#403F53;--shiki-dark:#5DA994"> ParseError</span><span style="color:#403F53;--shiki-dark:#666666">&gt;</span><span style="color:#403F53;--shiki-dark:#666666"> {</span></span>
<span class="line" style="display:inline"><span style="color:#994CC3;--shiki-dark:#CB7676">    let</span><span style="color:#4876D6;--shiki-dark:#BD976A"> allocator</span><span style="color:#994CC3;--shiki-dark:#666666"> =</span><span style="color:#403F53;--shiki-dark:#5DA994"> Allocator</span><span style="color:#0C969B;--shiki-dark:#CB7676">::</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit">default</span><span style="color:#403F53;--shiki-dark:#666666">();</span></span>
<span class="line" style="display:inline"><span style="color:#994CC3;--shiki-dark:#CB7676">    let</span><span style="color:#4876D6;--shiki-dark:#BD976A"> parser</span><span style="color:#994CC3;--shiki-dark:#666666"> =</span><span style="color:#403F53;--shiki-dark:#5DA994"> Parser</span><span style="color:#0C969B;--shiki-dark:#CB7676">::</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit">new</span><span style="color:#403F53;--shiki-dark:#666666">(</span><span style="color:#0C969B;--shiki-dark:#CB7676">&amp;</span><span style="color:#4876D6;--shiki-dark:#BD976A">allocator</span><span style="color:#403F53;--shiki-dark:#666666">,</span><span style="color:#4876D6;--shiki-dark:#BD976A"> code</span><span style="color:#403F53;--shiki-dark:#666666">,</span><span style="color:#403F53;--shiki-dark:#80A665"> SourceType</span><span style="color:#0C969B;--shiki-dark:#CB7676">::</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit">default</span><span style="color:#403F53;--shiki-dark:#666666">());</span></span>
<span class="line" style="display:inline"><span style="color:#4876D6;--shiki-dark:#BD976A">    parser</span><span style="color:#0C969B;--shiki-dark:#CB7676">.</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit">parse</span><span style="color:#403F53;--shiki-dark:#666666">()</span></span>
<span class="line" style="display:inline"><span style="color:#403F53;--shiki-dark:#666666">}</span></span></code></pre><p>The result is tools that operate at the speed of hardware, giving developers near-instantaneous feedback loops.</p></div>]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[Deploying Full-Stack RSC to Cloudflare Workers]]></title>
        <id>https://craigary.net/writings/cloudflare-workers-rsc</id>
        <link href="https://craigary.net/writings/cloudflare-workers-rsc"/>
        <updated>2025-09-19T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Architecting ultra-low latency applications using React Server Components on edge runtimes.]]></summary>
        <content type="html"><![CDATA[<div class="comark-content article-body"><p>Deploying React applications to the edge has traditionally been limited to simple static assets or basic SSR. With modern RSC runtimes, we can now stream full-stack applications directly from edge nodes.</p><h2 id="edge-benefits">Edge Benefits</h2><ol><li><strong>Near-Zero Latency</strong>: Requests are processed at the data center closest to the client.</li><li><strong>Streaming HTML</strong>: Time to First Byte (TTFB) drops dramatically as the shell streams immediately.</li><li><strong>KV and D1 Integration</strong>: Co-located storage eliminates round-trip penalties to distant databases.</li></ol><pre language="typescript" class="shiki shiki-themes night-owl-light vitesse-dark dark:vitesse-dark" style="background-color:#FBFBFB;color:#403f53;--shiki-dark-bg:#121212;--shiki-dark:#dbd7caee"><code class="language-typescript"><span class="line" style="display:inline"><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#4D9375;--shiki-dark-font-style:inherit">export</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#4D9375;--shiki-dark-font-style:inherit"> default</span><span style="color:#403F53;--shiki-dark:#666666"> {</span></span>
<span class="line" style="display:inline"><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#CB7676;--shiki-dark-font-style:inherit">  async</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit"> fetch</span><span style="color:#111111;--shiki-dark:#666666">(</span><span style="color:#403F53;--shiki-dark:#BD976A">request</span><span style="color:#0C969B;--shiki-dark:#666666">:</span><span style="color:#111111;--shiki-dark:#5DA994"> Request</span><span style="color:#403F53;--shiki-dark:#666666">, </span><span style="color:#403F53;--shiki-dark:#BD976A">env</span><span style="color:#0C969B;--shiki-dark:#666666">:</span><span style="color:#111111;--shiki-dark:#5DA994"> Env</span><span style="color:#111111;--shiki-dark:#666666">)</span><span style="color:#0C969B;--shiki-dark:#666666">:</span><span style="color:#111111;--shiki-dark:#5DA994"> Promise</span><span style="color:#403F53;--shiki-dark:#666666">&lt;</span><span style="color:#111111;--shiki-dark:#5DA994">Response</span><span style="color:#403F53;--shiki-dark:#666666">&gt; {</span></span>
<span class="line" style="display:inline"><span style="color:#4876D6;--shiki-dark:#CB7676">    const</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#BD976A;--shiki-dark-font-style:inherit"> stream</span><span style="color:#994CC3;--shiki-dark:#666666"> =</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#4D9375;--shiki-dark-font-style:inherit"> await</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit"> renderAppToReadableStream</span><span style="color:#403F53;--shiki-dark:#666666">(</span><span style="color:#403F53;--shiki-light-font-style:italic;--shiki-dark:#BD976A;--shiki-dark-font-style:inherit">request</span><span style="color:#994CC3;--shiki-dark:#666666">,</span><span style="color:#403F53;--shiki-light-font-style:italic;--shiki-dark:#BD976A;--shiki-dark-font-style:inherit"> env</span><span style="color:#403F53;--shiki-dark:#666666">)</span></span>
<span class="line" style="display:inline"><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#4D9375;--shiki-dark-font-style:inherit">    return</span><span style="color:#0C969B;--shiki-dark:#CB7676"> new</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit"> Response</span><span style="color:#403F53;--shiki-dark:#666666">(</span><span style="color:#403F53;--shiki-dark:#BD976A">stream</span><span style="color:#403F53;--shiki-dark:#666666">, {</span></span>
<span class="line" style="display:inline"><span style="color:#403F53;--shiki-dark:#B8A965">      headers</span><span style="color:#403F53;--shiki-dark:#666666">: { </span><span style="color:#111111;--shiki-dark:#C98A7D77">&#x27;</span><span style="color:#C96765;--shiki-dark:#C98A7D">Content-Type</span><span style="color:#111111;--shiki-dark:#C98A7D77">&#x27;</span><span style="color:#403F53;--shiki-dark:#666666">: </span><span style="color:#111111;--shiki-dark:#C98A7D77">&#x27;</span><span style="color:#C96765;--shiki-dark:#C98A7D">text/html; charset=utf-8</span><span style="color:#111111;--shiki-dark:#C98A7D77">&#x27;</span><span style="color:#403F53;--shiki-dark:#666666"> },</span></span>
<span class="line" style="display:inline"><span style="color:#403F53;--shiki-dark:#666666">    })</span></span>
<span class="line" style="display:inline"><span style="color:#403F53;--shiki-dark:#666666">  },</span></span>
<span class="line" style="display:inline"><span style="color:#403F53;--shiki-dark:#666666">}</span></span></code></pre></div>]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[Crafting Interfaces: My Design Engineering Philosophy]]></title>
        <id>https://craigary.net/writings/design-engineering-philosophy</id>
        <link href="https://craigary.net/writings/design-engineering-philosophy"/>
        <updated>2025-08-01T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[The intersection of visual aesthetics, interaction fidelity, and systematic code design.]]></summary>
        <content type="html"><![CDATA[<div class="comark-content article-body"><p>Design engineering is more than just turning Figma mocks into CSS. It is about understanding nuance, physics, rhythm, and ergonomics.</p><h2 id="pillars-of-design-engineering">Pillars of Design Engineering</h2><h3 id="pillars-of-design-engineering-_1-motion-with-intent">1. Motion with Intent</h3><p>Animations must never feel gratuitous. They should provide spatial orientation and tactile feedback.</p><h3 id="pillars-of-design-engineering-_2-typography-as-the-backbone">2. Typography as the Backbone</h3><p>A good typographic hierarchy communicates 90% of the message before a user even begins to read.</p><ul><li>Maintain consistent vertical rhythm.</li><li>Use fluid type scaling with <code>clamp()</code>.</li><li>Avoid arbitrary font sizes.</li></ul><h3 id="pillars-of-design-engineering-_3-graceful-degradation">3. Graceful Degradation</h3><p>An application should remain functional and legible regardless of network conditions, reduced motion preferences, or viewport constraints.</p></div>]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[Practical Guide to Optimizing Core Web Vitals (INP & LCP)]]></title>
        <id>https://craigary.net/writings/optimizing-web-vitals</id>
        <link href="https://craigary.net/writings/optimizing-web-vitals"/>
        <updated>2025-06-15T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Actionable techniques to boost your site’s Interaction to Next Paint and Largest Contentful Paint.]]></summary>
        <content type="html"><![CDATA[<div class="comark-content article-body"><p>Google’s Core Web Vitals directly correlate with user satisfaction and search rankings. Let us look at high-impact techniques to optimize them.</p><h2 id="crushing-inp-interaction-to-next-paint">Crushing INP (Interaction to Next Paint)</h2><p>Long tasks that block the main thread for over 50ms cause noticeable input lag.</p><pre language="typescript" class="shiki shiki-themes night-owl-light vitesse-dark dark:vitesse-dark" style="background-color:#FBFBFB;color:#403f53;--shiki-dark-bg:#121212;--shiki-dark:#dbd7caee"><code class="language-typescript"><span class="line" style="display:inline"><span style="color:#989FB1;--shiki-light-font-style:italic;--shiki-dark:#758575DD;--shiki-dark-font-style:inherit">//</span><span style="color:#939DBB;--shiki-light-font-style:italic;--shiki-dark:#758575DD;--shiki-dark-font-style:inherit"> Yield to main thread during expensive operations</span></span>
<span class="line" style="display:inline"><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#CB7676;--shiki-dark-font-style:inherit">async</span><span style="color:#994CC3;--shiki-dark:#CB7676"> function</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit"> yieldToMain</span><span style="color:#111111;--shiki-dark:#666666">()</span><span style="color:#403F53;--shiki-dark:#666666"> {</span></span>
<span class="line" style="display:inline"><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#4D9375;--shiki-dark-font-style:inherit">  return</span><span style="color:#0C969B;--shiki-dark:#CB7676"> new</span><span style="color:#4876D6;--shiki-dark:#B8A965"> Promise</span><span style="color:#403F53;--shiki-dark:#666666">(</span><span style="color:#111111;--shiki-dark:#666666">(</span><span style="color:#403F53;--shiki-dark:#BD976A">resolve</span><span style="color:#111111;--shiki-dark:#666666">)</span><span style="color:#994CC3;--shiki-dark:#666666"> =&gt;</span><span style="color:#403F53;--shiki-dark:#666666"> {</span></span>
<span class="line" style="display:inline"><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit">    setTimeout</span><span style="color:#403F53;--shiki-dark:#666666">(</span><span style="color:#403F53;--shiki-dark:#BD976A">resolve</span><span style="color:#5F7E97;--shiki-dark:#666666">,</span><span style="color:#AA0982;--shiki-dark:#4C9A91"> 0</span><span style="color:#403F53;--shiki-dark:#666666">)</span></span>
<span class="line" style="display:inline"><span style="color:#403F53;--shiki-dark:#666666">  })</span></span>
<span class="line" style="display:inline"><span style="color:#403F53;--shiki-dark:#666666">}</span></span>
<span class="line" style="display:inline"></span>
<span class="line" style="display:inline"><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#4D9375;--shiki-dark-font-style:inherit">for</span><span style="color:#403F53;--shiki-dark:#666666"> (</span><span style="color:#994CC3;--shiki-dark:#CB7676">const</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#BD976A;--shiki-dark-font-style:inherit"> batch</span><span style="color:#0C969B;--shiki-dark:#CB7676"> of</span><span style="color:#403F53;--shiki-dark:#BD976A"> largeDataset</span><span style="color:#403F53;--shiki-dark:#666666">)</span><span style="color:#403F53;--shiki-dark:#666666"> {</span></span>
<span class="line" style="display:inline"><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit">  processBatch</span><span style="color:#403F53;--shiki-dark:#666666">(</span><span style="color:#403F53;--shiki-dark:#BD976A">batch</span><span style="color:#403F53;--shiki-dark:#666666">)</span></span>
<span class="line" style="display:inline"><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#4D9375;--shiki-dark-font-style:inherit">  await</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit"> yieldToMain</span><span style="color:#403F53;--shiki-dark:#666666">()</span></span>
<span class="line" style="display:inline"><span style="color:#403F53;--shiki-dark:#666666">}</span></span></code></pre><h2 id="streamlining-lcp">Streamlining LCP</h2><ul><li>Preload hero fonts with <code>&lt;link rel=&quot;preload&quot; as=&quot;font&quot;&gt;</code>.</li><li>Use modern formats like AVIF or WebP.</li><li>Inline critical CSS to avoid network blocking.</li></ul></div>]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[Modern Monorepo Architecture with Pnpm Workspaces]]></title>
        <id>https://craigary.net/writings/monorepo-best-practices</id>
        <link href="https://craigary.net/writings/monorepo-best-practices"/>
        <updated>2025-05-11T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Structuring scalable TypeScript monorepos with strict dependency isolation and fast caching.]]></summary>
        <content type="html"><![CDATA[<div class="comark-content article-body"><p>Managing multiple packages in a single repository requires disciplined structure and reliable tooling.</p><h2 id="pnpm-workspaces-setup">Pnpm Workspaces Setup</h2><p>Here is a clean <code>pnpm-workspace.yaml</code> layout:</p><pre language="yaml" class="shiki shiki-themes night-owl-light vitesse-dark dark:vitesse-dark" style="background-color:#FBFBFB;color:#403f53;--shiki-dark-bg:#121212;--shiki-dark:#dbd7caee"><code class="language-yaml"><span class="line" style="display:inline"><span style="color:#111111;--shiki-dark:#B8A965">packages</span><span style="color:#403F53;--shiki-dark:#666666">:</span></span>
<span class="line" style="display:inline"><span style="color:#403F53;--shiki-dark:#666666">  -</span><span style="color:#111111;--shiki-dark:#C98A7D77"> &#x27;</span><span style="color:#C96765;--shiki-dark:#C98A7D">apps/*</span><span style="color:#111111;--shiki-dark:#C98A7D77">&#x27;</span></span>
<span class="line" style="display:inline"><span style="color:#403F53;--shiki-dark:#666666">  -</span><span style="color:#111111;--shiki-dark:#C98A7D77"> &#x27;</span><span style="color:#C96765;--shiki-dark:#C98A7D">packages/*</span><span style="color:#111111;--shiki-dark:#C98A7D77">&#x27;</span></span>
<span class="line" style="display:inline"><span style="color:#403F53;--shiki-dark:#666666">  -</span><span style="color:#111111;--shiki-dark:#C98A7D77"> &#x27;</span><span style="color:#C96765;--shiki-dark:#C98A7D">!**/test/**</span><span style="color:#111111;--shiki-dark:#C98A7D77">&#x27;</span></span></code></pre><h3 id="pnpm-workspaces-setup-key-advantages">Key Advantages</h3><ul><li><strong>Deduplication</strong>: Hard links save gigabytes of disk space across packages.</li><li><strong>Strict Isolation</strong>: Phantom dependencies are prevented by default.</li><li><strong>Catalogs</strong>: Centralize versions of shared dependencies in <code>pnpm-workspace.yaml</code>.</li></ul></div>]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[Modern CSS: Native Nesting, Cascade Layers & Container Queries]]></title>
        <id>https://craigary.net/writings/future-of-css-nesting</id>
        <link href="https://craigary.net/writings/future-of-css-nesting"/>
        <updated>2025-03-27T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[How standard CSS has rendered many preprocessors obsolete with native features.]]></summary>
        <content type="html"><![CDATA[<div class="comark-content article-body"><p>CSS has experienced an incredible renaissance over the last three years. Features that previously mandated preprocessors are now universally supported.</p><h2 id="native-nesting-in-action">Native Nesting in Action</h2><pre language="css" class="shiki shiki-themes night-owl-light vitesse-dark dark:vitesse-dark" style="background-color:#FBFBFB;color:#403f53;--shiki-dark-bg:#121212;--shiki-dark:#dbd7caee"><code class="language-css"><span class="line" style="display:inline"><span style="color:#4876D6;--shiki-dark:#666666">.</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#BD976A;--shiki-dark-font-style:inherit">card</span><span style="color:#403F53;--shiki-dark:#666666"> {</span></span>
<span class="line" style="display:inline"><span style="color:#0C969B;--shiki-dark:#B8A965">  background-color</span><span style="color:#403F53;--shiki-dark:#666666">:</span><span style="color:#4876D6;--shiki-dark:#B8A965"> var</span><span style="color:#403F53;--shiki-dark:#666666">(</span><span style="color:#4876D6;--shiki-dark:#BD976A">--card-bg</span><span style="color:#403F53;--shiki-dark:#666666">);</span></span>
<span class="line" style="display:inline"><span style="color:#0C969B;--shiki-dark:#B8A965">  border-radius</span><span style="color:#403F53;--shiki-dark:#666666">:</span><span style="color:#AA0982;--shiki-dark:#4C9A91"> 0.75</span><span style="color:#AA0982;--shiki-dark:#CB7676">rem</span><span style="color:#403F53;--shiki-dark:#666666">;</span></span>
<span class="line" style="display:inline"></span>
<span class="line" style="display:inline"><span style="color:#0C969B;--shiki-dark:#4D9375">  &amp;</span><span style="color:#4876D6;--shiki-dark:#666666"> .</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#BD976A;--shiki-dark-font-style:inherit">card-header</span><span style="color:#403F53;--shiki-dark:#666666"> {</span></span>
<span class="line" style="display:inline"><span style="color:#0C969B;--shiki-dark:#B8A965">    font-weight</span><span style="color:#403F53;--shiki-dark:#666666">:</span><span style="color:#AA0982;--shiki-dark:#4C9A91"> 600</span><span style="color:#403F53;--shiki-dark:#666666">;</span></span>
<span class="line" style="display:inline"><span style="color:#403F53;--shiki-dark:#666666">  }</span></span>
<span class="line" style="display:inline"></span>
<span class="line" style="display:inline"><span style="color:#0C969B;--shiki-dark:#4D9375">  &amp;</span><span style="color:#4876D6;--shiki-dark:#666666">:</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#BD976A;--shiki-dark-font-style:inherit">hover</span><span style="color:#403F53;--shiki-dark:#666666"> {</span></span>
<span class="line" style="display:inline"><span style="color:#0C969B;--shiki-dark:#B8A965">    box-shadow</span><span style="color:#403F53;--shiki-dark:#666666">:</span><span style="color:#AA0982;--shiki-dark:#4C9A91"> 0</span><span style="color:#AA0982;--shiki-dark:#4C9A91"> 10</span><span style="color:#AA0982;--shiki-dark:#CB7676">px</span><span style="color:#AA0982;--shiki-dark:#4C9A91"> 15</span><span style="color:#AA0982;--shiki-dark:#CB7676">px</span><span style="color:#AA0982;--shiki-dark:#4C9A91"> -3</span><span style="color:#AA0982;--shiki-dark:#CB7676">px</span><span style="color:#4876D6;--shiki-dark:#B8A965"> rgba</span><span style="color:#403F53;--shiki-dark:#666666">(</span><span style="color:#AA0982;--shiki-dark:#4C9A91">0</span><span style="color:#403F53;--shiki-dark:#666666">,</span><span style="color:#AA0982;--shiki-dark:#4C9A91"> 0</span><span style="color:#403F53;--shiki-dark:#666666">,</span><span style="color:#AA0982;--shiki-dark:#4C9A91"> 0</span><span style="color:#403F53;--shiki-dark:#666666">,</span><span style="color:#AA0982;--shiki-dark:#4C9A91"> 0.1</span><span style="color:#403F53;--shiki-dark:#666666">);</span></span>
<span class="line" style="display:inline"><span style="color:#403F53;--shiki-dark:#666666">  }</span></span>
<span class="line" style="display:inline"><span style="color:#403F53;--shiki-dark:#666666">}</span></span></code></pre><h2 id="cascade-layers-layer">Cascade Layers (<code>@layer</code>)</h2><p>Manage specificity cleanly without resorting to <code>!important</code>:</p><pre language="css" class="shiki shiki-themes night-owl-light vitesse-dark dark:vitesse-dark" style="background-color:#FBFBFB;color:#403f53;--shiki-dark-bg:#121212;--shiki-dark:#dbd7caee"><code class="language-css"><span class="line" style="display:inline"><span style="color:#994CC3;--shiki-dark:#666666">@</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#4D9375;--shiki-dark-font-style:inherit">layer</span><span style="color:#403F53;--shiki-dark:#BD976A"> reset</span><span style="color:#403F53;--shiki-dark:#666666">,</span><span style="color:#403F53;--shiki-dark:#BD976A"> base</span><span style="color:#403F53;--shiki-dark:#666666">,</span><span style="color:#403F53;--shiki-dark:#BD976A"> components</span><span style="color:#403F53;--shiki-dark:#666666">,</span><span style="color:#403F53;--shiki-dark:#BD976A"> utilities</span><span style="color:#403F53;--shiki-dark:#666666">;</span></span>
<span class="line" style="display:inline"></span>
<span class="line" style="display:inline"><span style="color:#994CC3;--shiki-dark:#666666">@</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#4D9375;--shiki-dark-font-style:inherit">layer</span><span style="color:#403F53;--shiki-dark:#BD976A"> components</span><span style="color:#403F53;--shiki-dark:#666666"> {</span></span>
<span class="line" style="display:inline"><span style="color:#4876D6;--shiki-dark:#666666">  .</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#BD976A;--shiki-dark-font-style:inherit">btn</span><span style="color:#403F53;--shiki-dark:#666666"> {</span></span>
<span class="line" style="display:inline"><span style="color:#0C969B;--shiki-dark:#B8A965">    padding</span><span style="color:#403F53;--shiki-dark:#666666">:</span><span style="color:#AA0982;--shiki-dark:#4C9A91"> 0.5</span><span style="color:#AA0982;--shiki-dark:#CB7676">rem</span><span style="color:#AA0982;--shiki-dark:#4C9A91"> 1</span><span style="color:#AA0982;--shiki-dark:#CB7676">rem</span><span style="color:#403F53;--shiki-dark:#666666">;</span></span>
<span class="line" style="display:inline"><span style="color:#403F53;--shiki-dark:#666666">  }</span></span>
<span class="line" style="display:inline"><span style="color:#403F53;--shiki-dark:#666666">}</span></span></code></pre></div>]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[Effective Vitest Patterns for Component and Integration Testing]]></title>
        <id>https://craigary.net/writings/effective-vitest-patterns</id>
        <link href="https://craigary.net/writings/effective-vitest-patterns"/>
        <updated>2025-02-14T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Writing fast, reliable, and expressive tests with Vitest and Testing Library.]]></summary>
        <content type="html"><![CDATA[<div class="comark-content article-body"><p><a href="https://vitest.dev" rel="noreferrer" target="_blank" class="inline not-prose px-2 py-1 rounded-full text-[0.8em] font-mono bg-neutral-200/50 text-neutral-800 dark:bg-neutral-800/80 dark:text-neutral-100 mx-0.5 !no-underline !border-none selection:bg-transparent selection:text-inherit box-decoration-clone align-baseline hover:bg-neutral-200/80 hover:text-neutral-900 dark:hover:bg-neutral-700 dark:hover:text-white cursor-pointer active:scale-[0.96] transition-[background-color,color,transform] duration-150 ease-out"><span class="leading-none">Vitest</span></a> is powered by Vite, sharing the same transform pipeline, resolve configuration, and plugins.</p><h2 id="testing-component-behavior">Testing Component Behavior</h2><pre language="typescript" class="shiki shiki-themes night-owl-light vitesse-dark dark:vitesse-dark" style="background-color:#FBFBFB;color:#403f53;--shiki-dark-bg:#121212;--shiki-dark:#dbd7caee"><code class="language-typescript"><span class="line" style="display:inline"><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#4D9375;--shiki-dark-font-style:inherit">import</span><span style="color:#403F53;--shiki-dark:#666666"> {</span><span style="color:#403F53;--shiki-dark:#BD976A"> render</span><span style="color:#403F53;--shiki-dark:#666666">,</span><span style="color:#403F53;--shiki-dark:#BD976A"> screen</span><span style="color:#403F53;--shiki-dark:#666666"> }</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#4D9375;--shiki-dark-font-style:inherit"> from</span><span style="color:#111111;--shiki-dark:#C98A7D77"> &#x27;</span><span style="color:#C96765;--shiki-dark:#C98A7D">@testing-library/react</span><span style="color:#111111;--shiki-dark:#C98A7D77">&#x27;</span></span>
<span class="line" style="display:inline"><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#4D9375;--shiki-dark-font-style:inherit">import</span><span style="color:#403F53;--shiki-dark:#BD976A"> userEvent</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#4D9375;--shiki-dark-font-style:inherit"> from</span><span style="color:#111111;--shiki-dark:#C98A7D77"> &#x27;</span><span style="color:#C96765;--shiki-dark:#C98A7D">@testing-library/user-event</span><span style="color:#111111;--shiki-dark:#C98A7D77">&#x27;</span></span>
<span class="line" style="display:inline"><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#4D9375;--shiki-dark-font-style:inherit">import</span><span style="color:#403F53;--shiki-dark:#666666"> {</span><span style="color:#403F53;--shiki-dark:#BD976A"> describe</span><span style="color:#403F53;--shiki-dark:#666666">,</span><span style="color:#403F53;--shiki-dark:#BD976A"> expect</span><span style="color:#403F53;--shiki-dark:#666666">,</span><span style="color:#403F53;--shiki-dark:#BD976A"> it</span><span style="color:#403F53;--shiki-dark:#666666"> }</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#4D9375;--shiki-dark-font-style:inherit"> from</span><span style="color:#111111;--shiki-dark:#C98A7D77"> &#x27;</span><span style="color:#C96765;--shiki-dark:#C98A7D">vitest</span><span style="color:#111111;--shiki-dark:#C98A7D77">&#x27;</span></span>
<span class="line" style="display:inline"><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#4D9375;--shiki-dark-font-style:inherit">import</span><span style="color:#403F53;--shiki-dark:#BD976A"> Counter</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#4D9375;--shiki-dark-font-style:inherit"> from</span><span style="color:#111111;--shiki-dark:#C98A7D77"> &#x27;</span><span style="color:#C96765;--shiki-dark:#C98A7D">./Counter</span><span style="color:#111111;--shiki-dark:#C98A7D77">&#x27;</span></span>
<span class="line" style="display:inline"></span>
<span class="line" style="display:inline"><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit">describe</span><span style="color:#403F53;--shiki-dark:#666666">(</span><span style="color:#111111;--shiki-dark:#C98A7D77">&#x27;</span><span style="color:#C96765;--shiki-dark:#C98A7D">Counter</span><span style="color:#111111;--shiki-dark:#C98A7D77">&#x27;</span><span style="color:#403F53;--shiki-dark:#666666">,</span><span style="color:#111111;--shiki-dark:#666666"> ()</span><span style="color:#994CC3;--shiki-dark:#666666"> =&gt;</span><span style="color:#403F53;--shiki-dark:#666666"> {</span></span>
<span class="line" style="display:inline"><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit">  it</span><span style="color:#403F53;--shiki-dark:#666666">(</span><span style="color:#111111;--shiki-dark:#C98A7D77">&#x27;</span><span style="color:#C96765;--shiki-dark:#C98A7D">increments count on click</span><span style="color:#111111;--shiki-dark:#C98A7D77">&#x27;</span><span style="color:#403F53;--shiki-dark:#666666">,</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#CB7676;--shiki-dark-font-style:inherit"> async</span><span style="color:#111111;--shiki-dark:#666666"> ()</span><span style="color:#994CC3;--shiki-dark:#666666"> =&gt;</span><span style="color:#403F53;--shiki-dark:#666666"> {</span></span>
<span class="line" style="display:inline"><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit">    render</span><span style="color:#403F53;--shiki-dark:#666666">(&lt;</span><span style="color:#111111;--shiki-dark:#5DA994">Counter</span><span style="color:#403F53;--shiki-dark:#DBD7CAEE"> /</span><span style="color:#403F53;--shiki-dark:#666666">&gt;)</span></span>
<span class="line" style="display:inline"><span style="color:#994CC3;--shiki-dark:#CB7676">    const</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#BD976A;--shiki-dark-font-style:inherit"> button</span><span style="color:#994CC3;--shiki-dark:#666666"> =</span><span style="color:#403F53;--shiki-light-font-style:italic;--shiki-dark:#BD976A;--shiki-dark-font-style:inherit"> screen</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#666666;--shiki-dark-font-style:inherit">.</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit">getByRole</span><span style="color:#403F53;--shiki-dark:#666666">(</span><span style="color:#111111;--shiki-dark:#C98A7D77">&#x27;</span><span style="color:#C96765;--shiki-dark:#C98A7D">button</span><span style="color:#111111;--shiki-dark:#C98A7D77">&#x27;</span><span style="color:#994CC3;--shiki-dark:#666666">,</span><span style="color:#994CC3;--shiki-dark:#666666"> {</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#B8A965;--shiki-dark-font-style:inherit"> name</span><span style="color:#994CC3;--shiki-dark:#666666">:</span><span style="color:#111111;--shiki-dark:#C98A7D77"> /</span><span style="color:#5CA7E4;--shiki-light-font-style:italic;--shiki-dark:#C4704F;--shiki-dark-font-style:inherit">count</span><span style="color:#111111;--shiki-dark:#C98A7D77">/</span><span style="color:#5CA7E4;--shiki-dark:#4D9375">i</span><span style="color:#994CC3;--shiki-dark:#666666"> }</span><span style="color:#403F53;--shiki-dark:#666666">)</span></span>
<span class="line" style="display:inline"></span>
<span class="line" style="display:inline"><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#4D9375;--shiki-dark-font-style:inherit">    await</span><span style="color:#403F53;--shiki-dark:#BD976A"> userEvent</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#666666;--shiki-dark-font-style:inherit">.</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit">click</span><span style="color:#403F53;--shiki-dark:#666666">(</span><span style="color:#403F53;--shiki-dark:#BD976A">button</span><span style="color:#403F53;--shiki-dark:#666666">)</span></span>
<span class="line" style="display:inline"><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit">    expect</span><span style="color:#403F53;--shiki-dark:#666666">(</span><span style="color:#403F53;--shiki-dark:#BD976A">screen</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#666666;--shiki-dark-font-style:inherit">.</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit">getByText</span><span style="color:#403F53;--shiki-dark:#666666">(</span><span style="color:#111111;--shiki-dark:#C98A7D77">&#x27;</span><span style="color:#C96765;--shiki-dark:#C98A7D">Count: 1</span><span style="color:#111111;--shiki-dark:#C98A7D77">&#x27;</span><span style="color:#403F53;--shiki-dark:#666666">))</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#666666;--shiki-dark-font-style:inherit">.</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit">toBeInTheDocument</span><span style="color:#403F53;--shiki-dark:#666666">()</span></span>
<span class="line" style="display:inline"><span style="color:#403F53;--shiki-dark:#666666">  })</span></span>
<span class="line" style="display:inline"><span style="color:#403F53;--shiki-dark:#666666">})</span></span></code></pre><p>Tests run directly against the development build with instant watch mode triggers.</p></div>]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[The Subtle Art of Micro-interactions in Web UI]]></title>
        <id>https://craigary.net/writings/micro-interactions-in-web-ui</id>
        <link href="https://craigary.net/writings/micro-interactions-in-web-ui"/>
        <updated>2025-01-08T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Elevating digital products with purposeful animations, tactile feedback, and sensory delight.]]></summary>
        <content type="html"><![CDATA[<div class="comark-content article-body"><p>What separates a functional product from an unforgettable one? In almost every case, it is the micro-interactions.</p><h2 id="principles-of-micro-interactions">Principles of Micro-interactions</h2><ol><li><strong>Trigger</strong>: An intuitive cue that prompts an action.</li><li><strong>Feedback</strong>: Visual or auditory confirmation of the state transition.</li><li><strong>Restraint</strong>: If an animation draws attention to itself rather than facilitating the task, it is too loud.</li></ol><blockquote>“Details are not details. They make the design.” — Charles Eames</blockquote><p>Subtle spring physics and opacity transitions communicate weight and polish.</p></div>]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[Building Extensible Markdown & MDX Pipelines]]></title>
        <id>https://craigary.net/writings/building-extensible-markdown-pipelines</id>
        <link href="https://craigary.net/writings/building-extensible-markdown-pipelines"/>
        <updated>2024-11-20T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[A deep dive into Unified, Remark, and Rehype plugins for custom documentation systems.]]></summary>
        <content type="html"><![CDATA[<div class="comark-content article-body"><p>The Unified and Comark ecosystems are immensely flexible abstractions for processing modern web markup<sup class="footnote-ref"><a href="#fn-ast" id="fnref-ast">[1]</a></sup>. By parsing source text into a compact abstract syntax tree, developers can manipulate nodes with modular plugins before rendering with modern UI frameworks.</p><h2 id="pipeline-architecture">Pipeline Architecture</h2><p>The processing lifecycle turns raw Markdown into rich, interactive components on the web<sup class="footnote-ref"><a href="#fn-plugins" id="fnref-plugins">[2]</a></sup>:</p><div class="not-prose mermaid my-8 flex w-full justify-center overflow-x-auto rounded-xl border border-neutral-200/80 bg-neutral-50/50 p-6 transition-colors duration-200 dark:border-neutral-800/80 dark:bg-neutral-900/30 " style="width:100%"><div class="mermaid-light"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 656.062 626.3" width="656.062" height="626.3" style="--bg:#ffffff;--fg:#1f2328;--line:#d1d9e0;--accent:#0969da;--muted:#59636e">
<style>
  @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&amp;display=swap');
  text { font-family: 'Inter', system-ui, sans-serif; }
  svg {
    /* Derived from --bg and --fg (overridable via --line, --accent, etc.) */
    --_text:          var(--fg);
    --_text-sec:      var(--muted, color-mix(in srgb, var(--fg) 60%, var(--bg)));
    --_text-muted:    var(--muted, color-mix(in srgb, var(--fg) 40%, var(--bg)));
    --_text-faint:    color-mix(in srgb, var(--fg) 25%, var(--bg));
    --_line:          var(--line, color-mix(in srgb, var(--fg) 50%, var(--bg)));
    --_arrow:         var(--accent, color-mix(in srgb, var(--fg) 85%, var(--bg)));
    --_node-fill:     var(--surface, color-mix(in srgb, var(--fg) 3%, var(--bg)));
    --_node-stroke:   var(--border, color-mix(in srgb, var(--fg) 20%, var(--bg)));
    --_group-fill:    var(--bg);
    --_group-hdr:     color-mix(in srgb, var(--fg) 5%, var(--bg));
    --_inner-stroke:  color-mix(in srgb, var(--fg) 12%, var(--bg));
    --_key-badge:     color-mix(in srgb, var(--fg) 10%, var(--bg));
  }
</style>
<defs>
  <marker id="arrowhead" markerWidth="8" markerHeight="5" refX="7" refY="2.5" orient="auto">
    <polygon points="0 0, 8 2.5, 0 5" fill="var(--_arrow)" stroke="var(--_arrow)" stroke-width="0.75" stroke-linejoin="round" />
  </marker>
  <marker id="arrowhead-start" markerWidth="8" markerHeight="5" refX="1" refY="2.5" orient="auto-start-reverse">
    <polygon points="8 0, 0 2.5, 8 5" fill="var(--_arrow)" stroke="var(--_arrow)" stroke-width="0.75" stroke-linejoin="round" />
  </marker>
</defs>
<polyline class="edge" data-from="A" data-to="B" data-style="solid" data-arrow-start="false" data-arrow-end="true" points="340.25750000000005,76.9 340.25750000000005,124.9" fill="none" stroke="var(--_line)" stroke-width="1" marker-end="url(#arrowhead)" />
<polyline class="edge" data-from="B" data-to="C" data-style="solid" data-arrow-start="false" data-arrow-end="true" points="340.25750000000005,161.8 340.25750000000005,209.8" fill="none" stroke="var(--_line)" stroke-width="1" marker-end="url(#arrowhead)" />
<polyline class="edge" data-from="C" data-to="D" data-style="solid" data-arrow-start="false" data-arrow-end="true" points="340.25750000000005,246.70000000000002 340.25750000000005,276.70000000000005 138.78,276.70000000000005 138.78,306.70000000000005" fill="none" stroke="var(--_line)" stroke-width="1" marker-end="url(#arrowhead)" />
<polyline class="edge" data-from="C" data-to="E" data-style="solid" data-arrow-start="false" data-arrow-end="true" points="340.25750000000005,246.70000000000002 340.25750000000005,276.70000000000005 340.2575,276.70000000000005 340.2575,306.70000000000005" fill="none" stroke="var(--_line)" stroke-width="1" marker-end="url(#arrowhead)" />
<polyline class="edge" data-from="C" data-to="F" data-style="solid" data-arrow-start="false" data-arrow-end="true" points="340.25750000000005,246.70000000000002 340.25750000000005,276.70000000000005 529.5085,276.70000000000005 529.5085,306.70000000000005" fill="none" stroke="var(--_line)" stroke-width="1" marker-end="url(#arrowhead)" />
<polyline class="edge" data-from="D" data-to="G" data-style="solid" data-arrow-start="false" data-arrow-end="true" points="138.78,343.6 138.78,361.6 340.2575,361.6 340.2575,379.6" fill="none" stroke="var(--_line)" stroke-width="1" marker-end="url(#arrowhead)" />
<polyline class="edge" data-from="E" data-to="G" data-style="solid" data-arrow-start="false" data-arrow-end="true" points="340.2575,343.6 340.2575,361.6 340.2575,361.6 340.2575,379.6" fill="none" stroke="var(--_line)" stroke-width="1" marker-end="url(#arrowhead)" />
<polyline class="edge" data-from="F" data-to="G" data-style="solid" data-arrow-start="false" data-arrow-end="true" points="529.5085,343.6 529.5085,361.6 340.2575,361.6 340.2575,379.6" fill="none" stroke="var(--_line)" stroke-width="1" marker-end="url(#arrowhead)" />
<polyline class="edge" data-from="G" data-to="H" data-style="solid" data-arrow-start="false" data-arrow-end="true" points="340.2575,416.5 340.2575,464.5" fill="none" stroke="var(--_line)" stroke-width="1" marker-end="url(#arrowhead)" />
<polyline class="edge" data-from="H" data-to="I" data-style="solid" data-arrow-start="false" data-arrow-end="true" points="340.2575,501.4 340.2575,549.4" fill="none" stroke="var(--_line)" stroke-width="1" marker-end="url(#arrowhead)" />
<g class="node" data-id="A" data-label="Markdown Source" data-shape="rectangle">
  <rect x="264.4485" y="40" width="151.618" height="36.900000000000006" rx="0" ry="0" fill="var(--_node-fill)" stroke="var(--_node-stroke)" stroke-width="0.75" />
  <text x="340.25750000000005" y="58.45" text-anchor="middle" font-size="13" font-weight="500" fill="var(--_text)" dy="4.55">Markdown Source</text>
</g>
<g class="node" data-id="B" data-label="Comark Parser" data-shape="rectangle">
  <rect x="273.711" y="124.9" width="133.09300000000002" height="36.900000000000006" rx="0" ry="0" fill="var(--_node-fill)" stroke="var(--_node-stroke)" stroke-width="0.75" />
  <text x="340.25750000000005" y="143.35000000000002" text-anchor="middle" font-size="13" font-weight="500" fill="var(--_text)" dy="4.55">Comark Parser</text>
</g>
<g class="node" data-id="C" data-label="Plugin Pipeline" data-shape="rectangle">
  <rect x="275.934" y="209.8" width="128.647" height="36.900000000000006" rx="0" ry="0" fill="var(--_node-fill)" stroke="var(--_node-stroke)" stroke-width="0.75" />
  <text x="340.25750000000005" y="228.25" text-anchor="middle" font-size="13" font-weight="500" fill="var(--_text)" dy="4.55">Plugin Pipeline</text>
</g>
<g class="node" data-id="D" data-label="Punctuation &amp; Typography" data-shape="rectangle">
  <rect x="40" y="306.70000000000005" width="197.56" height="36.900000000000006" rx="0" ry="0" fill="var(--_node-fill)" stroke="var(--_node-stroke)" stroke-width="0.75" />
  <text x="138.78" y="325.15000000000003" text-anchor="middle" font-size="13" font-weight="500" fill="var(--_text)" dy="4.55">Punctuation &amp; Typography</text>
</g>
<g class="node" data-id="E" data-label="Footnote Collector" data-shape="rectangle">
  <rect x="265.56" y="306.70000000000005" width="149.395" height="36.900000000000006" rx="0" ry="0" fill="var(--_node-fill)" stroke="var(--_node-stroke)" stroke-width="0.75" />
  <text x="340.2575" y="325.15000000000003" text-anchor="middle" font-size="13" font-weight="500" fill="var(--_text)" dy="4.55">Footnote Collector</text>
</g>
<g class="node" data-id="F" data-label="Mermaid Diagram AST" data-shape="rectangle">
  <rect x="442.95500000000004" y="306.70000000000005" width="173.10699999999997" height="36.900000000000006" rx="0" ry="0" fill="var(--_node-fill)" stroke="var(--_node-stroke)" stroke-width="0.75" />
  <text x="529.5085" y="325.15000000000003" text-anchor="middle" font-size="13" font-weight="500" fill="var(--_text)" dy="4.55">Mermaid Diagram AST</text>
</g>
<g class="node" data-id="G" data-label="Unified Document AST" data-shape="rectangle">
  <rect x="254.81550000000001" y="379.6" width="170.88399999999996" height="36.900000000000006" rx="0" ry="0" fill="var(--_node-fill)" stroke="var(--_node-stroke)" stroke-width="0.75" />
  <text x="340.2575" y="398.05" text-anchor="middle" font-size="13" font-weight="500" fill="var(--_text)" dy="4.55">Unified Document AST</text>
</g>
<g class="node" data-id="H" data-label="React Server Components" data-shape="rectangle">
  <rect x="242.2185" y="464.5" width="196.07799999999997" height="36.900000000000006" rx="0" ry="0" fill="var(--_node-fill)" stroke="var(--_node-stroke)" stroke-width="0.75" />
  <text x="340.2575" y="482.95" text-anchor="middle" font-size="13" font-weight="500" fill="var(--_text)" dy="4.55">React Server Components</text>
</g>
<g class="node" data-id="I" data-label="Hydrated Interactive UI" data-shape="rectangle">
  <rect x="252.5925" y="549.4" width="175.32999999999998" height="36.900000000000006" rx="0" ry="0" fill="var(--_node-fill)" stroke="var(--_node-stroke)" stroke-width="0.75" />
  <text x="340.2575" y="567.85" text-anchor="middle" font-size="13" font-weight="500" fill="var(--_text)" dy="4.55">Hydrated Interactive UI</text>
</g>
</svg></div><div class="mermaid-dark"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 656.062 626.3" width="656.062" height="626.3" style="--bg:#0d1117;--fg:#e6edf3;--line:#3d444d;--accent:#4493f8;--muted:#9198a1">
<style>
  @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&amp;display=swap');
  text { font-family: 'Inter', system-ui, sans-serif; }
  svg {
    /* Derived from --bg and --fg (overridable via --line, --accent, etc.) */
    --_text:          var(--fg);
    --_text-sec:      var(--muted, color-mix(in srgb, var(--fg) 60%, var(--bg)));
    --_text-muted:    var(--muted, color-mix(in srgb, var(--fg) 40%, var(--bg)));
    --_text-faint:    color-mix(in srgb, var(--fg) 25%, var(--bg));
    --_line:          var(--line, color-mix(in srgb, var(--fg) 50%, var(--bg)));
    --_arrow:         var(--accent, color-mix(in srgb, var(--fg) 85%, var(--bg)));
    --_node-fill:     var(--surface, color-mix(in srgb, var(--fg) 3%, var(--bg)));
    --_node-stroke:   var(--border, color-mix(in srgb, var(--fg) 20%, var(--bg)));
    --_group-fill:    var(--bg);
    --_group-hdr:     color-mix(in srgb, var(--fg) 5%, var(--bg));
    --_inner-stroke:  color-mix(in srgb, var(--fg) 12%, var(--bg));
    --_key-badge:     color-mix(in srgb, var(--fg) 10%, var(--bg));
  }
</style>
<defs>
  <marker id="arrowhead" markerWidth="8" markerHeight="5" refX="7" refY="2.5" orient="auto">
    <polygon points="0 0, 8 2.5, 0 5" fill="var(--_arrow)" stroke="var(--_arrow)" stroke-width="0.75" stroke-linejoin="round" />
  </marker>
  <marker id="arrowhead-start" markerWidth="8" markerHeight="5" refX="1" refY="2.5" orient="auto-start-reverse">
    <polygon points="8 0, 0 2.5, 8 5" fill="var(--_arrow)" stroke="var(--_arrow)" stroke-width="0.75" stroke-linejoin="round" />
  </marker>
</defs>
<polyline class="edge" data-from="A" data-to="B" data-style="solid" data-arrow-start="false" data-arrow-end="true" points="340.25750000000005,76.9 340.25750000000005,124.9" fill="none" stroke="var(--_line)" stroke-width="1" marker-end="url(#arrowhead)" />
<polyline class="edge" data-from="B" data-to="C" data-style="solid" data-arrow-start="false" data-arrow-end="true" points="340.25750000000005,161.8 340.25750000000005,209.8" fill="none" stroke="var(--_line)" stroke-width="1" marker-end="url(#arrowhead)" />
<polyline class="edge" data-from="C" data-to="D" data-style="solid" data-arrow-start="false" data-arrow-end="true" points="340.25750000000005,246.70000000000002 340.25750000000005,276.70000000000005 138.78,276.70000000000005 138.78,306.70000000000005" fill="none" stroke="var(--_line)" stroke-width="1" marker-end="url(#arrowhead)" />
<polyline class="edge" data-from="C" data-to="E" data-style="solid" data-arrow-start="false" data-arrow-end="true" points="340.25750000000005,246.70000000000002 340.25750000000005,276.70000000000005 340.2575,276.70000000000005 340.2575,306.70000000000005" fill="none" stroke="var(--_line)" stroke-width="1" marker-end="url(#arrowhead)" />
<polyline class="edge" data-from="C" data-to="F" data-style="solid" data-arrow-start="false" data-arrow-end="true" points="340.25750000000005,246.70000000000002 340.25750000000005,276.70000000000005 529.5085,276.70000000000005 529.5085,306.70000000000005" fill="none" stroke="var(--_line)" stroke-width="1" marker-end="url(#arrowhead)" />
<polyline class="edge" data-from="D" data-to="G" data-style="solid" data-arrow-start="false" data-arrow-end="true" points="138.78,343.6 138.78,361.6 340.2575,361.6 340.2575,379.6" fill="none" stroke="var(--_line)" stroke-width="1" marker-end="url(#arrowhead)" />
<polyline class="edge" data-from="E" data-to="G" data-style="solid" data-arrow-start="false" data-arrow-end="true" points="340.2575,343.6 340.2575,361.6 340.2575,361.6 340.2575,379.6" fill="none" stroke="var(--_line)" stroke-width="1" marker-end="url(#arrowhead)" />
<polyline class="edge" data-from="F" data-to="G" data-style="solid" data-arrow-start="false" data-arrow-end="true" points="529.5085,343.6 529.5085,361.6 340.2575,361.6 340.2575,379.6" fill="none" stroke="var(--_line)" stroke-width="1" marker-end="url(#arrowhead)" />
<polyline class="edge" data-from="G" data-to="H" data-style="solid" data-arrow-start="false" data-arrow-end="true" points="340.2575,416.5 340.2575,464.5" fill="none" stroke="var(--_line)" stroke-width="1" marker-end="url(#arrowhead)" />
<polyline class="edge" data-from="H" data-to="I" data-style="solid" data-arrow-start="false" data-arrow-end="true" points="340.2575,501.4 340.2575,549.4" fill="none" stroke="var(--_line)" stroke-width="1" marker-end="url(#arrowhead)" />
<g class="node" data-id="A" data-label="Markdown Source" data-shape="rectangle">
  <rect x="264.4485" y="40" width="151.618" height="36.900000000000006" rx="0" ry="0" fill="var(--_node-fill)" stroke="var(--_node-stroke)" stroke-width="0.75" />
  <text x="340.25750000000005" y="58.45" text-anchor="middle" font-size="13" font-weight="500" fill="var(--_text)" dy="4.55">Markdown Source</text>
</g>
<g class="node" data-id="B" data-label="Comark Parser" data-shape="rectangle">
  <rect x="273.711" y="124.9" width="133.09300000000002" height="36.900000000000006" rx="0" ry="0" fill="var(--_node-fill)" stroke="var(--_node-stroke)" stroke-width="0.75" />
  <text x="340.25750000000005" y="143.35000000000002" text-anchor="middle" font-size="13" font-weight="500" fill="var(--_text)" dy="4.55">Comark Parser</text>
</g>
<g class="node" data-id="C" data-label="Plugin Pipeline" data-shape="rectangle">
  <rect x="275.934" y="209.8" width="128.647" height="36.900000000000006" rx="0" ry="0" fill="var(--_node-fill)" stroke="var(--_node-stroke)" stroke-width="0.75" />
  <text x="340.25750000000005" y="228.25" text-anchor="middle" font-size="13" font-weight="500" fill="var(--_text)" dy="4.55">Plugin Pipeline</text>
</g>
<g class="node" data-id="D" data-label="Punctuation &amp; Typography" data-shape="rectangle">
  <rect x="40" y="306.70000000000005" width="197.56" height="36.900000000000006" rx="0" ry="0" fill="var(--_node-fill)" stroke="var(--_node-stroke)" stroke-width="0.75" />
  <text x="138.78" y="325.15000000000003" text-anchor="middle" font-size="13" font-weight="500" fill="var(--_text)" dy="4.55">Punctuation &amp; Typography</text>
</g>
<g class="node" data-id="E" data-label="Footnote Collector" data-shape="rectangle">
  <rect x="265.56" y="306.70000000000005" width="149.395" height="36.900000000000006" rx="0" ry="0" fill="var(--_node-fill)" stroke="var(--_node-stroke)" stroke-width="0.75" />
  <text x="340.2575" y="325.15000000000003" text-anchor="middle" font-size="13" font-weight="500" fill="var(--_text)" dy="4.55">Footnote Collector</text>
</g>
<g class="node" data-id="F" data-label="Mermaid Diagram AST" data-shape="rectangle">
  <rect x="442.95500000000004" y="306.70000000000005" width="173.10699999999997" height="36.900000000000006" rx="0" ry="0" fill="var(--_node-fill)" stroke="var(--_node-stroke)" stroke-width="0.75" />
  <text x="529.5085" y="325.15000000000003" text-anchor="middle" font-size="13" font-weight="500" fill="var(--_text)" dy="4.55">Mermaid Diagram AST</text>
</g>
<g class="node" data-id="G" data-label="Unified Document AST" data-shape="rectangle">
  <rect x="254.81550000000001" y="379.6" width="170.88399999999996" height="36.900000000000006" rx="0" ry="0" fill="var(--_node-fill)" stroke="var(--_node-stroke)" stroke-width="0.75" />
  <text x="340.2575" y="398.05" text-anchor="middle" font-size="13" font-weight="500" fill="var(--_text)" dy="4.55">Unified Document AST</text>
</g>
<g class="node" data-id="H" data-label="React Server Components" data-shape="rectangle">
  <rect x="242.2185" y="464.5" width="196.07799999999997" height="36.900000000000006" rx="0" ry="0" fill="var(--_node-fill)" stroke="var(--_node-stroke)" stroke-width="0.75" />
  <text x="340.2575" y="482.95" text-anchor="middle" font-size="13" font-weight="500" fill="var(--_text)" dy="4.55">React Server Components</text>
</g>
<g class="node" data-id="I" data-label="Hydrated Interactive UI" data-shape="rectangle">
  <rect x="252.5925" y="549.4" width="175.32999999999998" height="36.900000000000006" rx="0" ry="0" fill="var(--_node-fill)" stroke="var(--_node-stroke)" stroke-width="0.75" />
  <text x="340.2575" y="567.85" text-anchor="middle" font-size="13" font-weight="500" fill="var(--_text)" dy="4.55">Hydrated Interactive UI</text>
</g>
</svg></div></div><h2 id="typographic-refinements">Typographic Refinements</h2><p>With the typography plugin active, plain text punctuation is automatically converted into polished Unicode characters:</p><ul><li>“Smart quotes” and ‘nested quotes’ replace standard typewriter quotes.</li><li>En-dashes (2024–2026) and em-dashes—used for parenthetical thoughts—are converted seamlessly.</li><li>Trailing thoughts with ellipses… look balanced and intentional.</li><li>Symbols like ©, ®, ™, and ± 0.05 are normalized into standard glyphs.</li></ul><h2 id="how-ast-transformations-work">How AST Transformations Work</h2><ol><li><strong>Parse</strong>: Markdown text -&gt; <code>mdast</code> (Markdown Abstract Syntax Tree)</li><li><strong>Transform</strong>: Remark &amp; Comark plugins modify the tree in-flight</li><li><strong>Bridge</strong>: Nodes are mapped to custom React components<sup class="footnote-ref"><a href="#fn-beautiful-mermaid" id="fnref-beautiful-mermaid">[3]</a></sup></li><li><strong>Compile</strong>: RSC streams pristine markup directly to the client</li></ol><pre language="typescript" class="shiki shiki-themes night-owl-light vitesse-dark dark:vitesse-dark" style="background-color:#FBFBFB;color:#403f53;--shiki-dark-bg:#121212;--shiki-dark:#dbd7caee"><code class="language-typescript"><span class="line" style="display:inline"><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#4D9375;--shiki-dark-font-style:inherit">export</span><span style="color:#994CC3;--shiki-dark:#CB7676"> function</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit"> remarkCustomDirective</span><span style="color:#111111;--shiki-dark:#666666">()</span><span style="color:#403F53;--shiki-dark:#666666"> {</span></span>
<span class="line" style="display:inline"><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#4D9375;--shiki-dark-font-style:inherit">  return</span><span style="color:#111111;--shiki-dark:#666666"> (</span><span style="color:#403F53;--shiki-dark:#BD976A">tree</span><span style="color:#111111;--shiki-dark:#666666">)</span><span style="color:#994CC3;--shiki-dark:#666666"> =&gt;</span><span style="color:#403F53;--shiki-dark:#666666"> {</span></span>
<span class="line" style="display:inline"><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit">    visit</span><span style="color:#403F53;--shiki-dark:#666666">(</span><span style="color:#403F53;--shiki-dark:#BD976A">tree</span><span style="color:#5F7E97;--shiki-dark:#666666">,</span><span style="color:#111111;--shiki-dark:#666666"> (</span><span style="color:#403F53;--shiki-dark:#BD976A">node</span><span style="color:#111111;--shiki-dark:#666666">)</span><span style="color:#994CC3;--shiki-dark:#666666"> =&gt;</span><span style="color:#403F53;--shiki-dark:#666666"> {</span></span>
<span class="line" style="display:inline"><span style="color:#994CC3;--shiki-dark:#4D9375">      if</span><span style="color:#403F53;--shiki-dark:#666666"> (</span><span style="color:#403F53;--shiki-dark:#BD976A">node</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#666666;--shiki-dark-font-style:inherit">.</span><span style="color:#0C969B;--shiki-dark:#BD976A">type</span><span style="color:#994CC3;--shiki-dark:#CB7676"> ===</span><span style="color:#111111;--shiki-dark:#C98A7D77"> &#x27;</span><span style="color:#C96765;--shiki-dark:#C98A7D">textDirective</span><span style="color:#111111;--shiki-dark:#C98A7D77">&#x27;</span><span style="color:#994CC3;--shiki-dark:#CB7676"> &amp;&amp;</span><span style="color:#403F53;--shiki-dark:#BD976A"> node</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#666666;--shiki-dark-font-style:inherit">.</span><span style="color:#0C969B;--shiki-dark:#BD976A">name</span><span style="color:#994CC3;--shiki-dark:#CB7676"> ===</span><span style="color:#111111;--shiki-dark:#C98A7D77"> &#x27;</span><span style="color:#C96765;--shiki-dark:#C98A7D">badge</span><span style="color:#111111;--shiki-dark:#C98A7D77">&#x27;</span><span style="color:#403F53;--shiki-dark:#666666">)</span><span style="color:#403F53;--shiki-dark:#666666"> {</span></span>
<span class="line" style="display:inline"><span style="color:#403F53;--shiki-dark:#BD976A">        node</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#666666;--shiki-dark-font-style:inherit">.</span><span style="color:#0C969B;--shiki-dark:#BD976A">data</span><span style="color:#994CC3;--shiki-dark:#666666"> =</span><span style="color:#403F53;--shiki-dark:#666666"> {</span></span>
<span class="line" style="display:inline"><span style="color:#403F53;--shiki-dark:#B8A965">          hName</span><span style="color:#403F53;--shiki-dark:#666666">: </span><span style="color:#111111;--shiki-dark:#C98A7D77">&#x27;</span><span style="color:#C96765;--shiki-dark:#C98A7D">span</span><span style="color:#111111;--shiki-dark:#C98A7D77">&#x27;</span><span style="color:#5F7E97;--shiki-dark:#666666">,</span></span>
<span class="line" style="display:inline"><span style="color:#403F53;--shiki-dark:#B8A965">          hProperties</span><span style="color:#403F53;--shiki-dark:#666666">: { </span><span style="color:#403F53;--shiki-dark:#B8A965">className</span><span style="color:#403F53;--shiki-dark:#666666">: [</span><span style="color:#111111;--shiki-dark:#C98A7D77">&#x27;</span><span style="color:#C96765;--shiki-dark:#C98A7D">badge-item</span><span style="color:#111111;--shiki-dark:#C98A7D77">&#x27;</span><span style="color:#403F53;--shiki-dark:#666666">] }</span><span style="color:#5F7E97;--shiki-dark:#666666">,</span></span>
<span class="line" style="display:inline"><span style="color:#403F53;--shiki-dark:#666666">        }</span></span>
<span class="line" style="display:inline"><span style="color:#403F53;--shiki-dark:#666666">      }</span></span>
<span class="line" style="display:inline"><span style="color:#403F53;--shiki-dark:#666666">    })</span></span>
<span class="line" style="display:inline"><span style="color:#403F53;--shiki-dark:#666666">  }</span></span>
<span class="line" style="display:inline"><span style="color:#403F53;--shiki-dark:#666666">}</span></span></code></pre><section class="footnotes"><hr/><h2 id="footnotes">Footnotes</h2><ol class="footnotes-list"><li id="fn-ast">An Abstract Syntax Tree (AST) represents the hierarchical syntactic structure of source code or markup. <a href="#fnref-ast" class="footnote-backref">↩</a></li><li id="fn-plugins">Comark plugins run during the markdown-it parsing phase or directly transform the Comark AST nodes. <a href="#fnref-plugins" class="footnote-backref">↩</a></li><li id="fn-beautiful-mermaid">Diagrams are rendered using `beautiful-mermaid` with zero client-side DOM dependencies and native dark mode support. <a href="#fnref-beautiful-mermaid" class="footnote-backref">↩</a></li></ol></section></div>]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[Why Solving Type Challenges Makes You a Better Developer]]></title>
        <id>https://craigary.net/writings/why-type-challenges-matter</id>
        <link href="https://craigary.net/writings/why-type-challenges-matter"/>
        <updated>2024-10-05T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Sharpening your mental model of TypeScript through deliberate practice.]]></summary>
        <content type="html"><![CDATA[<div class="comark-content article-body"><p>Many developers view TypeScript merely as a linter with types. But beneath the surface lies a Turing-complete type system.</p><h2 id="unpacking-infer-and-recursion">Unpacking <code>infer</code> and Recursion</h2><p>Consider flattening an array type:</p><pre language="typescript" class="shiki shiki-themes night-owl-light vitesse-dark dark:vitesse-dark" style="background-color:#FBFBFB;color:#403f53;--shiki-dark-bg:#121212;--shiki-dark:#dbd7caee"><code class="language-typescript"><span class="line" style="display:inline"><span style="color:#994CC3;--shiki-dark:#CB7676">type</span><span style="color:#403F53;--shiki-dark:#5DA994"> Flatten</span><span style="color:#403F53;--shiki-dark:#666666">&lt;</span><span style="color:#111111;--shiki-dark:#5DA994">T</span><span style="color:#403F53;--shiki-dark:#666666">&gt;</span><span style="color:#994CC3;--shiki-dark:#666666"> =</span><span style="color:#111111;--shiki-dark:#5DA994"> T</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#CB7676;--shiki-dark-font-style:inherit"> extends</span><span style="color:#111111;--shiki-dark:#5DA994"> Array</span><span style="color:#403F53;--shiki-dark:#666666">&lt;</span><span style="color:#0C969B;--shiki-dark:#CB7676">infer</span><span style="color:#111111;--shiki-dark:#5DA994"> Item</span><span style="color:#403F53;--shiki-dark:#666666">&gt;</span><span style="color:#994CC3;--shiki-dark:#CB7676"> ?</span><span style="color:#111111;--shiki-dark:#5DA994"> Flatten</span><span style="color:#403F53;--shiki-dark:#666666">&lt;</span><span style="color:#111111;--shiki-dark:#5DA994">Item</span><span style="color:#403F53;--shiki-dark:#666666">&gt;</span><span style="color:#994CC3;--shiki-dark:#CB7676"> :</span><span style="color:#111111;--shiki-dark:#5DA994"> T</span></span>
<span class="line" style="display:inline"></span>
<span class="line" style="display:inline"><span style="color:#994CC3;--shiki-dark:#CB7676">type</span><span style="color:#403F53;--shiki-dark:#5DA994"> Test</span><span style="color:#994CC3;--shiki-dark:#666666"> =</span><span style="color:#111111;--shiki-dark:#5DA994"> Flatten</span><span style="color:#403F53;--shiki-dark:#666666">&lt;[</span><span style="color:#AA0982;--shiki-dark:#4C9A91">1</span><span style="color:#403F53;--shiki-dark:#666666">,</span><span style="color:#403F53;--shiki-dark:#666666"> [</span><span style="color:#AA0982;--shiki-dark:#4C9A91">2</span><span style="color:#403F53;--shiki-dark:#666666">,</span><span style="color:#403F53;--shiki-dark:#666666"> [</span><span style="color:#AA0982;--shiki-dark:#4C9A91">3</span><span style="color:#403F53;--shiki-dark:#666666">,</span><span style="color:#403F53;--shiki-dark:#666666"> [</span><span style="color:#AA0982;--shiki-dark:#4C9A91">4</span><span style="color:#403F53;--shiki-dark:#666666">]]]]&gt;</span><span style="color:#989FB1;--shiki-light-font-style:italic;--shiki-dark:#758575DD;--shiki-dark-font-style:inherit"> //</span><span style="color:#939DBB;--shiki-light-font-style:italic;--shiki-dark:#758575DD;--shiki-dark-font-style:inherit"> number</span></span></code></pre><p>Working through type puzzles forces you to understand variance, distribution, and assignability at a fundamental level.</p></div>]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[Silky Smooth Navigation with the View Transitions API]]></title>
        <id>https://craigary.net/writings/exploring-view-transitions-api</id>
        <link href="https://craigary.net/writings/exploring-view-transitions-api"/>
        <updated>2024-08-30T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Implementing seamless, native app-like page transitions on the web without heavyweight libraries.]]></summary>
        <content type="html"><![CDATA[<div class="comark-content article-body"><p>The View Transitions API is arguably the most impactful enhancement to web animation in a decade.</p><h2 id="the-basic-workflow">The Basic Workflow</h2><pre language="javascript" class="shiki shiki-themes night-owl-light vitesse-dark dark:vitesse-dark" style="background-color:#FBFBFB;color:#403f53;--shiki-dark-bg:#121212;--shiki-dark:#dbd7caee"><code class="language-javascript"><span class="line" style="display:inline"><span style="color:#994CC3;--shiki-dark:#4D9375">if</span><span style="color:#403F53;--shiki-dark:#666666"> (</span><span style="color:#994CC3;--shiki-dark:#CB7676">!</span><span style="color:#0C969B;--shiki-light-font-style:italic;--shiki-dark:#BD976A;--shiki-dark-font-style:inherit">document</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#666666;--shiki-dark-font-style:inherit">.</span><span style="color:#0C969B;--shiki-dark:#BD976A">startViewTransition</span><span style="color:#403F53;--shiki-dark:#666666">)</span><span style="color:#403F53;--shiki-dark:#666666"> {</span></span>
<span class="line" style="display:inline"><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit">  updateDOM</span><span style="color:#403F53;--shiki-dark:#666666">()</span></span>
<span class="line" style="display:inline"><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#4D9375;--shiki-dark-font-style:inherit">  return</span></span>
<span class="line" style="display:inline"><span style="color:#403F53;--shiki-dark:#666666">}</span></span>
<span class="line" style="display:inline"></span>
<span class="line" style="display:inline"><span style="color:#0C969B;--shiki-light-font-style:italic;--shiki-dark:#BD976A;--shiki-dark-font-style:inherit">document</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#666666;--shiki-dark-font-style:inherit">.</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit">startViewTransition</span><span style="color:#403F53;--shiki-dark:#666666">(</span><span style="color:#111111;--shiki-dark:#666666">()</span><span style="color:#994CC3;--shiki-dark:#666666"> =&gt;</span><span style="color:#403F53;--shiki-dark:#666666"> {</span></span>
<span class="line" style="display:inline"><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit">  updateDOM</span><span style="color:#403F53;--shiki-dark:#666666">()</span></span>
<span class="line" style="display:inline"><span style="color:#403F53;--shiki-dark:#666666">})</span></span></code></pre><p>The browser automatically captures snapshots of the previous state, prepares the incoming state, and blends them using customizable CSS keyframes:</p><pre language="css" class="shiki shiki-themes night-owl-light vitesse-dark dark:vitesse-dark" style="background-color:#FBFBFB;color:#403f53;--shiki-dark-bg:#121212;--shiki-dark:#dbd7caee"><code class="language-css"><span class="line" style="display:inline"><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#DBD7CAEE;--shiki-dark-font-style:inherit">::view-transition-old(root</span><span style="color:#403F53;--shiki-dark:#DBD7CAEE">),</span></span>
<span class="line" style="display:inline"><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#DBD7CAEE;--shiki-dark-font-style:inherit">::view-transition-new(root</span><span style="color:#403F53;--shiki-dark:#DBD7CAEE">) </span><span style="color:#403F53;--shiki-dark:#666666">{</span></span>
<span class="line" style="display:inline"><span style="color:#0C969B;--shiki-dark:#B8A965">  animation-duration</span><span style="color:#403F53;--shiki-dark:#666666">:</span><span style="color:#AA0982;--shiki-dark:#4C9A91"> 0.25</span><span style="color:#AA0982;--shiki-dark:#CB7676">s</span><span style="color:#403F53;--shiki-dark:#666666">;</span></span>
<span class="line" style="display:inline"><span style="color:#403F53;--shiki-dark:#666666">}</span></span></code></pre><p>::
::</p></div>]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[Visualizing the JavaScript Event Loop and Microtasks]]></title>
        <id>https://craigary.net/writings/understanding-javascript-event-loop</id>
        <link href="https://craigary.net/writings/understanding-javascript-event-loop"/>
        <updated>2024-07-12T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Demystifying execution contexts, the call stack, macrotasks, and promise resolution.]]></summary>
        <content type="html"><![CDATA[<div class="comark-content article-body"><p>Understanding the order of execution in JavaScript is critical when orchestrating async operations.</p><h2 id="task-queues-breakdown">Task Queues Breakdown</h2><ol><li><strong>Call Stack</strong>: Executes synchronous operations until empty.</li><li><strong>Microtask Queue</strong>: Resolved promises (<code>Promise.then</code>), <code>queueMicrotask</code>, and <code>MutationObserver</code>. Emptied completely after each stack frame.</li><li><strong>Macrotask Queue</strong>: <code>setTimeout</code>, <code>setInterval</code>, <code>setImmediate</code>, and I/O. Executed one per tick.</li></ol><pre language="javascript" class="shiki shiki-themes night-owl-light vitesse-dark dark:vitesse-dark" style="background-color:#FBFBFB;color:#403f53;--shiki-dark-bg:#121212;--shiki-dark:#dbd7caee"><code class="language-javascript"><span class="line" style="display:inline"><span style="color:#0C969B;--shiki-light-font-style:italic;--shiki-dark:#BD976A;--shiki-dark-font-style:inherit">console</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#666666;--shiki-dark-font-style:inherit">.</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit">log</span><span style="color:#403F53;--shiki-dark:#666666">(</span><span style="color:#111111;--shiki-dark:#C98A7D77">&#x27;</span><span style="color:#C96765;--shiki-dark:#C98A7D">1</span><span style="color:#111111;--shiki-dark:#C98A7D77">&#x27;</span><span style="color:#403F53;--shiki-dark:#666666">)</span></span>
<span class="line" style="display:inline"><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit">setTimeout</span><span style="color:#403F53;--shiki-dark:#666666">(</span><span style="color:#111111;--shiki-dark:#666666">()</span><span style="color:#994CC3;--shiki-dark:#666666"> =&gt;</span><span style="color:#0C969B;--shiki-light-font-style:italic;--shiki-dark:#BD976A;--shiki-dark-font-style:inherit"> console</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#666666;--shiki-dark-font-style:inherit">.</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit">log</span><span style="color:#403F53;--shiki-dark:#666666">(</span><span style="color:#111111;--shiki-dark:#C98A7D77">&#x27;</span><span style="color:#C96765;--shiki-dark:#C98A7D">2</span><span style="color:#111111;--shiki-dark:#C98A7D77">&#x27;</span><span style="color:#403F53;--shiki-dark:#666666">),</span><span style="color:#AA0982;--shiki-dark:#4C9A91"> 0</span><span style="color:#403F53;--shiki-dark:#666666">)</span></span>
<span class="line" style="display:inline"><span style="color:#4876D6;--shiki-dark:#B8A965">Promise</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#666666;--shiki-dark-font-style:inherit">.</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit">resolve</span><span style="color:#403F53;--shiki-dark:#666666">()</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#666666;--shiki-dark-font-style:inherit">.</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit">then</span><span style="color:#403F53;--shiki-dark:#666666">(</span><span style="color:#111111;--shiki-dark:#666666">()</span><span style="color:#994CC3;--shiki-dark:#666666"> =&gt;</span><span style="color:#0C969B;--shiki-light-font-style:italic;--shiki-dark:#BD976A;--shiki-dark-font-style:inherit"> console</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#666666;--shiki-dark-font-style:inherit">.</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit">log</span><span style="color:#403F53;--shiki-dark:#666666">(</span><span style="color:#111111;--shiki-dark:#C98A7D77">&#x27;</span><span style="color:#C96765;--shiki-dark:#C98A7D">3</span><span style="color:#111111;--shiki-dark:#C98A7D77">&#x27;</span><span style="color:#403F53;--shiki-dark:#666666">))</span></span>
<span class="line" style="display:inline"><span style="color:#0C969B;--shiki-light-font-style:italic;--shiki-dark:#BD976A;--shiki-dark-font-style:inherit">console</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#666666;--shiki-dark-font-style:inherit">.</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit">log</span><span style="color:#403F53;--shiki-dark:#666666">(</span><span style="color:#111111;--shiki-dark:#C98A7D77">&#x27;</span><span style="color:#C96765;--shiki-dark:#C98A7D">4</span><span style="color:#111111;--shiki-dark:#C98A7D77">&#x27;</span><span style="color:#403F53;--shiki-dark:#666666">)</span></span>
<span class="line" style="display:inline"></span>
<span class="line" style="display:inline"><span style="color:#989FB1;--shiki-light-font-style:italic;--shiki-dark:#758575DD;--shiki-dark-font-style:inherit">//</span><span style="color:#939DBB;--shiki-light-font-style:italic;--shiki-dark:#758575DD;--shiki-dark-font-style:inherit"> Output: 1, 4, 3, 2</span></span></code></pre></div>]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[Designing Accessible Component Systems from the Ground Up]]></title>
        <id>https://craigary.net/writings/designing-accessible-design-systems</id>
        <link href="https://craigary.net/writings/designing-accessible-design-systems"/>
        <updated>2024-05-25T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Why accessibility (a11y) should be a first-class citizen in your UI architecture.]]></summary>
        <content type="html"><![CDATA[<div class="comark-content article-body"><p>Accessibility is not a feature you bolt on right before launching; it is an architectural decision.</p><h2 id="core-tenets">Core Tenets</h2><ul><li><strong>Semantic HTML First</strong>: Always use native <code>&lt;button&gt;</code>, <code>&lt;nav&gt;</code>, and <code>&lt;dialog&gt;</code> elements when possible.</li><li><strong>Focus Rings</strong>: Never use <code>outline: none</code> without providing a distinct focus-visible replacement.</li><li><strong>Color Contrast</strong>: Verify standard 4.5:1 ratios for normal text against varying themes.</li></ul><pre language="html" class="shiki shiki-themes night-owl-light vitesse-dark dark:vitesse-dark" style="background-color:#FBFBFB;color:#403f53;--shiki-dark-bg:#121212;--shiki-dark:#dbd7caee"><code class="language-html"><span class="line" style="display:inline"><span style="color:#994CC3;--shiki-dark:#666666">&lt;</span><span style="color:#994CC3;--shiki-dark:#4D9375">button</span></span>
<span class="line" style="display:inline"><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#BD976A;--shiki-dark-font-style:inherit">  type</span><span style="color:#994CC3;--shiki-dark:#666666">=</span><span style="color:#111111;--shiki-dark:#C98A7D77">&quot;</span><span style="color:#C96765;--shiki-dark:#C98A7D">button</span><span style="color:#111111;--shiki-dark:#C98A7D77">&quot;</span></span>
<span class="line" style="display:inline"><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#BD976A;--shiki-dark-font-style:inherit">  aria-expanded</span><span style="color:#994CC3;--shiki-dark:#666666">=</span><span style="color:#111111;--shiki-dark:#C98A7D77">&quot;</span><span style="color:#C96765;--shiki-dark:#C98A7D">false</span><span style="color:#111111;--shiki-dark:#C98A7D77">&quot;</span></span>
<span class="line" style="display:inline"><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#BD976A;--shiki-dark-font-style:inherit">  aria-controls</span><span style="color:#994CC3;--shiki-dark:#666666">=</span><span style="color:#111111;--shiki-dark:#C98A7D77">&quot;</span><span style="color:#C96765;--shiki-dark:#C98A7D">menu-dropdown</span><span style="color:#111111;--shiki-dark:#C98A7D77">&quot;</span></span>
<span class="line" style="display:inline"><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#BD976A;--shiki-dark-font-style:inherit">  class</span><span style="color:#994CC3;--shiki-dark:#666666">=</span><span style="color:#111111;--shiki-dark:#C98A7D77">&quot;</span><span style="color:#C96765;--shiki-dark:#C98A7D">focus-visible:ring-2 focus-visible:ring-offset-2</span><span style="color:#111111;--shiki-dark:#C98A7D77">&quot;</span></span>
<span class="line" style="display:inline"><span style="color:#994CC3;--shiki-dark:#666666">&gt;</span></span>
<span class="line" style="display:inline"><span style="color:#403F53;--shiki-dark:#DBD7CAEE">  Menu Options</span></span>
<span class="line" style="display:inline"><span style="color:#994CC3;--shiki-dark:#666666">&lt;/</span><span style="color:#994CC3;--shiki-dark:#4D9375">button</span><span style="color:#994CC3;--shiki-dark:#666666">&gt;</span></span></code></pre></div>]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[Lessons Learned from Migrating Enterprise Apps from Webpack to Vite]]></title>
        <id>https://craigary.net/writings/from-webpack-to-vite</id>
        <link href="https://craigary.net/writings/from-webpack-to-vite"/>
        <updated>2024-04-02T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[A retrospective on migrating a 100k+ line codebase to modern ES module-driven tooling.]]></summary>
        <content type="html"><![CDATA[<div class="comark-content article-body"><p>Migrating legacy build systems requires meticulous planning, especially when handling non-standard imports, environment variables, and polyfills.</p><h2 id="the-strategy">The Strategy</h2><ol><li><strong>Audit Dependencies</strong>: Identify packages relying on Node.js globals (<code>process.env</code>, <code>Buffer</code>).</li><li><strong>Alias Cleanliness</strong>: Align TypeScript paths with Vite resolve aliases.</li><li><strong>Chunk Strategy</strong>: Fine-tune code splitting using <code>manualChunks</code> or Rolldown groups.</li></ol><p>The outcome: developer happiness surged, CI pipeline costs dropped, and HMR became instantaneous.</p></div>]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[Creative Coding: Generative Art with HTML Canvas]]></title>
        <id>https://craigary.net/writings/generative-art-canvas-experiments</id>
        <link href="https://craigary.net/writings/generative-art-canvas-experiments"/>
        <updated>2024-02-18T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Exploring algorithmic beauty, Perlin noise, and math-driven generative graphics in the browser.]]></summary>
        <content type="html"><![CDATA[<div class="comark-content article-body"><p>There is something deeply satisfying about writing mathematical formulas that blossom into generative artwork on an HTML5 canvas.</p><h2 id="flow-fields-and-perlin-noise">Flow Fields and Perlin Noise</h2><p>By computing directional angles based on 2D noise coordinates, particles trace organic, ribbon-like trajectories:</p><pre language="javascript" class="shiki shiki-themes night-owl-light vitesse-dark dark:vitesse-dark" style="background-color:#FBFBFB;color:#403f53;--shiki-dark-bg:#121212;--shiki-dark:#dbd7caee"><code class="language-javascript"><span class="line" style="display:inline"><span style="color:#994CC3;--shiki-dark:#CB7676">function</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit"> drawFlow</span><span style="color:#111111;--shiki-dark:#666666">(</span><span style="color:#403F53;--shiki-dark:#BD976A">ctx</span><span style="color:#403F53;--shiki-dark:#666666">,</span><span style="color:#403F53;--shiki-dark:#BD976A"> width</span><span style="color:#403F53;--shiki-dark:#666666">,</span><span style="color:#403F53;--shiki-dark:#BD976A"> height</span><span style="color:#111111;--shiki-dark:#666666">)</span><span style="color:#403F53;--shiki-dark:#666666"> {</span></span>
<span class="line" style="display:inline"><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#4D9375;--shiki-dark-font-style:inherit">  for</span><span style="color:#403F53;--shiki-dark:#666666"> (</span><span style="color:#994CC3;--shiki-dark:#CB7676">let</span><span style="color:#403F53;--shiki-dark:#BD976A"> x</span><span style="color:#994CC3;--shiki-dark:#666666"> =</span><span style="color:#AA0982;--shiki-dark:#4C9A91"> 0</span><span style="color:#403F53;--shiki-dark:#666666">;</span><span style="color:#403F53;--shiki-dark:#BD976A"> x</span><span style="color:#994CC3;--shiki-dark:#666666"> &lt;</span><span style="color:#403F53;--shiki-dark:#BD976A"> width</span><span style="color:#403F53;--shiki-dark:#666666">;</span><span style="color:#403F53;--shiki-dark:#BD976A"> x</span><span style="color:#994CC3;--shiki-dark:#CB7676"> +=</span><span style="color:#AA0982;--shiki-dark:#4C9A91"> 20</span><span style="color:#403F53;--shiki-dark:#666666">)</span><span style="color:#403F53;--shiki-dark:#666666"> {</span></span>
<span class="line" style="display:inline"><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#4D9375;--shiki-dark-font-style:inherit">    for</span><span style="color:#403F53;--shiki-dark:#666666"> (</span><span style="color:#994CC3;--shiki-dark:#CB7676">let</span><span style="color:#403F53;--shiki-dark:#BD976A"> y</span><span style="color:#994CC3;--shiki-dark:#666666"> =</span><span style="color:#AA0982;--shiki-dark:#4C9A91"> 0</span><span style="color:#403F53;--shiki-dark:#666666">;</span><span style="color:#403F53;--shiki-dark:#BD976A"> y</span><span style="color:#994CC3;--shiki-dark:#666666"> &lt;</span><span style="color:#403F53;--shiki-dark:#BD976A"> height</span><span style="color:#403F53;--shiki-dark:#666666">;</span><span style="color:#403F53;--shiki-dark:#BD976A"> y</span><span style="color:#994CC3;--shiki-dark:#CB7676"> +=</span><span style="color:#AA0982;--shiki-dark:#4C9A91"> 20</span><span style="color:#403F53;--shiki-dark:#666666">)</span><span style="color:#403F53;--shiki-dark:#666666"> {</span></span>
<span class="line" style="display:inline"><span style="color:#994CC3;--shiki-dark:#CB7676">      const</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#BD976A;--shiki-dark-font-style:inherit"> angle</span><span style="color:#994CC3;--shiki-dark:#666666"> =</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit"> noise</span><span style="color:#403F53;--shiki-dark:#666666">(</span><span style="color:#403F53;--shiki-dark:#BD976A">x</span><span style="color:#994CC3;--shiki-dark:#CB7676"> *</span><span style="color:#AA0982;--shiki-dark:#4C9A91"> 0.005</span><span style="color:#5F7E97;--shiki-dark:#666666">,</span><span style="color:#403F53;--shiki-dark:#BD976A"> y</span><span style="color:#994CC3;--shiki-dark:#CB7676"> *</span><span style="color:#AA0982;--shiki-dark:#4C9A91"> 0.005</span><span style="color:#403F53;--shiki-dark:#666666">)</span><span style="color:#994CC3;--shiki-dark:#CB7676"> *</span><span style="color:#0C969B;--shiki-light-font-style:italic;--shiki-dark:#BD976A;--shiki-dark-font-style:inherit"> Math</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#666666;--shiki-dark-font-style:inherit">.</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#BD976A;--shiki-dark-font-style:inherit">PI</span><span style="color:#994CC3;--shiki-dark:#CB7676"> *</span><span style="color:#AA0982;--shiki-dark:#4C9A91"> 2</span></span>
<span class="line" style="display:inline"><span style="color:#0C969B;--shiki-light-font-style:italic;--shiki-dark:#BD976A;--shiki-dark-font-style:inherit">      ctx</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#666666;--shiki-dark-font-style:inherit">.</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit">beginPath</span><span style="color:#403F53;--shiki-dark:#666666">()</span></span>
<span class="line" style="display:inline"><span style="color:#0C969B;--shiki-light-font-style:italic;--shiki-dark:#BD976A;--shiki-dark-font-style:inherit">      ctx</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#666666;--shiki-dark-font-style:inherit">.</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit">moveTo</span><span style="color:#403F53;--shiki-dark:#666666">(</span><span style="color:#403F53;--shiki-dark:#BD976A">x</span><span style="color:#5F7E97;--shiki-dark:#666666">,</span><span style="color:#403F53;--shiki-dark:#BD976A"> y</span><span style="color:#403F53;--shiki-dark:#666666">)</span></span>
<span class="line" style="display:inline"><span style="color:#0C969B;--shiki-light-font-style:italic;--shiki-dark:#BD976A;--shiki-dark-font-style:inherit">      ctx</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#666666;--shiki-dark-font-style:inherit">.</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit">lineTo</span><span style="color:#403F53;--shiki-dark:#666666">(</span><span style="color:#403F53;--shiki-dark:#BD976A">x</span><span style="color:#994CC3;--shiki-dark:#CB7676"> +</span><span style="color:#0C969B;--shiki-light-font-style:italic;--shiki-dark:#BD976A;--shiki-dark-font-style:inherit"> Math</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#666666;--shiki-dark-font-style:inherit">.</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit">cos</span><span style="color:#403F53;--shiki-dark:#666666">(</span><span style="color:#403F53;--shiki-dark:#BD976A">angle</span><span style="color:#403F53;--shiki-dark:#666666">)</span><span style="color:#994CC3;--shiki-dark:#CB7676"> *</span><span style="color:#AA0982;--shiki-dark:#4C9A91"> 15</span><span style="color:#5F7E97;--shiki-dark:#666666">,</span><span style="color:#403F53;--shiki-dark:#BD976A"> y</span><span style="color:#994CC3;--shiki-dark:#CB7676"> +</span><span style="color:#0C969B;--shiki-light-font-style:italic;--shiki-dark:#BD976A;--shiki-dark-font-style:inherit"> Math</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#666666;--shiki-dark-font-style:inherit">.</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit">sin</span><span style="color:#403F53;--shiki-dark:#666666">(</span><span style="color:#403F53;--shiki-dark:#BD976A">angle</span><span style="color:#403F53;--shiki-dark:#666666">)</span><span style="color:#994CC3;--shiki-dark:#CB7676"> *</span><span style="color:#AA0982;--shiki-dark:#4C9A91"> 15</span><span style="color:#403F53;--shiki-dark:#666666">)</span></span>
<span class="line" style="display:inline"><span style="color:#0C969B;--shiki-light-font-style:italic;--shiki-dark:#BD976A;--shiki-dark-font-style:inherit">      ctx</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#666666;--shiki-dark-font-style:inherit">.</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit">stroke</span><span style="color:#403F53;--shiki-dark:#666666">()</span></span>
<span class="line" style="display:inline"><span style="color:#403F53;--shiki-dark:#666666">    }</span></span>
<span class="line" style="display:inline"><span style="color:#403F53;--shiki-dark:#666666">  }</span></span>
<span class="line" style="display:inline"><span style="color:#403F53;--shiki-dark:#666666">}</span></span></code></pre><p>The interplay of randomness and structure produces endless variations.</p></div>]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[Demystifying Git: Trees, Blobs, and DAGs]]></title>
        <id>https://craigary.net/writings/mastering-git-internals</id>
        <link href="https://craigary.net/writings/mastering-git-internals"/>
        <updated>2024-01-15T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Peeking under the hood of Git object storage and Directed Acyclic Graphs.]]></summary>
        <content type="html"><![CDATA[<div class="comark-content article-body"><p>Git is fundamentally a content-addressable key-value store with a VCS interface layered on top.</p><h2 id="the-four-git-objects">The Four Git Objects</h2><ul><li><strong>Blob</strong>: Contains file data without metadata or filename.</li><li><strong>Tree</strong>: Represents a directory, linking filenames to blobs or child trees.</li><li><strong>Commit</strong>: Points to a top-level tree, author, timestamp, and parent commit(s).</li><li><strong>Annotated Tag</strong>: A permanent pointer to a specific commit with a message.</li></ul><p>Understanding this mental model demystifies commands like <code>git rebase</code> and <code>git cherry-pick</code>.</p></div>]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[High-Performance SVG Animations and Morphing]]></title>
        <id>https://craigary.net/writings/svg-animation-techniques</id>
        <link href="https://craigary.net/writings/svg-animation-techniques"/>
        <updated>2023-11-28T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Techniques for crafting buttery smooth vector animations with CSS and path interpolation.]]></summary>
        <content type="html"><![CDATA[<div class="comark-content article-body"><p>Vector graphics scale infinitely without resolution loss, making them ideal for hero graphics, icons, and interactive illustrations.</p><h2 id="stroke-drawing-animation">Stroke Drawing Animation</h2><p>Using <code>stroke-dasharray</code> and <code>stroke-dashoffset</code>:</p><pre language="css" class="shiki shiki-themes night-owl-light vitesse-dark dark:vitesse-dark" style="background-color:#FBFBFB;color:#403f53;--shiki-dark-bg:#121212;--shiki-dark:#dbd7caee"><code class="language-css"><span class="line" style="display:inline"><span style="color:#4876D6;--shiki-dark:#666666">.</span><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#BD976A;--shiki-dark-font-style:inherit">path-line</span><span style="color:#403F53;--shiki-dark:#666666"> {</span></span>
<span class="line" style="display:inline"><span style="color:#0C969B;--shiki-dark:#B8A965">  stroke-dasharray</span><span style="color:#403F53;--shiki-dark:#666666">:</span><span style="color:#AA0982;--shiki-dark:#4C9A91"> 1000</span><span style="color:#403F53;--shiki-dark:#666666">;</span></span>
<span class="line" style="display:inline"><span style="color:#0C969B;--shiki-dark:#B8A965">  stroke-dashoffset</span><span style="color:#403F53;--shiki-dark:#666666">:</span><span style="color:#AA0982;--shiki-dark:#4C9A91"> 1000</span><span style="color:#403F53;--shiki-dark:#666666">;</span></span>
<span class="line" style="display:inline"><span style="color:#0C969B;--shiki-dark:#B8A965">  animation</span><span style="color:#403F53;--shiki-dark:#666666">:</span><span style="color:#403F53;--shiki-dark:#DBD7CAEE"> draw </span><span style="color:#AA0982;--shiki-dark:#4C9A91">2</span><span style="color:#AA0982;--shiki-dark:#CB7676">s</span><span style="color:#C96765;--shiki-dark:#C99076"> ease-in-out</span><span style="color:#C96765;--shiki-dark:#C99076"> forwards</span><span style="color:#403F53;--shiki-dark:#666666">;</span></span>
<span class="line" style="display:inline"><span style="color:#403F53;--shiki-dark:#666666">}</span></span>
<span class="line" style="display:inline"></span>
<span class="line" style="display:inline"><span style="color:#994CC3;--shiki-dark:#666666">@</span><span style="color:#994CC3;--shiki-light-font-style:italic;--shiki-dark:#4D9375;--shiki-dark-font-style:inherit">keyframes</span><span style="color:#403F53;--shiki-dark:#BD976A"> draw</span><span style="color:#403F53;--shiki-dark:#666666"> {</span></span>
<span class="line" style="display:inline"><span style="color:#403F53;--shiki-dark:#80A665">  to</span><span style="color:#403F53;--shiki-dark:#666666"> {</span></span>
<span class="line" style="display:inline"><span style="color:#0C969B;--shiki-dark:#B8A965">    stroke-dashoffset</span><span style="color:#403F53;--shiki-dark:#666666">:</span><span style="color:#AA0982;--shiki-dark:#4C9A91"> 0</span><span style="color:#403F53;--shiki-dark:#666666">;</span></span>
<span class="line" style="display:inline"><span style="color:#403F53;--shiki-dark:#666666">  }</span></span>
<span class="line" style="display:inline"><span style="color:#403F53;--shiki-dark:#666666">}</span></span></code></pre><p>When properly optimized, SVG paths consume negligible CPU overhead while delivering crisp visuals on high-DPI displays.</p></div>]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[Edge Compute vs Traditional Serverless: An In-Depth Look]]></title>
        <id>https://craigary.net/writings/serverless-edge-compute</id>
        <link href="https://craigary.net/writings/serverless-edge-compute"/>
        <updated>2023-09-14T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Comparing cold starts, execution isolation, global distribution, and data locality.]]></summary>
        <content type="html"><![CDATA[<div class="comark-content article-body"><p>The word “serverless” has expanded from container-backed Lambdas to lightweight V8 isolates running on hundreds of worldwide points of presence.</p><h2 id="isolates-vs-containers">Isolates vs Containers</h2><table><thead><tr><th style="text-align:left">Metric</th><th style="text-align:left">V8 Isolates (Edge)</th><th style="text-align:left">Micro-VM (Lambda)</th></tr></thead><tbody><tr><td style="text-align:left">Cold Start</td><td style="text-align:left">&lt; 5ms</td><td style="text-align:left">200ms - 2s</td></tr><tr><td style="text-align:left">Memory Footprint</td><td style="text-align:left">~few MBs</td><td style="text-align:left">128MB+</td></tr><tr><td style="text-align:left">Node.js APIs</td><td style="text-align:left">Standard Web APIs</td><td style="text-align:left">Full Node.js Runtime</td></tr></tbody></table><p>Choosing between the two depends heavily on your database proximity and runtime dependency requirements.</p></div>]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[The Evolution of Zero-Runtime CSS in 2023]]></title>
        <id>https://craigary.net/writings/zero-runtime-css-evolution</id>
        <link href="https://craigary.net/writings/zero-runtime-css-evolution"/>
        <updated>2023-07-09T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[How modern compile-time CSS systems solved the runtime performance bottlenecks of CSS-in-JS.]]></summary>
        <content type="html"><![CDATA[<div class="comark-content article-body"><p>During the heyday of CSS-in-JS, developer ergonomics reigned supreme. However, as applications scaled, client-side runtime style injection led to notable main-thread contention.</p><h2 id="the-shift-to-static-extraction">The Shift to Static Extraction</h2><p>Compile-time extractors evaluate style declarations ahead of time:</p><ul><li>No runtime overhead.</li><li>Predictable specificity.</li><li>Full benefits of atomic deduplication.</li></ul><p>This paradigm shift paved the way for the sleek utility pipelines we enjoy today.</p></div>]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[Reflections on Four Years of Full-Time Open Source]]></title>
        <id>https://craigary.net/writings/open-source-sponsorship-journey</id>
        <link href="https://craigary.net/writings/open-source-sponsorship-journey"/>
        <updated>2023-04-20T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Personal observations on sustainability, community building, and creative freedom in open source.]]></summary>
        <content type="html"><![CDATA[<div class="comark-content article-body"><p>Maintaining open source software full-time is both one of the most rewarding and demanding pursuits in engineering.</p><h2 id="what-i-have-learned">What I Have Learned</h2><ol><li><strong>Saying “No” is a Superpower</strong>: Protecting scope is critical to long-term project viability.</li><li><strong>Documentation is as Important as Code</strong>: A brilliant feature that is poorly documented does not exist in users’ minds.</li><li><strong>Community Over Code</strong>: Fostering an inclusive, responsive community creates enduring momentum.</li></ol><p>Thank you to every sponsor, contributor, and user who makes this journey possible.</p></div>]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[Crafting Delightful Command-Line Experiences]]></title>
        <id>https://craigary.net/writings/crafting-delightful-cli-tools</id>
        <link href="https://craigary.net/writings/crafting-delightful-cli-tools"/>
        <updated>2023-02-10T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Principles for designing responsive, visually polished, and error-tolerant CLI applications.]]></summary>
        <content type="html"><![CDATA[<div class="comark-content article-body"><p>Command line tools do not have to be dry and cryptic. With modern terminal capabilities, you can build experiences that rival GUI apps.</p><h2 id="key-elements">Key Elements</h2><ul><li><strong>Clear Progress Feedback</strong>: Use elegant spinners and progress indicators for long-running steps.</li><li><strong>Actionable Error Messages</strong>: When a command fails, suggest the exact fix or alternative command.</li><li><strong>Graceful Cancellation</strong>: Always handle <code>SIGINT</code> cleanly.</li></ul><pre language="bash" class="shiki shiki-themes night-owl-light vitesse-dark dark:vitesse-dark" style="background-color:#FBFBFB;color:#403f53;--shiki-dark-bg:#121212;--shiki-dark:#dbd7caee"><code class="language-bash"><span class="line" style="display:inline"><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit">✔</span><span style="color:#4876D6;--shiki-dark:#C98A7D"> Project</span><span style="color:#4876D6;--shiki-dark:#C98A7D"> scaffolding</span><span style="color:#4876D6;--shiki-dark:#C98A7D"> completed</span></span>
<span class="line" style="display:inline"><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit">✔</span><span style="color:#4876D6;--shiki-dark:#C98A7D"> Dependencies</span><span style="color:#4876D6;--shiki-dark:#C98A7D"> installed</span><span style="color:#403F53;--shiki-dark:#DBD7CAEE"> (pnpm)</span></span>
<span class="line" style="display:inline"><span style="color:#4876D6;--shiki-light-font-style:italic;--shiki-dark:#80A665;--shiki-dark-font-style:inherit">➜</span><span style="color:#4876D6;--shiki-dark:#C98A7D"> Ready</span><span style="color:#4876D6;--shiki-dark:#C98A7D"> to</span><span style="color:#4876D6;--shiki-dark:#C98A7D"> develop:</span><span style="color:#4876D6;--shiki-dark:#C98A7D"> vp</span><span style="color:#4876D6;--shiki-dark:#C98A7D"> dev</span></span></code></pre></div>]]></content>
    </entry>
</feed>