{
    "version": "https://jsonfeed.org/version/1",
    "title": "Craig Hart",
    "home_page_url": "https://craigary.net",
    "feed_url": "https://craigary.net/feed.json",
    "description": "Just a human sitting in front of glowing glass.",
    "icon": "https://craigary.net/icon.svg",
    "author": {
        "name": "Craig Hart",
        "url": "https://craigary.net"
    },
    "items": [
        {
            "id": "https://craigary.net/writings/modern-web-development-2026",
            "content_html": "<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>\n<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>\n<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>\n<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>\n<span class=\"line\" style=\"display:inline\"><span style=\"color:#403F53;--shiki-dark:#666666\">  }</span></span>\n<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>\n<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>",
            "url": "https://craigary.net/writings/modern-web-development-2026",
            "title": "Modern Web Development in 2026",
            "summary": "Reflections on the state of full-stack TypeScript, React Server Components, and native tooling.",
            "date_modified": "2026-08-28T00:00:00.000Z"
        },
        {
            "id": "https://github.com/craigary/craigary",
            "content_html": "<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>",
            "url": "https://github.com/craigary/craigary",
            "title": "GitHub: Craigary Project",
            "summary": "External link redirecting to the official project repository.",
            "date_modified": "2026-08-01T00:00:00.000Z"
        },
        {
            "id": "https://craigary.net/writings/deep-dive-tanstack-start-rsc",
            "content_html": "<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>\n<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>\n<span class=\"line\" style=\"display:inline\"></span>\n<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>\n<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>\n<span class=\"line\" style=\"display:inline\"><span style=\"color:#994CC3;--shiki-dark:#666666\">}</span><span style=\"color:#403F53;--shiki-dark:#666666\">)</span></span>\n<span class=\"line\" style=\"display:inline\"></span>\n<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>\n<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>\n<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>\n<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>",
            "url": "https://craigary.net/writings/deep-dive-tanstack-start-rsc",
            "title": "Deep Dive into TanStack Start & React Server Components",
            "summary": "How TanStack Start leverages RSC for optimal streaming performance and developer ergonomics.",
            "date_modified": "2026-07-14T00:00:00.000Z"
        },
        {
            "id": "https://craigary.net/writings/building-with-vite-plus",
            "content_html": "<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>\n<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>\n<span class=\"line\" style=\"display:inline\"></span>\n<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>\n<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>",
            "url": "https://craigary.net/writings/building-with-vite-plus",
            "title": "Building Lightning Fast Apps with Vite+",
            "summary": "Exploring Vite+, the unified toolchain combining Vite, Rolldown, Vitest, and Oxlint.",
            "date_modified": "2026-06-02T00:00:00.000Z"
        },
        {
            "id": "https://craigary.net/writings/migrating-to-tailwind-v4",
            "content_html": "<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>\n<span class=\"line\" style=\"display:inline\"></span>\n<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>\n<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>\n<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>\n<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>\n<span class=\"line\" style=\"display:inline\"><span style=\"color:#403F53;--shiki-dark:#666666\">}</span></span>\n<span class=\"line\" style=\"display:inline\"></span>\n<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>\n<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>\n<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>",
            "url": "https://craigary.net/writings/migrating-to-tailwind-v4",
            "title": "Migrating Large Codebases to Tailwind CSS v4",
            "summary": "Hands-on experience and gotchas when upgrading complex design systems to Tailwind CSS v4.",
            "date_modified": "2026-04-18T00:00:00.000Z"
        },
        {
            "id": "https://craigary.net/writings/shiki-syntax-highlighting-at-scale",
            "content_html": "<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>\n<span class=\"line\" style=\"display:inline\"></span>\n<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>\n<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>\n<span class=\"line\" style=\"display:inline\"><span style=\"color:#403F53;--shiki-dark:#666666\">  {</span></span>\n<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>\n<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>\n<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>\n<span class=\"line\" style=\"display:inline\"><span style=\"color:#403F53;--shiki-dark:#666666\">    },</span></span>\n<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>\n<span class=\"line\" style=\"display:inline\"><span style=\"color:#403F53;--shiki-dark:#666666\">  },</span></span>\n<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>\n<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>\n<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>\n<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>\n<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>",
            "url": "https://craigary.net/writings/shiki-syntax-highlighting-at-scale",
            "title": "Shiki Syntax Highlighting at Scale",
            "summary": "How to achieve ultra-fast server-side code highlighting with dual theme support and zero client JS.",
            "date_modified": "2026-03-05T00:00:00.000Z"
        },
        {
            "id": "https://craigary.net/writings/state-of-typescript-2026",
            "content_html": "<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>\n<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>\n<span class=\"line\" style=\"display:inline\"></span>\n<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>\n<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>\n<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>\n<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>\n<span class=\"line\" style=\"display:inline\"></span>\n<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>\n<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>",
            "url": "https://craigary.net/writings/state-of-typescript-2026",
            "title": "The State of TypeScript in 2026",
            "summary": "An overview of newer TypeScript features, type-level programming, and best practices.",
            "date_modified": "2026-01-22T00:00:00.000Z"
        },
        {
            "id": "https://craigary.net/writings/mastering-content-collections",
            "content_html": "<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>\n<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>\n<span class=\"line\" style=\"display:inline\"></span>\n<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>\n<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>\n<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>\n<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>\n<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>\n<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>\n<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>\n<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>\n<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>\n<span class=\"line\" style=\"display:inline\"><span style=\"color:#994CC3;--shiki-dark:#666666\">}</span><span style=\"color:#403F53;--shiki-dark:#666666\">)</span></span>\n<span class=\"line\" style=\"display:inline\"></span>\n<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>",
            "url": "https://craigary.net/writings/mastering-content-collections",
            "title": "Type-Safe Content Pipelines with Content Collections",
            "summary": "How Content Collections bridges frontmatter validation and full MDX support with Zod schemas.",
            "date_modified": "2025-12-10T00:00:00.000Z"
        },
        {
            "id": "https://craigary.net/writings/rust-tooling-for-javascript",
            "content_html": "<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>\n<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>\n<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>\n<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>\n<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>",
            "url": "https://craigary.net/writings/rust-tooling-for-javascript",
            "title": "The Rise of Rust Tooling in the JavaScript Ecosystem",
            "summary": "Examining why Rust has become the engine of modern JavaScript tooling.",
            "date_modified": "2025-11-04T00:00:00.000Z"
        },
        {
            "id": "https://craigary.net/writings/cloudflare-workers-rsc",
            "content_html": "<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>\n<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>\n<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>\n<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>\n<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>\n<span class=\"line\" style=\"display:inline\"><span style=\"color:#403F53;--shiki-dark:#666666\">    })</span></span>\n<span class=\"line\" style=\"display:inline\"><span style=\"color:#403F53;--shiki-dark:#666666\">  },</span></span>\n<span class=\"line\" style=\"display:inline\"><span style=\"color:#403F53;--shiki-dark:#666666\">}</span></span></code></pre></div>",
            "url": "https://craigary.net/writings/cloudflare-workers-rsc",
            "title": "Deploying Full-Stack RSC to Cloudflare Workers",
            "summary": "Architecting ultra-low latency applications using React Server Components on edge runtimes.",
            "date_modified": "2025-09-19T00:00:00.000Z"
        },
        {
            "id": "https://craigary.net/writings/design-engineering-philosophy",
            "content_html": "<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>",
            "url": "https://craigary.net/writings/design-engineering-philosophy",
            "title": "Crafting Interfaces: My Design Engineering Philosophy",
            "summary": "The intersection of visual aesthetics, interaction fidelity, and systematic code design.",
            "date_modified": "2025-08-01T00:00:00.000Z"
        },
        {
            "id": "https://craigary.net/writings/optimizing-web-vitals",
            "content_html": "<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>\n<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>\n<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>\n<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>\n<span class=\"line\" style=\"display:inline\"><span style=\"color:#403F53;--shiki-dark:#666666\">  })</span></span>\n<span class=\"line\" style=\"display:inline\"><span style=\"color:#403F53;--shiki-dark:#666666\">}</span></span>\n<span class=\"line\" style=\"display:inline\"></span>\n<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>\n<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>\n<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>\n<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>",
            "url": "https://craigary.net/writings/optimizing-web-vitals",
            "title": "Practical Guide to Optimizing Core Web Vitals (INP & LCP)",
            "summary": "Actionable techniques to boost your site’s Interaction to Next Paint and Largest Contentful Paint.",
            "date_modified": "2025-06-15T00:00:00.000Z"
        },
        {
            "id": "https://craigary.net/writings/monorepo-best-practices",
            "content_html": "<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>\n<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>\n<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>\n<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>",
            "url": "https://craigary.net/writings/monorepo-best-practices",
            "title": "Modern Monorepo Architecture with Pnpm Workspaces",
            "summary": "Structuring scalable TypeScript monorepos with strict dependency isolation and fast caching.",
            "date_modified": "2025-05-11T00:00:00.000Z"
        },
        {
            "id": "https://craigary.net/writings/future-of-css-nesting",
            "content_html": "<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>\n<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>\n<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>\n<span class=\"line\" style=\"display:inline\"></span>\n<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>\n<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>\n<span class=\"line\" style=\"display:inline\"><span style=\"color:#403F53;--shiki-dark:#666666\">  }</span></span>\n<span class=\"line\" style=\"display:inline\"></span>\n<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>\n<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>\n<span class=\"line\" style=\"display:inline\"><span style=\"color:#403F53;--shiki-dark:#666666\">  }</span></span>\n<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>\n<span class=\"line\" style=\"display:inline\"></span>\n<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>\n<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>\n<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>\n<span class=\"line\" style=\"display:inline\"><span style=\"color:#403F53;--shiki-dark:#666666\">  }</span></span>\n<span class=\"line\" style=\"display:inline\"><span style=\"color:#403F53;--shiki-dark:#666666\">}</span></span></code></pre></div>",
            "url": "https://craigary.net/writings/future-of-css-nesting",
            "title": "Modern CSS: Native Nesting, Cascade Layers & Container Queries",
            "summary": "How standard CSS has rendered many preprocessors obsolete with native features.",
            "date_modified": "2025-03-27T00:00:00.000Z"
        },
        {
            "id": "https://craigary.net/writings/effective-vitest-patterns",
            "content_html": "<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>\n<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>\n<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>\n<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>\n<span class=\"line\" style=\"display:inline\"></span>\n<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>\n<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>\n<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>\n<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>\n<span class=\"line\" style=\"display:inline\"></span>\n<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>\n<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>\n<span class=\"line\" style=\"display:inline\"><span style=\"color:#403F53;--shiki-dark:#666666\">  })</span></span>\n<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>",
            "url": "https://craigary.net/writings/effective-vitest-patterns",
            "title": "Effective Vitest Patterns for Component and Integration Testing",
            "summary": "Writing fast, reliable, and expressive tests with Vitest and Testing Library.",
            "date_modified": "2025-02-14T00:00:00.000Z"
        },
        {
            "id": "https://craigary.net/writings/micro-interactions-in-web-ui",
            "content_html": "<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>",
            "url": "https://craigary.net/writings/micro-interactions-in-web-ui",
            "title": "The Subtle Art of Micro-interactions in Web UI",
            "summary": "Elevating digital products with purposeful animations, tactile feedback, and sensory delight.",
            "date_modified": "2025-01-08T00:00:00.000Z"
        },
        {
            "id": "https://craigary.net/writings/building-extensible-markdown-pipelines",
            "content_html": "<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\">\n<style>\n  @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&amp;display=swap');\n  text { font-family: 'Inter', system-ui, sans-serif; }\n  svg {\n    /* Derived from --bg and --fg (overridable via --line, --accent, etc.) */\n    --_text:          var(--fg);\n    --_text-sec:      var(--muted, color-mix(in srgb, var(--fg) 60%, var(--bg)));\n    --_text-muted:    var(--muted, color-mix(in srgb, var(--fg) 40%, var(--bg)));\n    --_text-faint:    color-mix(in srgb, var(--fg) 25%, var(--bg));\n    --_line:          var(--line, color-mix(in srgb, var(--fg) 50%, var(--bg)));\n    --_arrow:         var(--accent, color-mix(in srgb, var(--fg) 85%, var(--bg)));\n    --_node-fill:     var(--surface, color-mix(in srgb, var(--fg) 3%, var(--bg)));\n    --_node-stroke:   var(--border, color-mix(in srgb, var(--fg) 20%, var(--bg)));\n    --_group-fill:    var(--bg);\n    --_group-hdr:     color-mix(in srgb, var(--fg) 5%, var(--bg));\n    --_inner-stroke:  color-mix(in srgb, var(--fg) 12%, var(--bg));\n    --_key-badge:     color-mix(in srgb, var(--fg) 10%, var(--bg));\n  }\n</style>\n<defs>\n  <marker id=\"arrowhead\" markerWidth=\"8\" markerHeight=\"5\" refX=\"7\" refY=\"2.5\" orient=\"auto\">\n    <polygon points=\"0 0, 8 2.5, 0 5\" fill=\"var(--_arrow)\" stroke=\"var(--_arrow)\" stroke-width=\"0.75\" stroke-linejoin=\"round\" />\n  </marker>\n  <marker id=\"arrowhead-start\" markerWidth=\"8\" markerHeight=\"5\" refX=\"1\" refY=\"2.5\" orient=\"auto-start-reverse\">\n    <polygon points=\"8 0, 0 2.5, 8 5\" fill=\"var(--_arrow)\" stroke=\"var(--_arrow)\" stroke-width=\"0.75\" stroke-linejoin=\"round\" />\n  </marker>\n</defs>\n<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)\" />\n<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)\" />\n<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)\" />\n<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)\" />\n<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)\" />\n<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)\" />\n<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)\" />\n<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)\" />\n<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)\" />\n<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)\" />\n<g class=\"node\" data-id=\"A\" data-label=\"Markdown Source\" data-shape=\"rectangle\">\n  <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\" />\n  <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>\n</g>\n<g class=\"node\" data-id=\"B\" data-label=\"Comark Parser\" data-shape=\"rectangle\">\n  <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\" />\n  <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>\n</g>\n<g class=\"node\" data-id=\"C\" data-label=\"Plugin Pipeline\" data-shape=\"rectangle\">\n  <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\" />\n  <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>\n</g>\n<g class=\"node\" data-id=\"D\" data-label=\"Punctuation &amp; Typography\" data-shape=\"rectangle\">\n  <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\" />\n  <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>\n</g>\n<g class=\"node\" data-id=\"E\" data-label=\"Footnote Collector\" data-shape=\"rectangle\">\n  <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\" />\n  <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>\n</g>\n<g class=\"node\" data-id=\"F\" data-label=\"Mermaid Diagram AST\" data-shape=\"rectangle\">\n  <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\" />\n  <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>\n</g>\n<g class=\"node\" data-id=\"G\" data-label=\"Unified Document AST\" data-shape=\"rectangle\">\n  <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\" />\n  <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>\n</g>\n<g class=\"node\" data-id=\"H\" data-label=\"React Server Components\" data-shape=\"rectangle\">\n  <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\" />\n  <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>\n</g>\n<g class=\"node\" data-id=\"I\" data-label=\"Hydrated Interactive UI\" data-shape=\"rectangle\">\n  <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\" />\n  <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>\n</g>\n</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\">\n<style>\n  @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&amp;display=swap');\n  text { font-family: 'Inter', system-ui, sans-serif; }\n  svg {\n    /* Derived from --bg and --fg (overridable via --line, --accent, etc.) */\n    --_text:          var(--fg);\n    --_text-sec:      var(--muted, color-mix(in srgb, var(--fg) 60%, var(--bg)));\n    --_text-muted:    var(--muted, color-mix(in srgb, var(--fg) 40%, var(--bg)));\n    --_text-faint:    color-mix(in srgb, var(--fg) 25%, var(--bg));\n    --_line:          var(--line, color-mix(in srgb, var(--fg) 50%, var(--bg)));\n    --_arrow:         var(--accent, color-mix(in srgb, var(--fg) 85%, var(--bg)));\n    --_node-fill:     var(--surface, color-mix(in srgb, var(--fg) 3%, var(--bg)));\n    --_node-stroke:   var(--border, color-mix(in srgb, var(--fg) 20%, var(--bg)));\n    --_group-fill:    var(--bg);\n    --_group-hdr:     color-mix(in srgb, var(--fg) 5%, var(--bg));\n    --_inner-stroke:  color-mix(in srgb, var(--fg) 12%, var(--bg));\n    --_key-badge:     color-mix(in srgb, var(--fg) 10%, var(--bg));\n  }\n</style>\n<defs>\n  <marker id=\"arrowhead\" markerWidth=\"8\" markerHeight=\"5\" refX=\"7\" refY=\"2.5\" orient=\"auto\">\n    <polygon points=\"0 0, 8 2.5, 0 5\" fill=\"var(--_arrow)\" stroke=\"var(--_arrow)\" stroke-width=\"0.75\" stroke-linejoin=\"round\" />\n  </marker>\n  <marker id=\"arrowhead-start\" markerWidth=\"8\" markerHeight=\"5\" refX=\"1\" refY=\"2.5\" orient=\"auto-start-reverse\">\n    <polygon points=\"8 0, 0 2.5, 8 5\" fill=\"var(--_arrow)\" stroke=\"var(--_arrow)\" stroke-width=\"0.75\" stroke-linejoin=\"round\" />\n  </marker>\n</defs>\n<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)\" />\n<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)\" />\n<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)\" />\n<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)\" />\n<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)\" />\n<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)\" />\n<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)\" />\n<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)\" />\n<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)\" />\n<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)\" />\n<g class=\"node\" data-id=\"A\" data-label=\"Markdown Source\" data-shape=\"rectangle\">\n  <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\" />\n  <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>\n</g>\n<g class=\"node\" data-id=\"B\" data-label=\"Comark Parser\" data-shape=\"rectangle\">\n  <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\" />\n  <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>\n</g>\n<g class=\"node\" data-id=\"C\" data-label=\"Plugin Pipeline\" data-shape=\"rectangle\">\n  <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\" />\n  <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>\n</g>\n<g class=\"node\" data-id=\"D\" data-label=\"Punctuation &amp; Typography\" data-shape=\"rectangle\">\n  <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\" />\n  <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>\n</g>\n<g class=\"node\" data-id=\"E\" data-label=\"Footnote Collector\" data-shape=\"rectangle\">\n  <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\" />\n  <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>\n</g>\n<g class=\"node\" data-id=\"F\" data-label=\"Mermaid Diagram AST\" data-shape=\"rectangle\">\n  <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\" />\n  <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>\n</g>\n<g class=\"node\" data-id=\"G\" data-label=\"Unified Document AST\" data-shape=\"rectangle\">\n  <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\" />\n  <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>\n</g>\n<g class=\"node\" data-id=\"H\" data-label=\"React Server Components\" data-shape=\"rectangle\">\n  <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\" />\n  <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>\n</g>\n<g class=\"node\" data-id=\"I\" data-label=\"Hydrated Interactive UI\" data-shape=\"rectangle\">\n  <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\" />\n  <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>\n</g>\n</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>\n<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>\n<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>\n<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>\n<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>\n<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>\n<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>\n<span class=\"line\" style=\"display:inline\"><span style=\"color:#403F53;--shiki-dark:#666666\">        }</span></span>\n<span class=\"line\" style=\"display:inline\"><span style=\"color:#403F53;--shiki-dark:#666666\">      }</span></span>\n<span class=\"line\" style=\"display:inline\"><span style=\"color:#403F53;--shiki-dark:#666666\">    })</span></span>\n<span class=\"line\" style=\"display:inline\"><span style=\"color:#403F53;--shiki-dark:#666666\">  }</span></span>\n<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>",
            "url": "https://craigary.net/writings/building-extensible-markdown-pipelines",
            "title": "Building Extensible Markdown & MDX Pipelines",
            "summary": "A deep dive into Unified, Remark, and Rehype plugins for custom documentation systems.",
            "date_modified": "2024-11-20T00:00:00.000Z"
        },
        {
            "id": "https://craigary.net/writings/why-type-challenges-matter",
            "content_html": "<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>\n<span class=\"line\" style=\"display:inline\"></span>\n<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>",
            "url": "https://craigary.net/writings/why-type-challenges-matter",
            "title": "Why Solving Type Challenges Makes You a Better Developer",
            "summary": "Sharpening your mental model of TypeScript through deliberate practice.",
            "date_modified": "2024-10-05T00:00:00.000Z"
        },
        {
            "id": "https://craigary.net/writings/exploring-view-transitions-api",
            "content_html": "<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>\n<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>\n<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>\n<span class=\"line\" style=\"display:inline\"><span style=\"color:#403F53;--shiki-dark:#666666\">}</span></span>\n<span class=\"line\" style=\"display:inline\"></span>\n<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>\n<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>\n<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>\n<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>\n<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>\n<span class=\"line\" style=\"display:inline\"><span style=\"color:#403F53;--shiki-dark:#666666\">}</span></span></code></pre><p>::\n::</p></div>",
            "url": "https://craigary.net/writings/exploring-view-transitions-api",
            "title": "Silky Smooth Navigation with the View Transitions API",
            "summary": "Implementing seamless, native app-like page transitions on the web without heavyweight libraries.",
            "date_modified": "2024-08-30T00:00:00.000Z"
        },
        {
            "id": "https://craigary.net/writings/understanding-javascript-event-loop",
            "content_html": "<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>\n<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>\n<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>\n<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>\n<span class=\"line\" style=\"display:inline\"></span>\n<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>",
            "url": "https://craigary.net/writings/understanding-javascript-event-loop",
            "title": "Visualizing the JavaScript Event Loop and Microtasks",
            "summary": "Demystifying execution contexts, the call stack, macrotasks, and promise resolution.",
            "date_modified": "2024-07-12T00:00:00.000Z"
        },
        {
            "id": "https://craigary.net/writings/designing-accessible-design-systems",
            "content_html": "<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>\n<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>\n<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>\n<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>\n<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>\n<span class=\"line\" style=\"display:inline\"><span style=\"color:#994CC3;--shiki-dark:#666666\">&gt;</span></span>\n<span class=\"line\" style=\"display:inline\"><span style=\"color:#403F53;--shiki-dark:#DBD7CAEE\">  Menu Options</span></span>\n<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>",
            "url": "https://craigary.net/writings/designing-accessible-design-systems",
            "title": "Designing Accessible Component Systems from the Ground Up",
            "summary": "Why accessibility (a11y) should be a first-class citizen in your UI architecture.",
            "date_modified": "2024-05-25T00:00:00.000Z"
        },
        {
            "id": "https://craigary.net/writings/from-webpack-to-vite",
            "content_html": "<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>",
            "url": "https://craigary.net/writings/from-webpack-to-vite",
            "title": "Lessons Learned from Migrating Enterprise Apps from Webpack to Vite",
            "summary": "A retrospective on migrating a 100k+ line codebase to modern ES module-driven tooling.",
            "date_modified": "2024-04-02T00:00:00.000Z"
        },
        {
            "id": "https://craigary.net/writings/generative-art-canvas-experiments",
            "content_html": "<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>\n<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>\n<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>\n<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>\n<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>\n<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>\n<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>\n<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>\n<span class=\"line\" style=\"display:inline\"><span style=\"color:#403F53;--shiki-dark:#666666\">    }</span></span>\n<span class=\"line\" style=\"display:inline\"><span style=\"color:#403F53;--shiki-dark:#666666\">  }</span></span>\n<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>",
            "url": "https://craigary.net/writings/generative-art-canvas-experiments",
            "title": "Creative Coding: Generative Art with HTML Canvas",
            "summary": "Exploring algorithmic beauty, Perlin noise, and math-driven generative graphics in the browser.",
            "date_modified": "2024-02-18T00:00:00.000Z"
        },
        {
            "id": "https://craigary.net/writings/mastering-git-internals",
            "content_html": "<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>",
            "url": "https://craigary.net/writings/mastering-git-internals",
            "title": "Demystifying Git: Trees, Blobs, and DAGs",
            "summary": "Peeking under the hood of Git object storage and Directed Acyclic Graphs.",
            "date_modified": "2024-01-15T00:00:00.000Z"
        },
        {
            "id": "https://craigary.net/writings/svg-animation-techniques",
            "content_html": "<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>\n<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>\n<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>\n<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>\n<span class=\"line\" style=\"display:inline\"><span style=\"color:#403F53;--shiki-dark:#666666\">}</span></span>\n<span class=\"line\" style=\"display:inline\"></span>\n<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>\n<span class=\"line\" style=\"display:inline\"><span style=\"color:#403F53;--shiki-dark:#80A665\">  to</span><span style=\"color:#403F53;--shiki-dark:#666666\"> {</span></span>\n<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>\n<span class=\"line\" style=\"display:inline\"><span style=\"color:#403F53;--shiki-dark:#666666\">  }</span></span>\n<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>",
            "url": "https://craigary.net/writings/svg-animation-techniques",
            "title": "High-Performance SVG Animations and Morphing",
            "summary": "Techniques for crafting buttery smooth vector animations with CSS and path interpolation.",
            "date_modified": "2023-11-28T00:00:00.000Z"
        },
        {
            "id": "https://craigary.net/writings/serverless-edge-compute",
            "content_html": "<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>",
            "url": "https://craigary.net/writings/serverless-edge-compute",
            "title": "Edge Compute vs Traditional Serverless: An In-Depth Look",
            "summary": "Comparing cold starts, execution isolation, global distribution, and data locality.",
            "date_modified": "2023-09-14T00:00:00.000Z"
        },
        {
            "id": "https://craigary.net/writings/zero-runtime-css-evolution",
            "content_html": "<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>",
            "url": "https://craigary.net/writings/zero-runtime-css-evolution",
            "title": "The Evolution of Zero-Runtime CSS in 2023",
            "summary": "How modern compile-time CSS systems solved the runtime performance bottlenecks of CSS-in-JS.",
            "date_modified": "2023-07-09T00:00:00.000Z"
        },
        {
            "id": "https://craigary.net/writings/open-source-sponsorship-journey",
            "content_html": "<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>",
            "url": "https://craigary.net/writings/open-source-sponsorship-journey",
            "title": "Reflections on Four Years of Full-Time Open Source",
            "summary": "Personal observations on sustainability, community building, and creative freedom in open source.",
            "date_modified": "2023-04-20T00:00:00.000Z"
        },
        {
            "id": "https://craigary.net/writings/crafting-delightful-cli-tools",
            "content_html": "<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>\n<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>\n<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>",
            "url": "https://craigary.net/writings/crafting-delightful-cli-tools",
            "title": "Crafting Delightful Command-Line Experiences",
            "summary": "Principles for designing responsive, visually polished, and error-tolerant CLI applications.",
            "date_modified": "2023-02-10T00:00:00.000Z"
        }
    ]
}