<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:blog="https://jonesrussell.github.io/blog/ns"><channel><title>Shortcodes on Web Developer Blog</title><link>https://jonesrussell.github.io/blog/tags/shortcodes/</link><description>Recent content in Shortcodes on Web Developer Blog</description><image><title>Web Developer Blog</title><url>https://jonesrussell.github.io/blog/images/og-default.png</url><link>https://jonesrussell.github.io/blog/images/og-default.png</link></image><generator>Hugo -- 0.160.1</generator><language>en-us</language><lastBuildDate>Wed, 08 Apr 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://jonesrussell.github.io/blog/tags/shortcodes/feed.xml" rel="self" type="application/rss+xml"/><item><title>Hugo blog shortcodes: adding a visual component system to PaperMod</title><link>https://jonesrussell.github.io/blog/hugo-blog-visual-shortcodes/</link><pubDate>Wed, 08 Apr 2026 00:00:00 +0000</pubDate><guid>https://jonesrussell.github.io/blog/hugo-blog-visual-shortcodes/</guid><category>general</category><blog:tag>hugo</blog:tag><blog:tag>claude-code</blog:tag><blog:tag>papermod</blog:tag><blog:tag>shortcodes</blog:tag><description>Six Hugo shortcodes that give your PaperMod blog callouts, steps, pull quotes, stats, before/after comparisons, and CTAs — built in one vibe coding session.</description><content:encoded><![CDATA[<p>Ahnii!</p>
<p><a href="https://github.com/adityatelange/hugo-PaperMod">PaperMod</a> is a clean, fast <a href="https://gohugo.io/">Hugo</a> theme. What it doesn&rsquo;t give you out of the box is a component library: no callouts, no numbered steps, no before/after comparisons. If you write tutorials or technical posts, you end up compensating with blockquotes and bold text where purpose-built components would serve the reader better.</p>
<p>This post covers all six shortcodes, the CSS behind them, and how to add the same components to your own PaperMod blog. All of it came together in a single <a href="https://claude.com/claude-code">Claude Code</a> session.</p>
<h2 id="what-were-building">What we&rsquo;re building</h2>
<p>Six shortcodes, one CSS file:</p>
<ul>
<li><strong>callout</strong>: highlighted aside with five severity types</li>
<li><strong>steps / step</strong>: auto-numbered procedure blocks</li>
<li><strong>pullquote</strong>: large-format quote for emphasis</li>
<li><strong>stats / stat</strong>: side-by-side metric tiles</li>
<li><strong>compare / before / after</strong>: side-by-side comparison panels</li>
<li><strong>cta</strong>: call-to-action box with a button</li>
</ul>
<div class="stats">
  
<div class="stat">
  <div class="stat-number">6</div>
  <div class="stat-label">shortcodes</div>
</div>

<div class="stat">
  <div class="stat-number">9</div>
  <div class="stat-label">template files</div>
</div>

<div class="stat">
  <div class="stat-number">1</div>
  <div class="stat-label">CSS file</div>
</div>

<div class="stat">
  <div class="stat-number">0</div>
  <div class="stat-label">JS required</div>
</div>


</div>

<p>All styles hook into PaperMod&rsquo;s CSS variables (<code>--primary</code>, <code>--entry</code>, <code>--border</code>, etc.), so they adapt to dark and light mode automatically.</p>
<h2 id="file-locations">File locations</h2>
<p>Hugo resolves shortcodes from <code>layouts/shortcodes/</code>. Create one <code>.html</code> file per shortcode:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>layouts/shortcodes/
</span></span><span style="display:flex;"><span>  callout.html
</span></span><span style="display:flex;"><span>  steps.html
</span></span><span style="display:flex;"><span>  step.html
</span></span><span style="display:flex;"><span>  pullquote.html
</span></span><span style="display:flex;"><span>  stats.html
</span></span><span style="display:flex;"><span>  stat.html
</span></span><span style="display:flex;"><span>  compare.html
</span></span><span style="display:flex;"><span>  before.html
</span></span><span style="display:flex;"><span>  after.html
</span></span></code></pre></div><p>The CSS goes in <code>assets/css/extended/</code>. PaperMod loads everything in that directory automatically; no import statements needed.</p>
<h2 id="the-shortcodes">The shortcodes</h2>
<h3 id="callout">Callout</h3>
<p>A callout is a highlighted aside that draws the reader&rsquo;s attention. It accepts a <code>type</code> parameter: <code>info</code>, <code>warning</code>, <code>tip</code>, <code>note</code>, or <code>success</code>. Defaults to <code>note</code>.</p>
<p><strong>Template</strong> (<code>layouts/shortcodes/callout.html</code>):</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-html" data-lang="html"><span style="display:flex;"><span>{{- $type := .Get &#34;type&#34; | default &#34;note&#34; -}}
</span></span><span style="display:flex;"><span>{{- $emoji := dict &#34;info&#34; &#34;💡&#34; &#34;warning&#34; &#34;⚠️&#34; &#34;tip&#34; &#34;✨&#34; &#34;note&#34; &#34;📝&#34; &#34;success&#34; &#34;✅&#34; -}}
</span></span><span style="display:flex;"><span>&lt;<span style="color:#f92672">div</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;callout callout-{{ $type }}&#34;</span>&gt;
</span></span><span style="display:flex;"><span>  &lt;<span style="color:#f92672">div</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;callout-marker&#34;</span>&gt;{{ index $emoji $type }}&lt;/<span style="color:#f92672">div</span>&gt;
</span></span><span style="display:flex;"><span>  &lt;<span style="color:#f92672">div</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;callout-body&#34;</span>&gt;{{ .Inner | markdownify }}&lt;/<span style="color:#f92672">div</span>&gt;
</span></span><span style="display:flex;"><span>&lt;/<span style="color:#f92672">div</span>&gt;
</span></span></code></pre></div><p><strong>Usage:</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>{{&lt; callout type=&#34;warning&#34; &gt;}}
</span></span><span style="display:flex;"><span>Run `git stash` before switching branches or you will lose your changes.
</span></span><span style="display:flex;"><span>{{&lt; /callout &gt;}}
</span></span></code></pre></div><p><strong>Rendered:</strong></p>
<div class="callout callout-warning">
  <div class="callout-marker">⚠️</div>
  <div class="callout-body">
    Run <code>git stash</code> before switching branches or you will lose your changes.
  </div>
</div>

<p>The <code>markdownify</code> call means you can use inline markdown inside the body: backtick code, bold, links. All render correctly.</p>
<h3 id="steps-and-step">Steps and step</h3>
<p>The <code>steps</code> shortcode wraps a sequence of <code>step</code> shortcodes. Each <code>step</code> takes a title as its first positional argument and auto-numbers itself via CSS counters. No JavaScript, no manual numbering.</p>
<p><strong>Templates</strong> (<code>layouts/shortcodes/steps.html</code> and <code>step.html</code>):</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-html" data-lang="html"><span style="display:flex;"><span><span style="color:#75715e">&lt;!-- steps.html --&gt;</span>
</span></span><span style="display:flex;"><span>&lt;<span style="color:#f92672">div</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;steps&#34;</span>&gt;{{ .Inner }}&lt;/<span style="color:#f92672">div</span>&gt;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">&lt;!-- step.html --&gt;</span>
</span></span><span style="display:flex;"><span>&lt;<span style="color:#f92672">div</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;step&#34;</span>&gt;
</span></span><span style="display:flex;"><span>  &lt;<span style="color:#f92672">div</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;step-badge&#34;</span>&gt;&lt;/<span style="color:#f92672">div</span>&gt;
</span></span><span style="display:flex;"><span>  &lt;<span style="color:#f92672">div</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;step-body&#34;</span>&gt;
</span></span><span style="display:flex;"><span>    &lt;<span style="color:#f92672">div</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;step-title&#34;</span>&gt;{{ .Get 0 }}&lt;/<span style="color:#f92672">div</span>&gt;
</span></span><span style="display:flex;"><span>    &lt;<span style="color:#f92672">div</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;step-content&#34;</span>&gt;{{ .Inner | markdownify }}&lt;/<span style="color:#f92672">div</span>&gt;
</span></span><span style="display:flex;"><span>  &lt;/<span style="color:#f92672">div</span>&gt;
</span></span><span style="display:flex;"><span>&lt;/<span style="color:#f92672">div</span>&gt;
</span></span></code></pre></div><p><strong>Usage:</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>{{&lt; steps &gt;}}
</span></span><span style="display:flex;"><span>{{&lt; step &#34;Create the shortcode file&#34; &gt;}}
</span></span><span style="display:flex;"><span>Add `layouts/shortcodes/callout.html` to your project.
</span></span><span style="display:flex;"><span>{{&lt; /step &gt;}}
</span></span><span style="display:flex;"><span>{{&lt; step &#34;Add the CSS&#34; &gt;}}
</span></span><span style="display:flex;"><span>Create `assets/css/extended/components.css` with the component styles.
</span></span><span style="display:flex;"><span>{{&lt; /step &gt;}}
</span></span><span style="display:flex;"><span>{{&lt; /steps &gt;}}
</span></span></code></pre></div><p><strong>Rendered:</strong></p>
<div class="steps">
  
<div class="step">
  <div class="step-badge"></div>
  <div class="step-body">
    <div class="step-title">Create the shortcode file</div>
    <div class="step-content">Add <code>layouts/shortcodes/callout.html</code> to your project.</div>
  </div>
</div>

<div class="step">
  <div class="step-badge"></div>
  <div class="step-body">
    <div class="step-title">Add the CSS</div>
    <div class="step-content">Create <code>assets/css/extended/components.css</code> with the component styles.</div>
  </div>
</div>


</div>

<h3 id="stats-and-stat">Stats and stat</h3>
<p>The <code>stats</code> shortcode is a flex container for <code>stat</code> tiles. Each <code>stat</code> takes two positional arguments: the value and the label.</p>
<p><strong>Templates</strong> (<code>layouts/shortcodes/stats.html</code> and <code>stat.html</code>):</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-html" data-lang="html"><span style="display:flex;"><span><span style="color:#75715e">&lt;!-- stats.html --&gt;</span>
</span></span><span style="display:flex;"><span>&lt;<span style="color:#f92672">div</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;stats&#34;</span>&gt;{{ .Inner }}&lt;/<span style="color:#f92672">div</span>&gt;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">&lt;!-- stat.html --&gt;</span>
</span></span><span style="display:flex;"><span>&lt;<span style="color:#f92672">div</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;stat&#34;</span>&gt;
</span></span><span style="display:flex;"><span>  &lt;<span style="color:#f92672">div</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;stat-number&#34;</span>&gt;{{ .Get 0 }}&lt;/<span style="color:#f92672">div</span>&gt;
</span></span><span style="display:flex;"><span>  &lt;<span style="color:#f92672">div</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;stat-label&#34;</span>&gt;{{ .Get 1 }}&lt;/<span style="color:#f92672">div</span>&gt;
</span></span><span style="display:flex;"><span>&lt;/<span style="color:#f92672">div</span>&gt;
</span></span></code></pre></div><p><strong>Usage:</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>{{&lt; stats &gt;}}
</span></span><span style="display:flex;"><span>{{&lt; stat &#34;6&#34; &#34;shortcodes&#34; &gt;}}
</span></span><span style="display:flex;"><span>{{&lt; stat &#34;1&#34; &#34;CSS file&#34; &gt;}}
</span></span><span style="display:flex;"><span>{{&lt; stat &#34;0&#34; &#34;JS required&#34; &gt;}}
</span></span><span style="display:flex;"><span>{{&lt; /stats &gt;}}
</span></span></code></pre></div><p><strong>Rendered:</strong></p>
<div class="stats">
  
<div class="stat">
  <div class="stat-number">6</div>
  <div class="stat-label">shortcodes</div>
</div>

<div class="stat">
  <div class="stat-number">1</div>
  <div class="stat-label">CSS file</div>
</div>

<div class="stat">
  <div class="stat-number">0</div>
  <div class="stat-label">JS required</div>
</div>


</div>

<p>The tiles flex-wrap on small screens, so they stack gracefully on mobile without extra media query work.</p>
<h3 id="compare-before-and-after">Compare, before, and after</h3>
<p>Three files work together: <code>compare.html</code> wraps the pair, <code>before.html</code> and <code>after.html</code> render each panel. The before panel uses PaperMod&rsquo;s warning colour; after uses the success colour.</p>
<p><strong>Templates</strong> (<code>compare.html</code>, <code>before.html</code>, <code>after.html</code>):</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-html" data-lang="html"><span style="display:flex;"><span><span style="color:#75715e">&lt;!-- compare.html --&gt;</span>
</span></span><span style="display:flex;"><span>&lt;<span style="color:#f92672">div</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;compare&#34;</span>&gt;{{ .Inner }}&lt;/<span style="color:#f92672">div</span>&gt;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">&lt;!-- before.html --&gt;</span>
</span></span><span style="display:flex;"><span>&lt;<span style="color:#f92672">div</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;compare-before&#34;</span>&gt;
</span></span><span style="display:flex;"><span>  &lt;<span style="color:#f92672">div</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;compare-marker&#34;</span>&gt;✕&lt;/<span style="color:#f92672">div</span>&gt;
</span></span><span style="display:flex;"><span>  &lt;<span style="color:#f92672">div</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;compare-body&#34;</span>&gt;{{ .Inner | markdownify }}&lt;/<span style="color:#f92672">div</span>&gt;
</span></span><span style="display:flex;"><span>&lt;/<span style="color:#f92672">div</span>&gt;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">&lt;!-- after.html --&gt;</span>
</span></span><span style="display:flex;"><span>&lt;<span style="color:#f92672">div</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;compare-after&#34;</span>&gt;
</span></span><span style="display:flex;"><span>  &lt;<span style="color:#f92672">div</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;compare-marker&#34;</span>&gt;✓&lt;/<span style="color:#f92672">div</span>&gt;
</span></span><span style="display:flex;"><span>  &lt;<span style="color:#f92672">div</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;compare-body&#34;</span>&gt;{{ .Inner | markdownify }}&lt;/<span style="color:#f92672">div</span>&gt;
</span></span><span style="display:flex;"><span>&lt;/<span style="color:#f92672">div</span>&gt;
</span></span></code></pre></div><p><strong>Usage:</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>{{&lt; compare &gt;}}
</span></span><span style="display:flex;"><span>{{&lt; before &gt;}}
</span></span><span style="display:flex;"><span>Blockquote hacks repurposed as callouts.
</span></span><span style="display:flex;"><span>{{&lt; /before &gt;}}
</span></span><span style="display:flex;"><span>{{&lt; after &gt;}}
</span></span><span style="display:flex;"><span>Purpose-built `callout` shortcode with five types.
</span></span><span style="display:flex;"><span>{{&lt; /after &gt;}}
</span></span><span style="display:flex;"><span>{{&lt; /compare &gt;}}
</span></span></code></pre></div><p><strong>Rendered:</strong></p>
<div class="compare">
  
<div class="compare-before">
  <div class="compare-marker">✕</div>
  <div class="compare-body">Blockquote hacks repurposed as callouts.</div>
</div>

<div class="compare-after">
  <div class="compare-marker">✓</div>
  <div class="compare-body">Purpose-built <code>callout</code> shortcode with five types.</div>
</div>


</div>

<p>On screens narrower than 600px the panels stack vertically.</p>
<h3 id="pullquote">Pullquote</h3>
<p>A pullquote is a styled blockquote for emphasis. Use it to surface a key insight or memorable line from the surrounding text.</p>
<p><strong>Template</strong> (<code>layouts/shortcodes/pullquote.html</code>):</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-html" data-lang="html"><span style="display:flex;"><span>&lt;<span style="color:#f92672">blockquote</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;pullquote&#34;</span>&gt;
</span></span><span style="display:flex;"><span>  {{ .Inner | markdownify }}
</span></span><span style="display:flex;"><span>&lt;/<span style="color:#f92672">blockquote</span>&gt;
</span></span></code></pre></div><p><strong>Usage:</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>{{&lt; pullquote &gt;}}
</span></span><span style="display:flex;"><span>Good writing tools get out of the way. Good components make the writing better.
</span></span><span style="display:flex;"><span>{{&lt; /pullquote &gt;}}
</span></span></code></pre></div><p><strong>Rendered:</strong></p>
<blockquote class="pullquote">
  Good writing tools get out of the way. Good components make the writing better.
</blockquote>

<h3 id="cta">CTA</h3>
<p>A call-to-action box with a centred button. Takes three named parameters: <code>title</code>, <code>button</code>, and <code>href</code>. The inner body is optional copy.</p>
<p><strong>Template</strong> (<code>layouts/shortcodes/cta.html</code>):</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-html" data-lang="html"><span style="display:flex;"><span>{{- $title := .Get &#34;title&#34; -}}
</span></span><span style="display:flex;"><span>{{- $button := .Get &#34;button&#34; -}}
</span></span><span style="display:flex;"><span>{{- $href := .Get &#34;href&#34; -}}
</span></span><span style="display:flex;"><span>&lt;<span style="color:#f92672">div</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;cta&#34;</span>&gt;
</span></span><span style="display:flex;"><span>  &lt;<span style="color:#f92672">h3</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;cta-title&#34;</span>&gt;{{ $title }}&lt;/<span style="color:#f92672">h3</span>&gt;
</span></span><span style="display:flex;"><span>  &lt;<span style="color:#f92672">div</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;cta-body&#34;</span>&gt;{{ .Inner | markdownify }}&lt;/<span style="color:#f92672">div</span>&gt;
</span></span><span style="display:flex;"><span>  &lt;<span style="color:#f92672">a</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;cta-button&#34;</span> <span style="color:#a6e22e">href</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;{{ $href }}&#34;</span>&gt;{{ $button }}&lt;/<span style="color:#f92672">a</span>&gt;
</span></span><span style="display:flex;"><span>&lt;/<span style="color:#f92672">div</span>&gt;
</span></span></code></pre></div><p><strong>Usage:</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>{{&lt; cta title=&#34;Try it yourself&#34; button=&#34;View the source&#34; href=&#34;https://github.com/jonesrussell/blog&#34; &gt;}}
</span></span><span style="display:flex;"><span>All six shortcodes and the CSS are in the repo.
</span></span><span style="display:flex;"><span>{{&lt; /cta &gt;}}
</span></span></code></pre></div><p><strong>Rendered:</strong></p>
<div class="cta">
  <h3 class="cta-title">Try it yourself</h3>
  <div class="cta-body">All six shortcodes and the CSS are in the repo.</div>
  <a class="cta-button" href="https://github.com/jonesrussell/blog">View the source</a>
</div>

<h2 id="the-proving-ground">The proving ground</h2>
<p>Before calling the system done, retrofit an existing post. I used Minoo Elders, replacing a flat numbered list with a <code>steps</code> block and a closing paragraph with a <code>cta</code>. If the shortcodes work in a real post with real content, they are ready.</p>
<p>The retrofit caught a line-height edge case in the step badge CSS and confirmed the dark mode colours held in both themes. Worth the ten minutes.</p>
<h2 id="vibe-coding-the-component-system">Vibe coding the component system</h2>
<p>This system was built with <a href="https://claude.com/claude-code">Claude Code</a> in one session. Describe the component you want, review the draft, push back on anything over-engineered. Nine files and the CSS came together without a lot of manual effort.</p>
<p>The real gain is in the iteration loop: see a render, request a tweak, get updated CSS in thirty seconds. That speed is the whole point.</p>
<p>Baamaapii</p>
]]></content:encoded></item></channel></rss>