<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet href="https://mademistakes.com/feed.xsl" type="text/xsl"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-US"><generator uri="https://gohugo.io/" version="0.139.3">Hugo</generator><title type="html">Made Mistakes on Made Mistakes</title><subtitle type="html">The personal website and blog of Michael Rose.</subtitle><link href="https://mademistakes.com/" rel="alternate" type="text/html" title="html"/><link href="https://mademistakes.com/index.xml" rel="self" type="application/atom+xml" title="atom"/><updated>2026-02-27T16:48:33+00:00</updated><rights>© 2026 Michael Rose. All rights reserved.</rights><icon>https://mademistakes.com/images/made-mistakes-icon.528a92b2bcbac265e82e29a951516983c0b924df0a72f56d82ccdf619be42e39.svg</icon><logo>https://mademistakes.com/images/made-mistakes-logo.ccaccc4d501fb3fc62eaf7566fa48fc0069a3bad14a7f8bc27af16650d37d6c7.svg</logo><author><name>Michael Rose</name></author><id>https://mademistakes.com/</id><entry><title type="html">Feeds</title><link href="https://mademistakes.com/feeds/?utm_source=atom_feed" rel="alternate" type="text/html"/><id>https://mademistakes.com/feeds/</id><published>2024-06-04T14:36:56-04:00</published><updated>2024-06-04T21:36:54-04:00</updated><summary type="html">&amp;lt;p&amp;gt;To receive updates, subscribe to one of Made Mistakes&amp;amp;rsquo; web feeds&amp;lt;sup id=&amp;#34;fnref:1&amp;#34;&amp;gt;&amp;lt;a href=&amp;#34;#fn:1&amp;#34; class=&amp;#34;footnote-ref&amp;#34; role=&amp;#34;doc-noteref&amp;#34;&amp;gt;1&amp;lt;/a&amp;gt;&amp;lt;/sup&amp;gt; below using your news reader of choice&amp;amp;hellip; I like &amp;lt;a href=&amp;#34;https://feedly.com&amp;#34; rel=&amp;#34;noopener&amp;#34;&amp;gt;&amp;lt;strong&amp;gt;Feedly&amp;lt;/strong&amp;gt;&amp;lt;/a&amp;gt;.&amp;lt;/p&amp;gt;</summary><content type="html">&lt;p>To receive updates, subscribe to one of Made Mistakes&amp;rsquo; web feeds&lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup> below using your news reader of choice&amp;hellip; I like &lt;a href="https://feedly.com" rel="noopener">&lt;strong>Feedly&lt;/strong>&lt;/a>.&lt;/p>
&lt;ul>
&lt;li>&lt;a href="/index.xml">All pages&lt;/a>&lt;/li>
&lt;li>&lt;a href="/articles/index.xml">Articles&lt;/a>&lt;/li>
&lt;li>&lt;a href="/mastering-jekyll/index.xml">Mastering Jekyll&lt;/a>&lt;/li>
&lt;li>&lt;a href="/mastering-paper/index.xml">Mastering Paper&lt;/a>&lt;/li>
&lt;li>&lt;a href="/notes/index.xml">Notes&lt;/a>&lt;/li>
&lt;li>&lt;a href="/work/paperfaces/index.xml">PaperFaces&lt;/a>&lt;/li>
&lt;li>&lt;a href="/work/procreate-paintings/index.xml">Procreate paintings&lt;/a>&lt;/li>
&lt;li>&lt;a href="/work/tiny-paintings/index.xml">Tiny paintings&lt;/a>&lt;/li>
&lt;li>&lt;a href="/work/index.xml">Work&lt;/a>&lt;/li>
&lt;/ul>
&lt;div class="footnotes" role="doc-endnotes">
&lt;hr>
&lt;ol>
&lt;li id="fn:1">
&lt;p>If you&amp;rsquo;re new to feeds, RSS, Atom and the like, &lt;a href="https://aboutfeeds.com/" rel="noopener">aboutfeeds.com&lt;/a> has you covered and explains how to get started.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;/div>&lt;p>&lt;a href="https://mademistakes.com/feeds/?utm_source=atom_feed">Feeds&lt;/a> was originally published on Made Mistakes.&lt;/p></content></entry><entry><title type="html">And just like that, I was done with Netlify</title><link href="https://mademistakes.com/notes/done-with-netlify/?utm_source=atom_feed" rel="alternate" type="text/html"/><id>https://mademistakes.com/notes/done-with-netlify/</id><published>2024-02-29T09:53:40-05:00</published><updated>2024-02-29T09:53:40-05:00</updated><summary type="html">&amp;lt;p&amp;gt;As someone who uses &amp;lt;strong&amp;gt;Netlify&amp;amp;rsquo;s&amp;lt;/strong&amp;gt; Starter plan to host this site, the following situation is the kind of nonsense that keeps me up late at night&amp;amp;hellip;&amp;lt;/p&amp;gt;</summary><content type="html">&lt;p>As someone who uses &lt;strong>Netlify&amp;rsquo;s&lt;/strong> Starter plan to host this site, the following situation is the kind of nonsense that keeps me up late at night&amp;hellip;&lt;/p>
&lt;blockquote>
&lt;p>On February 24th, an invoice was issued to a Starter plan user of Netlify for a substantial amount of bandwidth usage.&lt;/p>
&lt;p>Our internal systems should have identified this rare case, and it should have been flagged by our systems before being sent to this user. We sincerely apologize for this situation and the anxiety it caused. Please know that we are taking this situation very seriously.&lt;/p>
&lt;p>This situation has reinforced the urgency of our ongoing work to put safeguards in place in our billing system to ensure that this never happens again. No user of Netlify should ever be concerned that this could happen to them, and we are committed to fixing this issue immediately.&lt;/p>
&lt;p>We will also be looking at our company practices, as there clearly was an opportunity for us to have better handled this situation.&lt;/p>
&lt;p>Again, we apologize to the impacted user, as well as the community at large. We promise to do better.&lt;/p>
&lt;p>&lt;cite>Netlify (&lt;a href="https://twitter.com/Netlify/" rel="noopener">@Netlify&lt;/a>) &lt;a href="https://twitter.com/Netlify/status/1762518910107033798?ref_src=twsrc%5Etfw">February 27, 2024&lt;/a>&lt;/cite>&lt;/p>
&lt;/blockquote>
&lt;p>The fact that a small static site hosted &lt;em>for free&lt;/em> could potentially &lt;a href="https://www.reddit.com/r/webdev/comments/1b14bty/netlify_just_sent_me_a_104k_bill_for_a_simple/" rel="noopener">cost thousands of dollars&lt;/a> is insane. Up until now, I assumed hitting any bandwidth limits would take the site down until the next billing period (or pay any overage fees to turn it back on).&lt;/p>
&lt;p>To mitigate the risk of this happening to me, I shut down Netlify deployments and migrated to &lt;a href="https://pages.cloudflare.com" rel="noopener">&lt;strong>Cloudflare Pages&lt;/strong>&lt;/a>. On paper Cloudflare&amp;rsquo;s features are way more attractive and don&amp;rsquo;t imply free plans will be charged for going over any limits.&lt;/p>
&lt;h2 id="free-plan-comparison">&lt;a href="#free-plan-comparison">Free plan comparison&lt;/a>&lt;/h2>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>-&lt;/th>
&lt;th style="text-align: center">&lt;a href="https://www.cloudflare.com/plans/" rel="noopener">Cloudflare&lt;/a>&lt;/th>
&lt;th style="text-align: center">&lt;a href="https://www.netlify.com/pricing/#core-pricing-table" rel="noopener">Netlify&lt;/a>&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>Bandwidth&lt;/strong>&lt;/td>
&lt;td style="text-align: center">Unlimited&lt;/td>
&lt;td style="text-align: center">100GB per month (then $55 per 100GB)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Builds&lt;/strong>&lt;/td>
&lt;td style="text-align: center">1 concurrent build, 500 times per month&lt;/td>
&lt;td style="text-align: center">1 concurrent build, 300 minutes per month (then $7 per 500)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Sites&lt;/strong>&lt;/td>
&lt;td style="text-align: center">Unlimited&lt;/td>
&lt;td style="text-align: center">500&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Preview deployments&lt;/strong>&lt;/td>
&lt;td style="text-align: center">✔&lt;/td>
&lt;td style="text-align: center">✔&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Custom domains&lt;/strong>&lt;/td>
&lt;td style="text-align: center">100 per project&lt;/td>
&lt;td style="text-align: center">~90 apex domains&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>SSL certificate&lt;/strong>&lt;/td>
&lt;td style="text-align: center">✔&lt;/td>
&lt;td style="text-align: center">✔&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Site analytics&lt;/strong>&lt;/td>
&lt;td style="text-align: center">Free&lt;/td>
&lt;td style="text-align: center">$9 per site per month&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Files&lt;/strong>&lt;/td>
&lt;td style="text-align: center">Sites can contain 20,000 files &amp;mdash; maximum asset size is 25 MiB&lt;/td>
&lt;td style="text-align: center">Limit of 54,000 files per directory &amp;mdash; maximum asset size is 100 MiB&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Header rules&lt;/strong>&lt;/td>
&lt;td style="text-align: center">Maximum of 100 rules&lt;/td>
&lt;td style="text-align: center">-&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Redirects&lt;/strong>&lt;/td>
&lt;td style="text-align: center">Maximum of 2,000 static redirects and 100 dynamic redirects. &lt;a href="https://developers.cloudflare.com/rules/url-forwarding/bulk-redirects/" rel="noopener">Bulk Redirects&lt;/a> allow for much more at the account level.&lt;/td>
&lt;td style="text-align: center">No apparent limit, recommended to keep under 10k redirects&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Content scraping protection&lt;/strong>&lt;/td>
&lt;td style="text-align: center">Protects text, images and email addresses from web scrapers&lt;/td>
&lt;td style="text-align: center">-&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Members&lt;/strong>&lt;/td>
&lt;td style="text-align: center">Pages site can be managed by an unlimited number of users via the Cloudflare dashboard&lt;/td>
&lt;td style="text-align: center">Limited to 1 free member, with unlimited Git Contributors&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="cloudflare-pages">&lt;a href="#cloudflare-pages">Cloudflare Pages&lt;/a>&lt;/h2>
&lt;p>&lt;a href="https://developers.cloudflare.com/pages/migrations/migrating-from-netlify/" title="Netlify to Cloudflare migration guide" rel="noopener">Migrating from Netlify to Cloudflare&lt;/a> was painless:&lt;/p>
&lt;ol>
&lt;li>Connect GitHub repository to Cloudflare Pages.&lt;/li>
&lt;li>Configure Hugo build command.&lt;/li>
&lt;li>Validate &lt;code>*.pages.dev&lt;/code> site builds when pushing commits to GitHub.&lt;/li>
&lt;li>Copy DNS records from Netlify over to Cloudflare.&lt;/li>
&lt;/ol>
&lt;p>The hardest part was waiting for all the records to propagate across the internet.&lt;/p>
&lt;p>My custom &lt;a href="https://developers.cloudflare.com/pages/configuration/headers/" rel="noopener">&lt;code>_headers&lt;/code>&lt;/a> and &lt;a href="https://developers.cloudflare.com/pages/configuration/redirects/" rel="noopener">&lt;code>_redirects&lt;/code>&lt;/a> files used by Netlify ported over great too as Cloudflare Pages uses the same.&lt;/p>
&lt;div class="browser-frame">&lt;img alt="mademistakes.com Cloudflare Pages build log output" class="img--md" decoding="async" fetchpriority="auto" height="231" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/cloudflare-pages-mm-build-log_hu916490700792609871.jpg" style="background: #f7f7f7;" width="300"
&lt;pre>&lt;code> srcset=&amp;quot;https://mademistakes.com/images/cloudflare-pages-mm-build-log_hu916490700792609871.jpg 300w, https://mademistakes.com/images/cloudflare-pages-mm-build-log_hu3322410832660247704.jpg 620w&amp;quot;
&amp;gt;
&lt;/code>&lt;/pre>
&lt;/div>
&lt;p>One snag I encountered during the migration was long build times. In my previous workflow, I set up &lt;strong>GitHub Actions&lt;/strong> and Netlify to cache all generated static assets between builds. For a site like mine with thousands of images, this slashes a cold build from 18 minutes down to a minute for incremental builds.&lt;/p>
&lt;div class="browser-frame">&lt;img alt="Build cache settings in Cloudflare dashboard." class="img--md" decoding="async" fetchpriority="auto" height="97" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/cloudflare-build-cache-beta_hu13344641167702600729.jpg" style="background: #fefefe;" width="300"
&lt;pre>&lt;code> srcset=&amp;quot;https://mademistakes.com/images/cloudflare-build-cache-beta_hu13344641167702600729.jpg 300w, https://mademistakes.com/images/cloudflare-build-cache-beta_hu12578056594562643106.jpg 620w&amp;quot;
&amp;gt;
&lt;/code>&lt;/pre>
&lt;/div>
&lt;p>Cloudflare Pages does support &lt;a href="https://developers.cloudflare.com/pages/configuration/build-caching/" rel="noopener">build caching&lt;/a>, but only for &lt;strong>Gatsby&lt;/strong>, &lt;strong>Next.js&lt;/strong>, and &lt;strong>Astro&lt;/strong> frameworks. It&amp;rsquo;s a beta feature, so I expect &lt;a href="https://gohugo.io" rel="noopener">&lt;strong>Hugo&lt;/strong>&lt;/a> to be added in the future. For now I&amp;rsquo;ll have to live with 18-minute builds &amp;mdash; unless I decide to use a GitHub Action again to build then deploy to Cloudflare using their &lt;a href="https://developers.cloudflare.com/workers/wrangler/" rel="noopener">&lt;strong>Wrangler&lt;/strong>&lt;/a> CLI. We&amp;rsquo;ll see&amp;hellip;&lt;/p>
&lt;div class="browser-frame">&lt;img alt="Cloudflare Web Analytics dashboard showing Core Web Vital scores for mademistakes.com" class="img--md" decoding="async" fetchpriority="auto" height="301" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/cloudflare-mm-web-analytics_hu1359105835052311903.jpg" style="background: #fafcfa;" width="300"
&lt;pre>&lt;code> srcset=&amp;quot;https://mademistakes.com/images/cloudflare-mm-web-analytics_hu1359105835052311903.jpg 300w, https://mademistakes.com/images/cloudflare-mm-web-analytics_hu8296939172255951730.jpg 620w&amp;quot;
&amp;gt;
&lt;/code>&lt;/pre>
&lt;/div>
&lt;p>Another bonus, Cloudflare gives you &lt;a href="https://developers.cloudflare.com/analytics/web-analytics" rel="noopener">&lt;em>privacy-first&lt;/em> site analytics&lt;/a> for free too. Which could be an option if I decide &lt;strong>Google Analytics&lt;/strong> at some point.&lt;/p>
&lt;p>And hell, if Cloudflare Pages turns out to be a dud or too restrictive I can always go back to self-hosting on the cheap.&lt;/p>&lt;p>&lt;a href="https://mademistakes.com/notes/done-with-netlify/?utm_source=atom_feed">And just like that, I was done with Netlify&lt;/a> was originally published on Made Mistakes.&lt;/p></content><category scheme="https://mademistakes.com/tag/static-sites" term="static-sites" label="static sites"/><category scheme="https://mademistakes.com/tag/web-development" term="web-development" label="web development"/></entry><entry><title type="html">DIY record cube back spacers</title><link href="https://mademistakes.com/notes/diy-record-cube-back-spacers/?utm_source=atom_feed" rel="alternate" type="text/html"/><id>https://mademistakes.com/notes/diy-record-cube-back-spacers/</id><published>2024-02-22T00:00:00-05:00</published><updated>2024-02-22T00:00:00-05:00</updated><summary type="html">Tutorial for building cheap alternatives to Turntable Revival&amp;#39;s wooden back spacers.</summary><content type="html">
&lt;img alt="" decoding="async" fetchpriority="auto" height="225" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/record-spines-not-flush_hu3725707645094851142.jpg" style="background: #423b34;" width="300"
srcset="https://mademistakes.com/images/record-spines-not-flush_hu3725707645094851142.jpg 300w,https://mademistakes.com/images/record-spines-not-flush_hu14676186037105455326.jpg 800w, https://mademistakes.com/images/record-spines-not-flush.jpg 1280w"
>&lt;p>For the longest time, it has driven me mad that my records get out of alignment any time I slide one out to play. To solve this problem, &lt;a href="https://turntablerevival.com/collections/premiumbackspacer" rel="noopener">&lt;strong>Turntable Revival&lt;/strong>&lt;/a> sells wooden back spacers for cube shelves (&lt;em>Ikea Kallax&lt;/em>, &lt;a href="https://www.target.com/p/6-cube-organizer-black-oak-brightroom-8482/-/A-83498092?preselect=83498092#lnk=sametab" rel="noopener">&lt;em>Target Threshold&lt;/em>&lt;/a>, &lt;em>Walmart - Better Homes &amp;amp; Gardens&lt;/em>) to straighten your records out.&lt;/p>
&lt;p>To save a buck I decided to MacGyver a cheap solution with &lt;a href="https://www.michaels.com/product/16-x-20-black-foam-boards-3ct-10308906" rel="noopener">black foam board&lt;/a> and &lt;a href="https://www.amazon.com/Acrylic-Adhesive-Waterproof-Removable-Mounting/dp/B0852XL3CC?crid=2MDPBZWLB6X5A&amp;amp;dib=eyJ2IjoiMSJ9.YhF0V2ykhhhV15a-ym4XOIp7IkPRBZr1Qrd4KCcmqkPKmgw4c8esIdE4a57y6OHX6231o3gnZB_JiOs2kB0q0JUhHAI8HDq6hnHJWhiTJfrxS7DxHncm7gH0QiHsx_R5VtkQaZhhckXa615FxJfNmsdUWLPt-aGb9XZYtw8OtXD1MKFkUoMaYktBfgSuNO4F0roV5X4U0NnAoC2cRb7yeeTZaxMrgAY6G1qSDwtkEJ6cngTLGoj0q23H7AFFDxrE6H6ozF4zxs2dfDariQuvaNTHWfEz77y7TTxVGYiGkfM.rjgM63bdYrwnbd4pF7EDu9bUKyqDMiuSOC2xzjLKyRM&amp;amp;dib_tag=se&amp;amp;keywords=double%2Bsided%2Bmounting%2Btape&amp;amp;qid=1708622444&amp;amp;sprefix=double%2Bsided%2Bmounting%2Btape%2Caps%2C260&amp;amp;sr=8-40&amp;amp;th=1&amp;amp;linkCode=ll1&amp;amp;tag=mademist-20&amp;amp;linkId=05b3e856c284606a34e325ba525dc266&amp;amp;language=en_US&amp;amp;ref_=as_li_ss_tl" rel="noopener">double-sided tape&lt;/a>.&lt;/p>
&lt;p>
&lt;img alt="Sheet of 16\" x 20\" black foam board was placed in front of cube shelves filled with vinyl records." class="img--md" decoding="async" fetchpriority="auto" height="225" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/records-black-foam-board_hu15371024702671151414.jpg" style="background: #36332c;" width="300"
srcset="https://mademistakes.com/images/records-black-foam-board_hu15371024702671151414.jpg 300w, https://mademistakes.com/images/records-black-foam-board_hu9558315633141948002.jpg 620w"
>&lt;/p>
&lt;h2 id="procedure">&lt;a href="#procedure">Procedure&lt;/a>&lt;/h2>
&lt;p>&lt;strong>Step 1:&lt;/strong> Measure the length of each cube. I have two 6-cube &lt;a href="https://www.target.com/p/6-cube-organizer-black-oak-brightroom-8482/-/A-83498092?preselect=83498092#lnk=sametab" rel="noopener">&lt;em>Target Threshold&lt;/em>&lt;/a> organizers, with each cube measuring 13 ½&amp;quot; wide.&lt;/p>
&lt;figure class="figure" data-columns="auto">
&lt;p>
&lt;img alt="Empty cube shelf." class="img--md" decoding="async" fetchpriority="auto" height="225" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/empty-cube-shelf_hu4979896627324774608.jpg" style="background: #40392f;" width="300"
srcset="https://mademistakes.com/images/empty-cube-shelf_hu4979896627324774608.jpg 300w, https://mademistakes.com/images/empty-cube-shelf_hu4915637073513362998.jpg 620w"
>&lt;/p>
&lt;p>
&lt;img alt="Measuring space from the record spine to the shelfs front." class="img--md" decoding="async" fetchpriority="auto" height="225" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/cube-shelf-depth_hu7985311135393807250.jpg" style="background: #434038;" width="300"
srcset="https://mademistakes.com/images/cube-shelf-depth_hu7985311135393807250.jpg 300w, https://mademistakes.com/images/cube-shelf-depth_hu17755978160342239386.jpg 620w"
>&lt;/p>
&lt;figcaption>Measure the empty shelf space to determine the back spacer&amp;rsquo;s size.&lt;/figcaption>
&lt;/figure>
&lt;p>&lt;strong>Step 2:&lt;/strong> Grab a record, place it to the far left and align it with the cube&amp;rsquo;s back edge. Then measure the distance from the record&amp;rsquo;s spine to the front of the cube. For my shelving, this ended up being 2&amp;quot; deep.&lt;/p>
&lt;p>
&lt;img alt="Kaospilot LP album placed on a cube shelf." class="img--md" decoding="async" fetchpriority="auto" height="225" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/record-in-empty-shelf_hu13015877248071766456.jpg" style="background: #443f38;" width="300"
srcset="https://mademistakes.com/images/record-in-empty-shelf_hu13015877248071766456.jpg 300w, https://mademistakes.com/images/record-in-empty-shelf_hu1693841772901651456.jpg 620w"
>&lt;/p>
&lt;aside class="notice">&lt;h3>Pro tip&lt;/h3>&lt;p>To leave room for lighting (or if you want the records recessed some) subtract about an inch from the measurement in step 2.&lt;/p>
&lt;/aside>
&lt;p>&lt;strong>Step 3:&lt;/strong> With a ruler and a pencil add guidelines to the foam board and use a sharp utility blade to cut the spacers.&lt;/p>
&lt;p>I took a &lt;a href="https://www.michaels.com/product/16-x-20-black-foam-boards-3ct-10308906" rel="noopener">16&amp;quot; x 20&amp;quot; foam board&lt;/a>, cut the shorter side down to 13 ½&amp;quot; and then cut that down at 2&amp;quot; intervals &amp;mdash; ending up with 10 back spacers.&lt;/p>
&lt;p>&lt;strong>Step 4:&lt;/strong> Using rubbing alcohol and a paper towel, clean the bottom side of each cube you&amp;rsquo;re adding a spacer to. Once dry, cut a strip of &lt;a href="https://www.amazon.com/Acrylic-Adhesive-Waterproof-Removable-Mounting/dp/B0852XL3CC?crid=2MDPBZWLB6X5A&amp;amp;dib=eyJ2IjoiMSJ9.YhF0V2ykhhhV15a-ym4XOIp7IkPRBZr1Qrd4KCcmqkPKmgw4c8esIdE4a57y6OHX6231o3gnZB_JiOs2kB0q0JUhHAI8HDq6hnHJWhiTJfrxS7DxHncm7gH0QiHsx_R5VtkQaZhhckXa615FxJfNmsdUWLPt-aGb9XZYtw8OtXD1MKFkUoMaYktBfgSuNO4F0roV5X4U0NnAoC2cRb7yeeTZaxMrgAY6G1qSDwtkEJ6cngTLGoj0q23H7AFFDxrE6H6ozF4zxs2dfDariQuvaNTHWfEz77y7TTxVGYiGkfM.rjgM63bdYrwnbd4pF7EDu9bUKyqDMiuSOC2xzjLKyRM&amp;amp;dib_tag=se&amp;amp;keywords=double%2Bsided%2Bmounting%2Btape&amp;amp;qid=1708622444&amp;amp;sprefix=double%2Bsided%2Bmounting%2Btape%2Caps%2C260&amp;amp;sr=8-40&amp;amp;th=1&amp;amp;linkCode=ll1&amp;amp;tag=mademist-20&amp;amp;linkId=05b3e856c284606a34e325ba525dc266&amp;amp;language=en_US&amp;amp;ref_=as_li_ss_tl" rel="noopener">double-sided tape&lt;/a> for each spacer and remove one side of the backing.&lt;/p>
&lt;p>Place the tape (sticky side down) onto the spacer and run your finger over it to make a good connection. Then remove the top piece of backing and place the spacer into the shelf making sure its edge is aligned with the back of the cube.&lt;/p>
&lt;p>
&lt;img alt="Black foam board with a strip of double-sided tape stuck to the back." class="img--md" decoding="async" fetchpriority="auto" height="185" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/black-foam-board-double-sided-tape_hu4220238627411354464.jpg" style="background: #36302d;" width="300"
srcset="https://mademistakes.com/images/black-foam-board-double-sided-tape_hu4220238627411354464.jpg 300w, https://mademistakes.com/images/black-foam-board-double-sided-tape_hu1410505461761103314.jpg 620w"
>&lt;/p>
&lt;p>Press down firmly on the foam board, using a circular motion to fully adhere the tape to the shelf.&lt;/p>
&lt;p>
&lt;img alt="Black foam board back spacer flush mounted to the shelfs back." class="img--md" decoding="async" fetchpriority="auto" height="225" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/black-foam-board-shelf-aligned_hu7673917723519233243.jpg" style="background: #2c271e;" width="300"
srcset="https://mademistakes.com/images/black-foam-board-shelf-aligned_hu7673917723519233243.jpg 300w, https://mademistakes.com/images/black-foam-board-shelf-aligned_hu6267281492643974746.jpg 620w"
>&lt;/p>
&lt;p>&lt;strong>Step 5:&lt;/strong> Slide a record into the cube shelf and verify you are happy with the placement. Repeat the process for the remaining cubes.&lt;/p>
&lt;p>
&lt;img alt="Records flush aligned in Target Threshold cube shelves." class="img--md" decoding="async" fetchpriority="auto" height="400" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/favorite-lps-record-player-2024_hu10888658474239398707.jpg" style="background: #393734;" width="300"
srcset="https://mademistakes.com/images/favorite-lps-record-player-2024_hu10888658474239398707.jpg 300w, https://mademistakes.com/images/favorite-lps-record-player-2024_hu9294284780672249719.jpg 620w"
>&lt;/p>&lt;p>&lt;a href="https://mademistakes.com/notes/diy-record-cube-back-spacers/?utm_source=atom_feed">DIY record cube back spacers&lt;/a> was originally published on Made Mistakes.&lt;/p></content><category scheme="https://mademistakes.com/tag/tutorials" term="tutorials" label="tutorials"/><category scheme="https://mademistakes.com/tag/vinyl" term="vinyl" label="vinyl"/></entry><entry><title type="html">Goodbye Gatsby, Hello Hugo</title><link href="https://mademistakes.com/notes/goodbye-gatsby-hello-hugo/?utm_source=atom_feed" rel="alternate" type="text/html"/><id>https://mademistakes.com/notes/goodbye-gatsby-hello-hugo/</id><published>2023-01-14T14:06:05-05:00</published><updated>2023-01-14T14:06:05-05:00</updated><summary type="html">Hugo has ruined me. Other static site generators that rely on community plugins and copypasta to fill out their gaps don&amp;amp;rsquo;t cut it anymore&amp;amp;hellip;</summary><content type="html">
&lt;img alt="" decoding="async" fetchpriority="auto" height="194" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/goodbye-gatsby-cover_hu2445842782457246693.jpg" style="background: #21162c;" width="300"
srcset="https://mademistakes.com/images/goodbye-gatsby-cover_hu2445842782457246693.jpg 300w,https://mademistakes.com/images/goodbye-gatsby-cover_hu128234966105913355.jpg 800w, https://mademistakes.com/images/goodbye-gatsby-cover.jpg 2520w"
>&lt;p>Personal sites are a great testing ground for mixing it up and experimenting. Back when &lt;a href="https://www.gatsbyjs.com" rel="noopener">&lt;strong>Gatsby&lt;/strong>&lt;/a> first started buzzing in Jamstack circles, I used that as my excuse to migrate off Jekyll and learn something new.&lt;/p>
&lt;p>As someone who avoids writing JavaScript whenever possible, there were considerable growing pains as dug into the &lt;a href="https://reactjs.org" rel="noopener">&lt;strong>React&lt;/strong>&lt;/a>, &lt;a href="https://graphql.org/" rel="noopener">&lt;strong>GraphQL&lt;/strong>&lt;/a>, and NPM ecosystems. It took time, but I got the site to a place on par with what I had achieved with &lt;a href="https://jekyllrb.com/" rel="noopener">&lt;strong>Jekyll&lt;/strong>&lt;/a>.&lt;/p>
&lt;p>As I used Gatsby more, the reliance on plugins started frustrating me. External dependencies lagging behind Gatsby made updating a chore, and in the case of &lt;a href="https://www.gatsbyjs.com/docs/reference/release-notes/migrating-from-v4-to-v5/" rel="noopener">migrating to Gatsby 5&lt;/a> &amp;mdash; impossible.&lt;/p>
&lt;p>For example, critical remark plugins related to &lt;a href="https://github.com/zestedesavoir/zmarkdown/issues/416" rel="noopener">Markdown parsing became stuck&lt;/a> blocking me from upgrading, unless I wanted to stop using them&amp;hellip; which I didn&amp;rsquo;t.&lt;/p>
&lt;h2 id="static-site-generator-choices">&lt;a href="#static-site-generator-choices">Static site generator choices&lt;/a>&lt;/h2>
&lt;p>So I started looking around. &lt;a href="/articles/going-static/">Jekyll seemed like a logical&lt;/a> choice, but returning to Ruby&amp;hellip; no thanks. Not to mention the &lt;a href="https://talk.jekyllrb.com/t/is-the-jekyll-project-dead/6820/20" rel="noopener">appearance of Jekyll fizzling out&lt;/a> wasn&amp;rsquo;t doing it any favors.&lt;/p>
&lt;p>&lt;a href="https://www.11ty.dev/" rel="noopener">&lt;strong>Eleventy&lt;/strong> (11ty)&lt;/a> is what all the cool kids are using, and it shares a lot of Jekyll-isms. And then there is &lt;a href="https://gohugo.io/" rel="noopener">&lt;strong>Hugo&lt;/strong>&lt;/a>, which made a splash years back when &lt;a href="https://www.smashingmagazine.com/2019/05/switch-wordpress-hugo/" rel="noopener">&lt;strong>Smashing Magazine&lt;/strong> switched&lt;/a> from WordPress to it. Maybe these were good static site generators to explore?&lt;/p>
&lt;p>For a few weeks, I worked on a proof of concept, migrating layouts and content from Gatsby over to 11ty. It all felt &lt;em>Jekylly&lt;/em> but with added flexibility and none of the Ruby pain.&lt;/p>
&lt;p>Then I encountered a roadblock (more of a detour) with &lt;a href="https://www.11ty.dev/docs/collections/" rel="noopener">collections&lt;/a> and how 11ty uses tags to assign them. I&amp;rsquo;m sure I could have worked around it, but conceptually it clashes with how I organize content: &lt;strong>categories as sections &amp;gt; tags for loosely related topics&lt;/strong>.&lt;/p>
&lt;p>On the other hand, Hugo seemed purpose-built for me. Full of strong conventions on &lt;em>how to do things&lt;/em>, but with enough flexibility to bend as needed. This was a stark contrast with what I had experienced trying to organize and write content with Gatsby, 11ty, and to a lesser degree Jekyll. With those SSGs, I often felt like I was rolling my own solution to do something that &lt;em>should be standard&lt;/em>.&lt;/p>
&lt;h2 id="feature-comparisons">&lt;a href="#feature-comparisons">Feature comparisons&lt;/a>&lt;/h2>
&lt;p>Want to dump a bunch of Markdown files in a folder with a sprinkling of front matter&amp;hellip; you can. But if you want to level up, Hugo offers up ways of &lt;a href="https://gohugo.io/content-management/organization/#page-bundles" rel="noopener">bundling pages&lt;/a> into &lt;a href="https://gohugo.io/content-management/sections/" rel="noopener">sections&lt;/a> alongside &lt;a href="https://gohugo.io/content-management/page-resources/" rel="noopener">resources&lt;/a> so you can keep your content nice and tidy.&lt;/p>
&lt;p>When following conventions Hugo expects, the file structure is used to inform a page&amp;rsquo;s &lt;strong>section&lt;/strong>, &lt;strong>content type&lt;/strong>, whether a branch or leaf node, its layout, and more. Making the building of menus, &lt;a href="https://gohugo.io/content-management/sections/#example-breadcrumb-navigation" rel="noopener">breadcrumb navigation&lt;/a>, list pages, and &lt;a href="https://gohugo.io/templates/pagination/" rel="noopener">pagination&lt;/a> straightforward as they are fully integrated into Hugo&amp;rsquo;s core features.&lt;/p>
&lt;p>Building basic blog sites with posts listed in reverse chronological order, it&amp;rsquo;s easy to miss out on all that Hugo offers. And to be honest, unless you have prior experience with static site generators or fully read through Hugo&amp;rsquo;s (at times obtuse) documentation, you can miss out on the benefits of having the following features.&lt;/p>
&lt;h3 id="markdown-render-hooks">&lt;a href="#markdown-render-hooks">Markdown render hooks&lt;/a>&lt;/h3>
&lt;p>Coming from Gatsby, I now expect all static site generators to take image references written in Markdown and spit out resized versions with appropriate responsive &lt;code>srcset&lt;/code> markup.&lt;/p>
&lt;p>I give you this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-md" data-lang="md">&lt;span class="line">&lt;span class="cl">![&lt;span class="nt">Chicken wings covered in hot sauce&lt;/span>](&lt;span class="na">chicken-wings.jpg&lt;/span>)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>You give me this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">img&lt;/span> &lt;span class="na">alt&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;Chicken wings covered in hot sauce&amp;#34;&lt;/span> &lt;span class="na">src&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;chicken-wings.jpg&amp;#34;&lt;/span> &lt;span class="na">srcset&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;chicken-wings-300.jpg 300w, chicken-wings-600.jpg 600w, chicken-wings-900 900w&amp;#34;&lt;/span> &lt;span class="na">sizes&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;(max-width: 900px) 100vw, 900px&amp;#34;&lt;/span> &lt;span class="na">loading&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;lazy&amp;#34;&lt;/span> &lt;span class="na">decoding&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;async&amp;#34;&lt;/span> &lt;span class="na">width&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;900&amp;#34;&lt;/span> &lt;span class="na">height&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;600&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>I fought with Jekyll for years trying to do this. The common pitfalls are:&lt;/p>
&lt;ul>
&lt;li>Image assets live outside of your &lt;code>_posts&lt;/code> or collection folders. Linking to them with relative paths in Markdown files is more difficult than it should be.&lt;/li>
&lt;li>Every Jekyll &lt;a href="https://github.com/wildlyinaccurate/jekyll-responsive-image" rel="noopener">responsive images plugin&lt;/a> I tried used some flavor of &lt;strong>ImageMagick&lt;/strong> to convert images. ImageMagick is slow and slogging through thousands of files crushed my build times. You can sort of get around this by using a combination of &lt;a href="https://sharp.pixelplumbing.com/" rel="noopener">&lt;strong>Sharp&lt;/strong>&lt;/a> and &lt;a href="https://gulpjs.com/" rel="noopener">&lt;strong>Gulp&lt;/strong>&lt;/a>, but it hardly feels like the &lt;em>Jekyll way&lt;/em>.&lt;/li>
&lt;li>Using Liquid tags e.g., &lt;code>{% responsive_image path: chicken-wings.jpg alt: &amp;quot;Chicken wings covered in hot sauce&amp;quot; %}&lt;/code> is less than ideal if you&amp;rsquo;re committed to an all-Markdown workflow. A &lt;a href="https://gist.github.com/tadamcz/869802c919c72172486f219751904108" rel="noopener">&lt;code>pre_render&lt;/code> hook plugin&lt;/a> that replaces Markdown image references with an appropriate &lt;code>{% responsive_image %}&lt;/code> tag can get around this, but feels hacky.&lt;/li>
&lt;/ul>
&lt;p>11ty with the Sharp-based &lt;a href="https://github.com/11ty/eleventy-img" rel="noopener">&lt;strong>eleventy-img&lt;/strong>&lt;/a> plugin and a &lt;a href="https://tomichen.com/blog/posts/20220416-responsive-images-in-markdown-with-eleventy-image/" rel="noopener">custom image renderer&lt;/a> for &lt;strong>markdown-it&lt;/strong> can get you closer. Still, for me, there was enough flakiness going on &amp;mdash; missing images and random build crashes &amp;mdash; encouraging me to look elsewhere. It&amp;rsquo;s an &lt;a href="https://github.com/11ty/eleventy/issues/2428#issuecomment-1152703912" rel="noopener">open issue&lt;/a>, so here&amp;rsquo;s hoping that someday there will be an enhancement to improve it.&lt;/p>
&lt;p>Then there&amp;rsquo;s Hugo, which lets you customize how Markdown outputs to HTML with what it calls &lt;a href="https://gohugo.io/templates/render-hooks/" rel="noopener">&lt;strong>render hooks&lt;/strong>&lt;/a>. These render hook templates are standard HTML with the same &lt;a href="https://gohugo.io/templates/introduction/" rel="noopener">templating logic&lt;/a> used to write shortcodes, partials, and layouts. Something I appreciate, as writing HTML comes more naturally to me than JavaScript or Ruby.&lt;/p>
&lt;p>I&amp;rsquo;m using a render hook (&lt;code>render-image.html&lt;/code>) to process Markdown images into responsive image markup like so:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">{{- $sizes := (slice &amp;#34;300&amp;#34; &amp;#34;768&amp;#34; &amp;#34;1024&amp;#34; &amp;#34;1280&amp;#34;) -}}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{{- /* Get file that matches the filename as specified as src=&amp;#34;&amp;#34; */ -}}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{{- $src := .Page.Resources.GetMatch .Destination -}}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{{- if and (not $src) .Page.File -}}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> {{- $path := path.Join .Page.File.Dir .Destination -}}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> {{- $src = resources.Get $path -}}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{{- end -}}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{{- $alt := .PlainText | safeHTML -}}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{{- /* Conditional for resources that aren&amp;#39;t in page bundle. */ -}}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{{- if $src -}}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> {{- $color := delimit (first 1 $src.Colors) &amp;#34;&amp;#34; -}}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">img&lt;/span> &lt;span class="na">sizes&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;(min-width: 1440px) 690px, (min-width: 1024px) 790px, 94vw&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">srcset&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s"> {{- range $sizes }}
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s"> {{- if ge $src.Width . }}{{ ($src.Resize (printf &amp;#34;&lt;/span>&lt;span class="err">%&lt;/span>&lt;span class="na">sx&lt;/span>&lt;span class="err">&amp;#34;&lt;/span> &lt;span class="err">.)).&lt;/span>&lt;span class="na">RelPermalink&lt;/span> &lt;span class="err">}}&lt;/span> &lt;span class="err">{{&lt;/span> &lt;span class="err">(&lt;/span>&lt;span class="na">printf&lt;/span> &lt;span class="err">&amp;#34;%&lt;/span>&lt;span class="na">sw&lt;/span>&lt;span class="err">&amp;#34;&lt;/span> &lt;span class="err">.)&lt;/span> &lt;span class="err">}},&lt;/span> &lt;span class="err">{{&lt;/span> &lt;span class="na">end&lt;/span> &lt;span class="err">}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="err">{{&lt;/span>&lt;span class="na">-&lt;/span> &lt;span class="na">end&lt;/span> &lt;span class="na">-&lt;/span>&lt;span class="err">}}&lt;/span> &lt;span class="err">{{&lt;/span> &lt;span class="err">$&lt;/span>&lt;span class="na">src&lt;/span>&lt;span class="err">.&lt;/span>&lt;span class="na">RelPermalink&lt;/span> &lt;span class="err">}}&lt;/span> &lt;span class="err">{{&lt;/span> &lt;span class="err">$&lt;/span>&lt;span class="na">src&lt;/span>&lt;span class="err">.&lt;/span>&lt;span class="na">Width&lt;/span> &lt;span class="err">}}&lt;/span>&lt;span class="na">w&lt;/span>&lt;span class="err">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="err">{{&lt;/span>&lt;span class="na">-&lt;/span> &lt;span class="err">/*&lt;/span> &lt;span class="na">If&lt;/span> &lt;span class="na">smaller&lt;/span> &lt;span class="na">than&lt;/span> &lt;span class="na">768px&lt;/span> &lt;span class="na">wide&lt;/span>&lt;span class="err">,&lt;/span> &lt;span class="na">then&lt;/span> &lt;span class="na">load&lt;/span> &lt;span class="na">the&lt;/span> &lt;span class="na">original&lt;/span> &lt;span class="err">*/&lt;/span> &lt;span class="na">-&lt;/span>&lt;span class="err">}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="err">{{&lt;/span> &lt;span class="na">if&lt;/span> &lt;span class="na">ge&lt;/span> &lt;span class="err">$&lt;/span>&lt;span class="na">src&lt;/span>&lt;span class="err">.&lt;/span>&lt;span class="na">Width&lt;/span> &lt;span class="err">&amp;#34;&lt;/span>&lt;span class="na">768&lt;/span>&lt;span class="err">&amp;#34;&lt;/span> &lt;span class="err">}}&lt;/span> &lt;span class="na">src&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ ($src.Resize &amp;#34;&lt;/span>&lt;span class="na">768x&lt;/span>&lt;span class="err">&amp;#34;).&lt;/span>&lt;span class="na">RelPermalink&lt;/span> &lt;span class="err">}}&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="err">{{&lt;/span> &lt;span class="na">else&lt;/span> &lt;span class="err">}}&lt;/span>&lt;span class="na">src&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ $src.RelPermalink }}&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="err">{{&lt;/span>&lt;span class="na">-&lt;/span> &lt;span class="na">end&lt;/span> &lt;span class="na">-&lt;/span>&lt;span class="err">}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">alt&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ $alt }}&amp;#34;&lt;/span> &lt;span class="na">loading&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;lazy&amp;#34;&lt;/span> &lt;span class="na">decoding&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;async&amp;#34;&lt;/span> &lt;span class="na">width&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ $src.Width }}&amp;#34;&lt;/span> &lt;span class="na">height&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ $src.Height }}&amp;#34;&lt;/span> &lt;span class="err">{{&lt;/span> &lt;span class="na">if&lt;/span> &lt;span class="na">ne&lt;/span> &lt;span class="err">$&lt;/span>&lt;span class="na">src&lt;/span>&lt;span class="err">.&lt;/span>&lt;span class="na">MediaType&lt;/span>&lt;span class="err">.&lt;/span>&lt;span class="na">SubType&lt;/span> &lt;span class="err">&amp;#34;&lt;/span>&lt;span class="na">png&lt;/span>&lt;span class="err">&amp;#34;&lt;/span> &lt;span class="err">}}&lt;/span>&lt;span class="na">style&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;background: {{ $color }}&amp;#34;&lt;/span>&lt;span class="err">{{&lt;/span>&lt;span class="na">-&lt;/span> &lt;span class="na">end&lt;/span> &lt;span class="na">-&lt;/span>&lt;span class="err">}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{{- else -}}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">img&lt;/span> &lt;span class="na">src&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ .Destination | safeURL }}&amp;#34;&lt;/span> &lt;span class="na">alt&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ $alt }}&amp;#34;&lt;/span> &lt;span class="na">loading&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;lazy&amp;#34;&lt;/span> &lt;span class="na">decoding&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;async&amp;#34;&lt;/span> &lt;span class="p">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{{- end -}}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Add anchor links (&lt;code>render-heading.html&lt;/code>) to headings:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">h&lt;/span>&lt;span class="err">{{&lt;/span> &lt;span class="err">.&lt;/span>&lt;span class="na">Level&lt;/span> &lt;span class="err">}}&lt;/span> &lt;span class="na">id&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ .Anchor | safeURL }}&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">a&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;#{{ .Anchor | safeURL }}&amp;#34;&lt;/span> &lt;span class="na">title&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;Permalink to {{ .Text | plainify }}&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>{{ .Text | safeHTML }}&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">a&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&amp;lt;&lt;/span>/h{{ .Level }}&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>And add a &lt;code>rel=&amp;quot;noopener&amp;quot;&lt;/code> attribute to external links (&lt;code>render-link.html&lt;/code>):&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">a&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ .Destination | safeURL }}&amp;#34;&lt;/span>&lt;span class="err">{{&lt;/span> &lt;span class="na">with&lt;/span> &lt;span class="err">.&lt;/span>&lt;span class="na">Title&lt;/span>&lt;span class="err">}}&lt;/span> &lt;span class="na">title&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ . }}&amp;#34;&lt;/span>&lt;span class="err">{{&lt;/span> &lt;span class="na">end&lt;/span> &lt;span class="err">}}{{&lt;/span> &lt;span class="na">if&lt;/span> &lt;span class="na">strings&lt;/span>&lt;span class="err">.&lt;/span>&lt;span class="na">HasPrefix&lt;/span> &lt;span class="err">.&lt;/span>&lt;span class="na">Destination&lt;/span> &lt;span class="err">&amp;#34;&lt;/span>&lt;span class="na">http&lt;/span>&lt;span class="err">&amp;#34;&lt;/span> &lt;span class="err">}}&lt;/span> &lt;span class="na">rel&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;noopener&amp;#34;&lt;/span>&lt;span class="err">{{&lt;/span> &lt;span class="na">end&lt;/span> &lt;span class="err">}}&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>{{ .Text | safeHTML }}&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">a&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="archetype-templates">&lt;a href="#archetype-templates">Archetype templates&lt;/a>&lt;/h3>
&lt;p>I write new posts so infrequently that it&amp;rsquo;s not uncommon for me to forget the names of front matter fields used in site layouts. To get around this I clone a &lt;em>starter&lt;/em> Markdown document with a pre-configured placeholder front matter and draft a new post from that.&lt;/p>
&lt;p>Hugo improves on this with the concept of &lt;a href="https://gohugo.io/content-management/archetypes/" rel="noopener">archetypes&lt;/a> &amp;mdash; directory-based template files used for creating new content.&lt;/p>
&lt;p>Archetype templates look a lot like one of my starter &lt;code>.md&lt;/code> files but with template logic and variables to pre-populate front matter fields like &lt;code>title&lt;/code> and &lt;code>date&lt;/code>.&lt;/p>
&lt;p>When running the &lt;code>hugo new&lt;/code> command to create a new file, an appropriate archetype is used based on the document&amp;rsquo;s file path. This flexibility allows you to have different sets of archetype templates based on content type or section.&lt;/p>
&lt;p>My &lt;code>/archetypes/default.md&lt;/code> template looks like this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nn">---&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">title&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;{{ replace .Name &amp;#34;&lt;/span>-&lt;span class="s2">&amp;#34; &amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34; | title }}&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">date&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>{{&lt;span class="w"> &lt;/span>&lt;span class="l">.Date }}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">last_modified_at&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">draft&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">description&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">image&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">toc&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nn">---&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Which when used with &lt;code>hugo new notes/my-new-note.md&lt;/code> generates a new Markdown file with the following starter contents:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nn">---&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">title&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;My New Note&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">date&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="ld">2023-01-12T14:04:39&lt;/span>&lt;span class="m">-05&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="m">00&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">last_modified_at&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">draft&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">description&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">image&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">toc&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nn">---&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The simplicity of creating templates in the same YAML and Markdown format as the rest of the site content makes a ton of sense to me. While other static site generators require: a content management system, scripts, task runners, or in Jekyll-land the &lt;a href="https://github.com/jekyll/jekyll-compose" rel="noopener">&lt;strong>jekyll-compose&lt;/strong>&lt;/a> Ruby gem &amp;mdash; to do what Hugo does natively.&lt;/p>
&lt;h3 id="taxonomies">&lt;a href="#taxonomies">Taxonomies&lt;/a>&lt;/h3>
&lt;p>If you use taxonomies like &lt;code>categories&lt;/code> or &lt;code>tags&lt;/code> to group content, the popular SSGs have you covered as Jekyll has &lt;a href="https://jekyllrb.com/docs/collections/" rel="noopener">collection support&lt;/a> to do custom grouping, but it&amp;rsquo;s not as robust as what Hugo offers.&lt;/p>
&lt;p>11ty also has collections but &lt;a href="https://www.11ty.dev/docs/collections/" rel="noopener">confusingly calls them &lt;code>tags&lt;/code>&lt;/a>, which is inconvenient if you&amp;rsquo;re coming from Jekyll or any other blogging platform.&lt;/p>
&lt;p>Gatsby has no concept of categories or tags and it is up to you to structure, organize, assign relationships, and write custom logic to filter content. I had a hell of a time writing my &lt;code>gatsby-node.js&lt;/code> file to generate paginated archive pages for categories and tags.&lt;/p>
&lt;p>And then there is Hugo, which &lt;a href="https://gohugo.io/content-management/taxonomies/" rel="noopener">automatically creates taxonomy&lt;/a> pages for categories and tags. If that&amp;rsquo;s not enough for you, Hugo also natively supports:&lt;/p>
&lt;ul>
&lt;li>Creating custom taxonomies to handle relationships other than categories and tags.&lt;/li>
&lt;li>Ordering of taxonomies by weights allows the same piece of content to appear in different positions in different taxonomies.&lt;/li>
&lt;li>&lt;a href="https://gohugo.io/content-management/taxonomies/#add-custom-metadata-to-a-taxonomy-or-term" rel="noopener">Adding metadata to a taxonomy or term&lt;/a>, something I used to hack together with YAML data files in Jekyll and Gatsby.&lt;/li>
&lt;/ul>
&lt;h3 id="content-excerpts">&lt;a href="#content-excerpts">Content excerpts&lt;/a>&lt;/h3>
&lt;p>Gatsby, Jekyll, and Hugo all have support for automatically creating shortened excerpts of a page&amp;rsquo;s main content to be used as teaser/preview text in RSS feeds, meta descriptions, and the like. Each of these SSGs uses separators to mark what part of the content should be used as an excerpt.&lt;/p>
&lt;ul>
&lt;li>Gatsby (via &lt;a href="https://www.gatsbyjs.com/plugins/gatsby-transformer-remark/#excerpts" rel="noopener">&lt;strong>gatsby-transformer-remark&lt;/strong>&lt;/a>) allows you to configure the length of excerpts and their format (plain text, HTML, or Markdown).&lt;/li>
&lt;li>Jekyll uses the &lt;a href="https://jekyllrb.com/docs/posts/#post-excerpts" rel="noopener">first paragraph as an excerpt&lt;/a>.&lt;/li>
&lt;li>Hugo calls them &lt;a href="https://gohugo.io/content-management/summaries/" rel="noopener">content summaries&lt;/a> and uses the first 70 words as an excerpt.&lt;/li>
&lt;/ul>
&lt;p>11ty requires &lt;a href="https://www.11ty.dev/docs/data-frontmatter-customize/" rel="noopener">front matter customizations to parse excerpts&lt;/a> and separators from content to function how the three SSGs above do.&lt;/p>
&lt;h3 id="image-processing">&lt;a href="#image-processing">Image processing&lt;/a>&lt;/h3>
&lt;p>Gatsby, Hugo, and 11ty all support image processing. Gatsby has the advantage if you&amp;rsquo;re using components as it can do responsive images with &lt;a href="https://using-gatsby-image.gatsbyjs.org/" rel="noopener">&lt;em>trendy loading effects&lt;/em>&lt;/a> like blur up, background color, and traced SVG via props and GraphQL queries.&lt;/p>
&lt;p>On the flip side, if you&amp;rsquo;re bringing your own HTML and need a utility to transform and manipulate images &amp;mdash; Hugo and 11ty have you covered.&lt;/p>
&lt;p>The differences come down to how images are processed and rendered:&lt;/p>
&lt;ol>
&lt;li>Gatsby has &lt;a href="https://www.gatsbyjs.com/plugins/gatsby-plugin-image/#using-the-gatsby-image-components" rel="noopener">React image components&lt;/a> and the magical black box that is &lt;a href="https://www.gatsbyjs.com/plugins/gatsby-remark-images/" rel="noopener">&lt;strong>gatsby-remark-images&lt;/strong>&lt;/a>.&lt;/li>
&lt;li>11ty has &lt;a href="https://www.11ty.dev/docs/plugins/image/" rel="noopener">&lt;strong>eleventy-img&lt;/strong>&lt;/a> which can be used in layouts and shortcodes.&lt;/li>
&lt;li>Hugo has image filters you can weave into layouts, shortcodes, and render hooks.&lt;/li>
&lt;/ol>
&lt;p>No surprise here, but I prefer the Hugo way. Using variables and piping on filters feels more natural than GraphQL queries and JavaScript. For example, a &lt;code>feature&lt;/code> image defined in the front matter can be transformed (resized to &lt;code>600px&lt;/code> wide) like so:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">{{ $source := .Page.Resources.GetMatch .Params.feature }}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{{ $image := $source.Resize &amp;#34;600x&amp;#34; }}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">img&lt;/span> &lt;span class="na">src&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ $image.RelPermalink }}&amp;#34;&lt;/span> &lt;span class="na">width&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ $image.Width }}&amp;#34;&lt;/span> &lt;span class="na">height&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ $image.Height }}&amp;#34;&lt;/span> &lt;span class="na">alt&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Adjusting an image&amp;rsquo;s hue, brightness, sharpness, and adding overlays are a &lt;a href="https://gohugo.io/functions/images/" rel="noopener">set of filters&lt;/a> away with Hugo.&lt;/p>
&lt;h4 id="colors">&lt;a href="#colors">Colors&lt;/a>&lt;/h4>
&lt;p>I started using Hugo&amp;rsquo;s new &lt;a href="https://gohugo.io/content-management/image-processing/#colors" rel="noopener">colors method&lt;/a> to return the dominant colors of an image. I then limit this array to the first value and set it as a &lt;code>background-color&lt;/code> to serve as a &lt;em>low quality image placeholder&lt;/em> (LQIP) before it fully loads.&lt;/p>
&lt;p>Don&amp;rsquo;t worry, I&amp;rsquo;m also setting a &lt;code>width&lt;/code> and &lt;code>height&lt;/code> on all images to avoid layout shifts during load.&lt;/p>
&lt;div class="browser-frame">&lt;img alt="Screenshot of gallery page with loading images showing solid backgrounds" class="img--md" decoding="async" fetchpriority="auto" height="246" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/mm-gallery-lqip-screenshot_hu14623935192140083533.jpg" style="background: #efefee;" width="300"
&lt;pre>&lt;code> srcset=&amp;quot;https://mademistakes.com/images/mm-gallery-lqip-screenshot_hu14623935192140083533.jpg 300w, https://mademistakes.com/images/mm-gallery-lqip-screenshot_hu14060119479471555584.jpg 620w&amp;quot;
&amp;gt;
&lt;/code>&lt;/pre>
&lt;/div>
&lt;aside class="notice">&lt;h3>Pro tip&lt;/h3>&lt;p>Gradient image placeholders pair well with Hugo&amp;rsquo;s &lt;code>.Color&lt;/code> method described in this article on &lt;a href="https://www.brycewray.com/posts/2022/09/new-way-lqips-hugo-0-104-0/" rel="noopener">how to generate LQIPs&lt;/a>.&lt;/p>
&lt;/aside>
&lt;h3 id="related-content">&lt;a href="#related-content">Related content&lt;/a>&lt;/h3>
&lt;p>To encourage browsing, I include links to related content at the end of each post. I&amp;rsquo;ve managed these groupings and relationships:&lt;/p>
&lt;ol>
&lt;li>Manually in the front matter.&lt;/li>
&lt;li>Automatically with plugins.&lt;/li>
&lt;/ol>
&lt;p>Thankfully, both Hugo and Jekyll have built-in methods for accessing related content automatically.&lt;/p>
&lt;p>Hugo can list a page&amp;rsquo;s related content based on the front matter parameters you define. While Jekyll&amp;rsquo;s related posts module uses &lt;a href="https://github.com/jekyll/classifier-reborn" rel="noopener">&lt;strong>classifier-reborn&lt;/strong>&lt;/a> to create relationships, is limited to the posts collection, and is slow.&lt;/p>
&lt;p>Each can be looped through using Go and Liquid:&lt;/p>
&lt;p>&lt;strong>Related posts via Go:&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">{{ $related := .Site.RegularPages.Related . | first 3 }}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{{ with $related }}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">h3&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>Related posts&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">h3&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">ul&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> {{ range . }}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">li&lt;/span>&lt;span class="p">&amp;gt;&amp;lt;&lt;/span>&lt;span class="nt">a&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ .RelPermalink }}&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>{{ .Title }}&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">a&lt;/span>&lt;span class="p">&amp;gt;&amp;lt;/&lt;/span>&lt;span class="nt">li&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> {{ end }}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">ul&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{{ end }}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>Related posts via Liquid:&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">{% if site.related_posts.size &amp;gt;= 1 %}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">h3&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>Related posts&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">h3&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">ul&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> {% for related_post in site.related_posts limit: 3 %}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">li&lt;/span>&lt;span class="p">&amp;gt;&amp;lt;&lt;/span>&lt;span class="nt">a&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ related_post.url }}&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>{{ related_post.title }}&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">a&lt;/span>&lt;span class="p">&amp;gt;&amp;lt;/&lt;/span>&lt;span class="nt">li&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> {% endfor %}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">ul&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{% endif %}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>With Gatsby, I struggled with GraphQL queries and an earlier version of &lt;a href="https://www.npmjs.com/package/gatsby-remark-related-posts/v/1.0.8" rel="noopener">&lt;strong>gatsby-remark-related-posts&lt;/strong>&lt;/a> to automatically determine related posts. From the documentation, &lt;a href="https://github.com/sititou70/gatsby-remark-related-posts" rel="noopener">version 2 of the plugin&lt;/a> seems to have improved on the clunkiness. But I can&amp;rsquo;t verify if that&amp;rsquo;s true as I haven&amp;rsquo;t tried upgrading yet.&lt;/p>
&lt;p>11ty didn&amp;rsquo;t appear to have a native way of &lt;a href="https://github.com/11ty/eleventy/discussions/2534" rel="noopener">listing related content&lt;/a>. Methods I dug up involved manual effort and maintenance &amp;mdash; add tags to content in the front matter then filter on those tags.&lt;/p>
&lt;h3 id="automatic-table-of-contents">&lt;a href="#automatic-table-of-contents">Automatic table of contents&lt;/a>&lt;/h3>
&lt;p>Gatsby&amp;rsquo;s &lt;a href="https://www.gatsbyjs.com/plugins/gatsby-transformer-remark/#configuring-the-tableofcontents" rel="noopener">remark transformer&lt;/a>, Jekyll via a &lt;a href="https://kramdown.gettalong.org/converter/html.html#toc" rel="noopener">Kramdown &lt;code>{: toc}&lt;/code> reference&lt;/a>, 11ty plugin &lt;a href="https://github.com/jdsteinbach/eleventy-plugin-toc" rel="noopener">&lt;strong>eleventy-plugin-toc&lt;/strong>&lt;/a>, and Hugo all take Markdown files and generate a &lt;a href="https://gohugo.io/content-management/toc/" rel="noopener">table of contents&lt;/a> from the headings. Useful for creating jump links to skip lengthy bits of content.&lt;/p>
&lt;h2 id="conclusion">&lt;a href="#conclusion">Conclusion&lt;/a>&lt;/h2>
&lt;p>If it wasn&amp;rsquo;t clear already &amp;mdash; I ditched Gatsby and migrated this site over to Hugo. Hugo is often viewed as the fastest static site generator&amp;hellip; it is.&lt;/p>
&lt;p>It excels at &lt;a href="https://gohugo.io/content-management/" rel="noopener">content management&lt;/a>, with a flexible organizational structure, multilingual mode, and menu system that all integrate with one another. It is clear that Hugo&amp;rsquo;s maintainers carefully consider each feature and how they should work together.&lt;/p>
&lt;p>&lt;em>Hugo has ruined me.&lt;/em> Other static site generators that rely on community plugins and copypasta to fill out their gaps don&amp;rsquo;t cut it anymore&amp;hellip;&lt;/p>&lt;p>&lt;a href="https://mademistakes.com/notes/goodbye-gatsby-hello-hugo/?utm_source=atom_feed">Goodbye Gatsby, Hello Hugo&lt;/a> was originally published on Made Mistakes.&lt;/p></content><category scheme="https://mademistakes.com/tag/open-source" term="open-source" label="open source"/><category scheme="https://mademistakes.com/tag/static-sites" term="static-sites" label="static sites"/><category scheme="https://mademistakes.com/tag/web-development" term="web-development" label="web development"/></entry><entry><title type="html">Adding last modified timestamps with Git</title><link href="https://mademistakes.com/notes/adding-last-modified-timestamps-with-git/?utm_source=atom_feed" rel="alternate" type="text/html"/><id>https://mademistakes.com/notes/adding-last-modified-timestamps-with-git/</id><published>2021-08-04T20:34:59-04:00</published><updated>2021-10-28T10:00:22-04:00</updated><summary type="html">Today I learned how to automatically add last modified dates to Markdown files used by static site generators like Jekyll and Gatsby.</summary><content type="html">&lt;p>A common ask of &lt;strong>Jekyll&lt;/strong>, &lt;strong>Gatsby&lt;/strong>, and other static site generator users is, &amp;ldquo;how do I automatically set the date in YAML front matter?&amp;rdquo; Today I learned you can do just that with a Git &lt;a href="https://git-scm.com/docs/githooks#_pre_commit" rel="noopener">pre-commit&lt;/a> hook.&lt;/p>
&lt;p>If your project has been setup with &lt;code>git init&lt;/code> you should have a Git hooks directory with several sample files to inspect.&lt;/p>
&lt;pre tabindex="0">&lt;code>├── .git
│ └── hooks
│ ├── applypatch-msg.sample
│ ├── commit-msg.sample
│ ├── post-update.sample
│ ├── pre-applypatch.sample
│ ├── pre-commit.sample
│ ├── ...
&lt;/code>&lt;/pre>&lt;p>You can either create a new file named &lt;code>pre-commit&lt;/code> inside of the &lt;code>hooks&lt;/code> directory (or rename the &lt;code>.sample&lt;/code> file). Then add the following shell script:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#!/bin/sh
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>&lt;span class="c1"># Contents of .git/hooks/pre-commit&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Replace `last_modified_at` timestamp with current time&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git diff --cached --name-status &lt;span class="p">|&lt;/span> egrep -i &lt;span class="s2">&amp;#34;^(A|M).*\.(md)&lt;/span>$&lt;span class="s2">&amp;#34;&lt;/span> &lt;span class="p">|&lt;/span> &lt;span class="k">while&lt;/span> &lt;span class="nb">read&lt;/span> a b&lt;span class="p">;&lt;/span> &lt;span class="k">do&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> cat &lt;span class="nv">$b&lt;/span> &lt;span class="p">|&lt;/span> sed &lt;span class="s2">&amp;#34;/---.*/,/---.*/s/^last_modified_at:.*&lt;/span>$&lt;span class="s2">/last_modified_at: &lt;/span>&lt;span class="k">$(&lt;/span>date -u &lt;span class="s2">&amp;#34;+%Y-%m-%dT%H:%M:%S&amp;#34;&lt;/span>&lt;span class="k">)&lt;/span>&lt;span class="s2">/&amp;#34;&lt;/span> &amp;gt; tmp
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> mv tmp &lt;span class="nv">$b&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> git add &lt;span class="nv">$b&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">done&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now when you commit a modified file with Git, the value of &lt;code>last_modified_at&lt;/code> will be replaced with the current time i.e., &lt;code>YYYY-MM-DDThh:mm:ss&lt;/code>. &lt;em>If you&amp;rsquo;re using a different front matter variable for modified timestamps, adjust the script above accordingly.&lt;/em>&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Before commit&lt;/th>
&lt;th>After commit&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>last_modified_at: &lt;/code>&lt;/td>
&lt;td>&lt;code>last_modified_at: 2021-08-04T20:34:59&lt;/code>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>I&amp;rsquo;ve always followed the convention of using &lt;code>date&lt;/code> for published timestamp and &lt;code>last_modified_at&lt;/code> for the modified timestamp. Mostly because core &lt;strong>Jekyll&lt;/strong> plugins like &lt;a href="https://github.com/jekyll/jekyll-sitemap" rel="noopener">jekyll-sitemap&lt;/a> and &lt;a href="https://github.com/jekyll/jekyll-feed" rel="noopener">jekyll-feed&lt;/a> support that front matter value.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nn">---&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">title&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;My awesome Markdown post&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">date&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="ld">2020-01-01&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">last_modified_at&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="ld">2021-08-04T20:34:59&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nn">---&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;aside class="notice">&lt;p>&lt;strong>Note:&lt;/strong> The only real gotcha I&amp;rsquo;ve hit with this method is making sure &lt;code>last_modified_at&lt;/code> exists in the front matter. It can be a blank value, but if the variable isn&amp;rsquo;t present then no timestamp is added when the file is committed.&lt;/p>
&lt;/aside>
&lt;p>I&amp;rsquo;d be interested if the script could be improved on to append the date if &lt;code>last_modified_at&lt;/code> hasn&amp;rsquo;t already been added, and replace the value if it has. Let me know below if you have any improves there.&lt;/p>
&lt;p>I&amp;rsquo;m also not sure if this pre-commit hook is automatically installed when the Git repository is cloned. Do I need to install it on both my iMac running macOS and laptop running Windows 10? Or does it come along for the ride when push/pulling from remote?&lt;/p>&lt;p>&lt;a href="https://mademistakes.com/notes/adding-last-modified-timestamps-with-git/?utm_source=atom_feed">Adding last modified timestamps with Git&lt;/a> was originally published on Made Mistakes.&lt;/p></content><category scheme="https://mademistakes.com/tag/static-sites" term="static-sites" label="static sites"/><category scheme="https://mademistakes.com/tag/til" term="til" label="TIL"/><category scheme="https://mademistakes.com/tag/tutorials" term="tutorials" label="tutorials"/></entry><entry><title type="html">URLs and links in Jekyll</title><link href="https://mademistakes.com/mastering-jekyll/how-to-link/?utm_source=atom_feed" rel="alternate" type="text/html"/><id>https://mademistakes.com/mastering-jekyll/how-to-link/</id><published>2021-07-20T10:22:43-04:00</published><updated>2023-12-08T09:18:01-05:00</updated><summary type="html">How Jekyll uses URLs and how to link posts, pages, assets, and other resources together.</summary><content type="html">
&lt;img alt="" decoding="async" fetchpriority="auto" height="169" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/how-to-link-illustration_hu15611894800030481516.png" style="background: #272333;" width="300"
srcset="https://mademistakes.com/images/how-to-link-illustration_hu15611894800030481516.png 300w,https://mademistakes.com/images/how-to-link-illustration_hu5096551534430639034.png 800w, https://mademistakes.com/images/how-to-link-illustration.png 2270w"
>&lt;p>Before you can link pages and assets together with Jekyll, you need to know how it assigns URLs (or permalinks&lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup>) to each. If you are a seasoned Jekyll users and have a good understanding of how this all works, skip down below to &lt;a href="#how-to-link">linking methods&lt;/a>.&lt;/p>
&lt;p>This tutorial will explain the various URL types, how to use them when building with Jekyll, and then how to link content and assets together.&lt;/p>
&lt;h2 id="how-urls-work-in-jekyll">&lt;a href="#how-urls-work-in-jekyll">How URLs work in Jekyll&lt;/a>&lt;/h2>
&lt;p>Jekyll&amp;rsquo;s system for creating URLs is simple and flexible, allowing you to structure files how you want. That is&amp;hellip;once you understand how permalinks work for posts, pages, collection documents, and static files.&lt;/p>
&lt;p>Below are the core concepts and conventions you&amp;rsquo;ll need to get started with each. For an exhaustive list of possible URL patterns and styles, be sure to read &lt;a href="https://jekyllrb.com/docs/permalinks/" rel="noopener">Jekyll&amp;rsquo;s permalink documentation&lt;/a>.&lt;/p>
&lt;h2 id="global-permalinks">&lt;a href="#global-permalinks">Global permalinks&lt;/a>&lt;/h2>
&lt;p>Changing the global output path for posts and pages is one of the first settings you&amp;rsquo;ll configure in &lt;code>_config.yml&lt;/code>. The default pattern used by Jekyll is &lt;code>permalink: date&lt;/code>, which is short for:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">permalink&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">/:categories/:year/:month/:day/:title:output_ext&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This permalink pattern is made up of the &lt;code>:categories&lt;/code>, &lt;code>:year&lt;/code>, &lt;code>:month&lt;/code>, &lt;code>:day&lt;/code>, &lt;code>:title&lt;/code>, and &lt;code>:output_ext&lt;/code> placeholders. For an entire list of placeholders review &lt;a href="https://jekyllrb.com/docs/permalinks/#placeholders" rel="noopener">Jekyll&amp;rsquo;s placeholder documentation&lt;/a>.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Placeholder&lt;/th>
&lt;th>Description&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>categories&lt;/code>&lt;/td>
&lt;td>Assigned categories for a post from its YAML front matter. Multiple categories e.g., &lt;code>categories: [development, CSS]&lt;/code> would output as &lt;code>/development/css&lt;/code>.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>year&lt;/code>&lt;/td>
&lt;td>Year value (four digits e.g., &lt;code>2021&lt;/code>) from the post&amp;rsquo;s filename.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>month&lt;/code>&lt;/td>
&lt;td>Month value (two digits &lt;code>01&lt;/code> through &lt;code>12&lt;/code>) from the post&amp;rsquo;s filename.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>day&lt;/code>&lt;/td>
&lt;td>Day value (two digits &lt;code>01&lt;/code> through &lt;code>31&lt;/code>) from the post&amp;rsquo;s filename.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>title&lt;/code>&lt;/td>
&lt;td>String from the document&amp;rsquo;s filename.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>output_ext&lt;/code>&lt;/td>
&lt;td>Extension of the output file, usually &lt;code>.html&lt;/code>.&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>In addition to the &lt;code>date&lt;/code> permalink, Jekyll has the following patterns built-in for posts. Note: date based placeholders are ignored for documents like pages and collections.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Permalink alias&lt;/th>
&lt;th>Permalink pattern&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>date&lt;/code>&lt;/td>
&lt;td>&lt;code>/:categories/:year/:month/:day/:title:output_ext&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>pretty&lt;/code>&lt;/td>
&lt;td>&lt;code>/:categories/:year/:month/:day/:title/&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>ordinal&lt;/code>&lt;/td>
&lt;td>&lt;code>/:categories/:year/:y_day/:title:output_ext&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>weekdate&lt;/code>&lt;/td>
&lt;td>&lt;code>/:categories/:year/W:week/:short_day/:title:output_ext&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>none&lt;/code>&lt;/td>
&lt;td>&lt;code>/:categories/:title:output_ext&lt;/code>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="url-defaults">&lt;a href="#url-defaults">URL defaults&lt;/a>&lt;/h3>
&lt;p>When building a fresh Jekyll site using the &lt;code>jekyll new&lt;/code> command, the following URL defaults apply. The same is true if your &lt;code>_config.yml&lt;/code> omits the &lt;code>permalink&lt;/code> configuration as Jekyll falls back to the &lt;code>/:categories/:year/:month/:day/:title:output_ext&lt;/code>.&lt;/p>
&lt;h4 id="post-url-defaults">&lt;a href="#post-url-defaults">Post URL defaults&lt;/a>&lt;/h4>
&lt;p>Posts are a special collection in Jekyll and have filename requirements as described in &lt;a href="https://jekyllrb.com/docs/posts/#creating-posts" rel="noopener">their posts documentation&lt;/a>. Because of this &lt;code>YYYY-MM-DD-title.ext&lt;/code> convention, Jekyll is able to determine a post&amp;rsquo;s &lt;code>title&lt;/code> and &lt;code>date&lt;/code> without either being manually added via YAML front matter.&lt;/p>
&lt;p>For example, let&amp;rsquo;s create a new Markdown post and name it &lt;code>2021-06-30-new-post.md&lt;/code>, making sure it&amp;rsquo;s in the &lt;code>_posts&lt;/code> directory.&lt;/p>
&lt;pre tabindex="0">&lt;code>├── _posts
│ └── 2021-06-30-new-post.md
&lt;/code>&lt;/pre>&lt;p>Jekyll knows the title of this post is &lt;code>new post&lt;/code> and the date is &lt;code>2021-06-30&lt;/code> for the reasons stated above.&lt;/p>
&lt;p>When Jekyll builds the site it will use the default permalink pattern of &lt;code>permalink: date&lt;/code> and output a HTML file into the &lt;code>_site&lt;/code> directory with a root-relative URL of &lt;code>/2021/06/30/new-post.html&lt;/code>.&lt;/p>
&lt;pre tabindex="0">&lt;code>├── _site
│ └── 2021
│ └── 06
│ └── 30
│ └── new-post.html
&lt;/code>&lt;/pre>&lt;p>Posts (unlike pages and other collections) have complete support for &lt;code>categories&lt;/code>, which become part of their URL when using the default permalink pattern.&lt;/p>
&lt;p>Using the example post created above and adding categories to it via YAML front matter:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nn">---&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line hl">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">categories&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;development&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;CSS&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nn">---&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The resulting URL would become &lt;code>/development/css/2021/06/30/new-post.html&lt;/code> with this file structure:&lt;/p>
&lt;pre tabindex="0">&lt;code>├── _site
| └── development
| └── css
│ └── 2021
│ └── 06
│ └── 30
│ └── new-post.html
&lt;/code>&lt;/pre>&lt;aside class="notice">&lt;h3>Note: Categories array is hierarchical&lt;/h3>&lt;p>Order of categories is important as Jekyll applies a hierarchy to them. In the example above &lt;code>development&lt;/code> precedes &lt;code>CSS&lt;/code> implying a parent-child relationship to the terms, nesting &lt;code>/css&lt;/code> inside of &lt;code>/development&lt;/code>.&lt;/p>
&lt;/aside>
&lt;aside class="notice">&lt;h3>Pro tip: Slugified categories (Jekyll v4.1+)&lt;/h3>&lt;p>Use &lt;code>slugified_categories&lt;/code> permalink placeholder to downcase and replace non-alphanumeric characters with hyphens.&lt;/p>
&lt;p>For example &lt;code>categories: [Ruby on Rails]&lt;/code> would convert to &lt;code>ruby-on-rails&lt;/code> in the context of a permalink.&lt;/p>
&lt;/aside>
&lt;h4 id="page-url-defaults">&lt;a href="#page-url-defaults">Page URL defaults&lt;/a>&lt;/h4>
&lt;p>A common Jekyll page convention is to add HTML or Markdown files in the root directory. In fact when running the &lt;code>jekyll new&lt;/code> command it scaffolds out the following two pages in the root:&lt;/p>
&lt;pre tabindex="0">&lt;code>├── about.markdown
├── index.html
&lt;/code>&lt;/pre>&lt;p>When Jekyll builds the site it will use the default permalink pattern of &lt;code>permalink: date&lt;/code> and output two HTML files into the &lt;code>_site&lt;/code> directory with root-relative URLs of &lt;code>/about.html&lt;/code> and &lt;code>/index.html&lt;/code>.&lt;/p>
&lt;pre tabindex="0">&lt;code>├── _site
| └── about.html
| └── index.html
&lt;/code>&lt;/pre>&lt;aside class="notice">&lt;h3>Don&amp;rsquo;t let &lt;code>permalink: date&lt;/code> fool you&lt;/h3>&lt;p>Because pages are not date based like posts, even if you add a date via YAML front matter to &lt;code>about.md&lt;/code> like so:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nn">---&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">date&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="ld">2021-07-07&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nn">---&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>It will not output the file into a year/month/day directory structure. You&amp;rsquo;ll still end up with:&lt;/p>
&lt;pre tabindex="0">&lt;code>├── _site
| └── about.html
&lt;/code>&lt;/pre>&lt;p>Note: There are &lt;a href="https://github.com/jekyll/jekyll/issues/7296#issue-366619778" rel="noopener">other inconsistencies&lt;/a> between posts, collections, and pages that have been documented in this GitHub issue.&lt;/p>
&lt;/aside>
&lt;p>Pages can be organized into sub-directories and Jekyll will respect that structure and output matching folders and files. For example, if we create a page named &lt;code>privacy-policy.md&lt;/code> and place it a directory named &lt;code>terms&lt;/code>.&lt;/p>
&lt;pre tabindex="0">&lt;code>├── terms
│ └── privacy-policy.md
&lt;/code>&lt;/pre>&lt;p>When Jekyll builds the privacy policy page it will output a HTML file into the &lt;code>_site&lt;/code> directory with a root-relative URL of &lt;code>/terms/privacy-policy.html&lt;/code>.&lt;/p>
&lt;pre tabindex="0">&lt;code>├── _site
│ └── terms
│ └── privacy-policy.html
&lt;/code>&lt;/pre>&lt;aside class="notice">&lt;h3>Note: Page categories and permalinks&lt;/h3>&lt;p>Pages ignore post specific placeholder parameters like &lt;code>date&lt;/code> and &lt;code>categories&lt;/code>. This is because pages are not date based nor are they included in the &lt;code>site.categories&lt;/code> object. Jekyll will omit these values when constructing URLs for pages.&lt;/p>
&lt;p>For example, if we add &lt;code>categories: [legal]&lt;/code> to the &lt;code>privacy-policy.md&lt;/code> page above, it has no affect on the permalink of the output file.&lt;/p>
&lt;/aside>
&lt;h4 id="collection-url-defaults">&lt;a href="#collection-url-defaults">Collection URL defaults&lt;/a>&lt;/h4>
&lt;p>Collection documents have a default permalink style of &lt;code>permalink: /:collection/:path&lt;/code> when &lt;code>output: true&lt;/code> is enabled. These files output in a similar way to pages. Meaning they ignore some permalink placeholders like categories.&lt;/p>
&lt;p>For example, if we create a collection named recipes with the following minimal configuration:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">collections&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">recipes&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">output&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>And then create two documents &amp;mdash; &lt;code>apple-pie.md&lt;/code> and &lt;code>minestrone.md&lt;/code>, and place them in the &lt;code>_recipes&lt;/code> directory.&lt;/p>
&lt;pre tabindex="0">&lt;code>├── _recipes
│ ├── apple-pie.md
│ └── minestrone.md
&lt;/code>&lt;/pre>&lt;p>When Jekyll builds the site it will use the default permalink pattern of &lt;code>permalink: /:collection/:path&lt;/code> and output two HTML files into the &lt;code>_site&lt;/code> directory with root-relative URLs of &lt;code>/recipes/apple-pie.html&lt;/code> and &lt;code>/recipes/minestrone.html&lt;/code>.&lt;/p>
&lt;pre tabindex="0">&lt;code>├── _site
│ └── recipes
│ ├── apple-pie.html
│ └── minestrone.html
&lt;/code>&lt;/pre>&lt;p>Collection documents can be organized into sub-directories (just like pages) and Jekyll will respect that structure and output matching folders and files.&lt;/p>
&lt;p>For example, if we group the apple pie and minestrone recipes into deserts and soups directories inside of &lt;code>_recipes&lt;/code>:&lt;/p>
&lt;pre tabindex="0">&lt;code>├── _recipes
│ ├── deserts
│ │ └── apple-pie.md
│ └── soups
│ └── minestrone.md
&lt;/code>&lt;/pre>&lt;p>Now when Jekyll builds the site these two recipes will output into the following structure:&lt;/p>
&lt;pre tabindex="0">&lt;code>├── _site
│ └── recipes
│ ├── deserts
│ │ └── apple-pie.html
│ └── soups
│ └── minestrone.html
&lt;/code>&lt;/pre>&lt;h4 id="static-file-url-defaults">&lt;a href="#static-file-url-defaults">Static file URL defaults&lt;/a>&lt;/h4>
&lt;p>Files that do not contain any YAML front matter (aka static files), Jekyll outputs in the same structure it sourced from. This applies to images, videos, PDFs, and other assets that aren&amp;rsquo;t processed via Jekyll&amp;rsquo;s &lt;a href="https://sass-lang.com/" rel="noopener">Sass&lt;/a> and &lt;a href="https://coffeescript.org/" rel="noopener">CoffeeScript&lt;/a> pipelines.&lt;/p>
&lt;p>Like pages and collection documents, static files can be organized in whichever way you choose and Jekyll will match naming and structure. For example, if we have a image named &lt;code>logo.png&lt;/code> and place it in a &lt;code>static/images&lt;/code> sub-directory.&lt;/p>
&lt;pre tabindex="0">&lt;code>├── static
│ └── images
│ └── logo.png
&lt;/code>&lt;/pre>&lt;p>When Jekyll builds the site it will and output the logo into the &lt;code>_site&lt;/code> directory with a root-relative URL of &lt;code>/static/images/logo.png&lt;/code>.&lt;/p>
&lt;pre tabindex="0">&lt;code>├── _site
│ └── static
│ └── images
│ └── logo.png
&lt;/code>&lt;/pre>&lt;h2 id="file-specific-permalinks">&lt;a href="#file-specific-permalinks">File specific permalinks&lt;/a>&lt;/h2>
&lt;p>Permalinks can be set in a file&amp;rsquo;s YAML front matter, overriding globally set patterns in the &lt;code>_config.yml&lt;/code> file. Organizing files in a &lt;code>_pages&lt;/code>&lt;sup id="fnref:2">&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref">2&lt;/a>&lt;/sup> folder and outputting them with a root-relative URL is a common use case.&lt;/p>
&lt;p>For example, if we create a &lt;code>resume.md&lt;/code> page and place it in a &lt;code>_pages&lt;/code> subdirectoy.&lt;/p>
&lt;pre tabindex="0">&lt;code>├── _pages
│ └── resume.md
&lt;/code>&lt;/pre>&lt;p>When Jekyll builds the site it will output the resume page into &lt;code>_pages/resume.html&lt;/code> which isn&amp;rsquo;t what we&amp;rsquo;re looking to achieve. We&amp;rsquo;d much rather it end up in &lt;code>pages/resume.html&lt;/code> without the leading &lt;code>_&lt;/code>.&lt;/p>
&lt;p>You could of course rename the &lt;code>_pages&lt;/code> directory to &lt;code>pages&lt;/code> and get the desired result. But if you&amp;rsquo;re looking to follow the convention of naming Jekyll source directories with underscores, then setting a permalink override is the solution.&lt;/p>
&lt;p>Inside of &lt;code>_pages/resume.md&lt;/code> we can add the following path to force where it outputs to.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nn">---&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">permalink&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">/resume.html&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nn">---&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now when Jekyll builds the site it will output the resume page into:&lt;/p>
&lt;pre tabindex="0">&lt;code>├── _site
│ └── resume.html
&lt;/code>&lt;/pre>&lt;p>We can even change the filename it outputs as:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nn">---&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">permalink&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">/resume/index.html&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nn">---&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;aside class="notice">&lt;h3>Pro tip: Permalink placeholders in YAML front matter&lt;/h3>&lt;p>Any of the placeholders you&amp;rsquo;d typically use to configure a global &lt;code>permalink&lt;/code> pattern in &lt;code>_config.yml&lt;/code> can also be configured in YAML front matter.&lt;/p>
&lt;p>Note: aliases like &lt;code>permalink: pretty&lt;/code> do not work in front matter, you&amp;rsquo;ll need to use the full pattern of &lt;code>/:categories/:year/:month/:day/:title/&lt;/code>.&lt;/p>
&lt;/aside>
&lt;h3 id="duplicate-permalinks">&lt;a href="#duplicate-permalinks">Duplicate permalinks&lt;/a>&lt;/h3>
&lt;p>Different source files with the same &lt;code>permalink&lt;/code> can cause all sorts of confusion when troubleshooting a Jekyll build. Maybe you made some changes to your home page&amp;rsquo;s content and can&amp;rsquo;t figure out why it&amp;rsquo;s not updating. Or maybe you see nothing at all and the page is blank.&lt;/p>
&lt;p>This is quite common when forking or copying other Jekyll repositories and ending up with both &lt;code>index.html&lt;/code> and &lt;code>index.md&lt;/code> files in your project. Since they share a filename, they&amp;rsquo;ll output to the same location and conflict 💥.&lt;/p>
&lt;p>This could also happen if you had a file like &lt;code>_pages/home.md&lt;/code> with &lt;code>permalink: /&lt;/code> or &lt;code>permalink: index.html&lt;/code> added to its YAML front matter.
Thankfully in newer versions, Jekyll&amp;rsquo;s CLI will warn you about such conflicts.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">Conflict: The following destination is shared by multiple files.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">The written file may end up with unexpected contents.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">C:/Users/michael/sites/jekyll-site/_site/index.html
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> - home.md
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> - index.md
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="how-to-link">&lt;a href="#how-to-link">How to link&lt;/a>&lt;/h2>
&lt;p>How do you link posts, pages, documents, images, videos, and other resources together with Jekyll? Before answering that question, it&amp;rsquo;s useful to learn about the following URL types:&lt;/p>
&lt;h3 id="url-types">&lt;a href="#url-types">URL types&lt;/a>&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>&lt;/th>
&lt;th>Type&lt;/th>
&lt;th>Description&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>1&lt;/td>
&lt;td>&lt;strong>Document-relative&lt;/strong>&lt;/td>
&lt;td>URL contains path relative to the current page.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>2&lt;/td>
&lt;td>&lt;strong>Root-relative&lt;/strong>&lt;/td>
&lt;td>URL contains path relative to the site&amp;rsquo;s &lt;em>root directory&lt;/em> &amp;mdash; starts with a forward slash &lt;code>/&lt;/code>.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>3&lt;/td>
&lt;td>&lt;strong>Absolute&lt;/strong>&lt;/td>
&lt;td>Full URL including protocol, domain, port, and or path.&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h4 id="document-relative-urls">&lt;a href="#document-relative-urls">Document-relative URLs&lt;/a>&lt;/h4>
&lt;p>A relative URL that does not start with &lt;code>/&lt;/code>, is &lt;strong>document-relative&lt;/strong> and will instruct the browser to look for the document or file in the context of the current page.&lt;/p>
&lt;p>Meaning if the browser has the following page open: &lt;code>https://mademistakes.com/articles/index.html&lt;/code>, and there is a document-relative link pointing to &lt;code>article-two/index.html&lt;/code> it would browse to the &lt;code>article-two&lt;/code> sub-directory inside of the &lt;code>articles&lt;/code> directory. Then look for a file named &lt;code>index.html&lt;/code>.&lt;/p>
&lt;pre tabindex="0">&lt;code>├── https://mademistakes.com/articles
│ └── index.html (current document)
│ └── articles-two
│ └── index.html
&lt;/code>&lt;/pre>&lt;h5 id="when-do-you-use-document-relative-urls">&lt;a href="#when-do-you-use-document-relative-urls">When do you use document-relative URLs?&lt;/a>&lt;/h5>
&lt;p>In my experience document-relative URLs are not all that common when working with Jekyll unless you have a flat directory structure. The one use case where I think they are helpful is if you want to organize supporting assets with a post in the same directory.&lt;/p>
&lt;pre tabindex="0">&lt;code>├── _posts
│ └── 2021-07-12
│ ├── 2021-07-12-weeknotes-8.md
│ ├── bbq.jpg
│ └── fireworks.jpg
&lt;/code>&lt;/pre>&lt;p>Unfortunately Jekyll doesn&amp;rsquo;t make this easy out of the box as it doesn&amp;rsquo;t know what to do with the non-Markdown files above.&lt;/p>
&lt;p>&lt;code>2021-07-12-weeknotes-8.md&lt;/code> would be read and output as expected to &lt;code>_site/2021/07/12/weeknotes-8.html&lt;/code>, but the static assets &lt;code>bbq.jpg&lt;/code> and &lt;code>fireworks.jpg&lt;/code> would not.&lt;/p>
&lt;p>With the help of Nicolas Hoizey&amp;rsquo;s &lt;a href="https://nhoizey.github.io/jekyll-postfiles/" rel="noopener">&lt;strong>jekyll-postfiles&lt;/strong>&lt;/a> plugin this can be fixed. Once installed, static assets will follow the same &lt;code>permalink&lt;/code> pattern as posts and output alongside them like this:&lt;/p>
&lt;pre tabindex="0">&lt;code>├── _site
│ └── 2021
│ └── 07
│ └── 12
│ ├── weeknotes-8.html
│ ├── impossible-burgers.jpg
│ └── fireworks.jpg
&lt;/code>&lt;/pre>&lt;p>Now we can add document-relative links to images (or other assets) in Markdown and HTML files without them 404ing.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-markdown" data-lang="markdown">&lt;span class="line">&lt;span class="cl">![&lt;span class="nt">Impossible burgers on the grill&lt;/span>](&lt;span class="na">impossible-burgers.jpg&lt;/span>)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">img&lt;/span> &lt;span class="na">src&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;fireworks.jpg&amp;#34;&lt;/span> &lt;span class="na">alt&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;Fireworks exploding in the sky&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="root-relative-urls">&lt;a href="#root-relative-urls">Root-relative URLs&lt;/a>&lt;/h4>
&lt;p>As the name implies, root-relative URLs make the path relative to the root directory of the site, denoted by a forward slash &lt;code>/&lt;/code>.&lt;/p>
&lt;p>Meaning if the browser has the following page open: &lt;code>https://mademistakes.com/about/index.html&lt;/code>, and there is a root-relative link pointing to &lt;code>/legal/privacy-policy/index.html&lt;/code> it would browse to the &lt;code>legal&lt;/code> directory from the root directory. Then down into the &lt;code>privacy-policy&lt;/code> sub-directory, where it would open a file named &lt;code>index.html&lt;/code>.&lt;/p>
&lt;pre tabindex="0">&lt;code>├── https://mademistakes.com
│ ├── about
│ │ └── index.html (current document)
│ └── legal
│ └── privacy-policy
| └── index.html
&lt;/code>&lt;/pre>&lt;p>The forward slash &lt;code>/&lt;/code> is import as it starts from the root of the server. If the &lt;code>/&lt;/code> was omitted and the link was &lt;code>legal/privacy-policy.html&lt;/code>, the browser would try to open &lt;code>https://mademistakes.com/about/legal/privacy-policy/index.html&lt;/code> instead. Which would lead to a 404 error page as this file does not exist on the server.&lt;/p>
&lt;h5 id="when-do-you-use-root-relative-urls">&lt;a href="#when-do-you-use-root-relative-urls">When do you use root-relative URLs?&lt;/a>&lt;/h5>
&lt;p>Root-relative are most often used to link internal pages (e.g., related posts, category and tag archives, an about page, etc.) from the same site together. In my experience they are helpful when building sites with a deep directory structure.&lt;/p>
&lt;p>It&amp;rsquo;s much easier to avoid broken links and 404 pages this way as you don&amp;rsquo;t have to worry page context when crafting URLs.&lt;/p>
&lt;p>Some examples of root-relative URLs are:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-markdown" data-lang="markdown">&lt;span class="line">&lt;span class="cl">[&lt;span class="nt">root-relative file&lt;/span>](&lt;span class="na">/root-relative-file.html&lt;/span>)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="c">&amp;lt;!-- anchor link --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">a&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;/root-relative-file.html&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>root-relative file&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">a&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&amp;lt;!-- sitemap --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">link&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;/sitemap.xml&amp;#34;&lt;/span> &lt;span class="na">rel&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;sitemap&amp;#34;&lt;/span> &lt;span class="na">type&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;application/xml&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&amp;lt;!-- JavaScript file --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">script&lt;/span> &lt;span class="na">src&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;/app.js&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&amp;lt;/&lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="absolute-urls">&lt;a href="#absolute-urls">Absolute URLs&lt;/a>&lt;/h4>
&lt;p>Unlike relative URLs, absolutes or full URLs have all of the necessary elements (protocol, domain, port, and path) needed to resolve a URL.&lt;/p>
&lt;h5 id="when-do-you-use-absolute-urls">&lt;a href="#when-do-you-use-absolute-urls">When do you use absolute URLs?&lt;/a>&lt;/h5>
&lt;p>Absolute URLs are used when linking to pages and files that live outside of the current site.&lt;/p>
&lt;p>For example, the about page on this site links out to Twitter with the following absolute URL &lt;code>https://twitter.com/mmistakes&lt;/code> which contains the protocol &lt;code>https://&lt;/code>, domain &lt;code>twitter.com&lt;/code> and path of &lt;code>/mmistakes&lt;/code>.&lt;/p>
&lt;p>Some examples of absolute URLs are:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-markdown" data-lang="markdown">&lt;span class="line">&lt;span class="cl">[&lt;span class="nt">file on another domain&lt;/span>](&lt;span class="na">https://not-mademistakes.com/absolute-file.html&lt;/span>)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="c">&amp;lt;!-- external anchor link --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">a&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;https://not.mademistakes.com/absolute-file.html&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>absolute file&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">a&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&amp;lt;!-- externally hosted CSS file --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">link&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;https://not.mademistakes.com/assets/styles.min.css&amp;#34;&lt;/span> &lt;span class="na">rel&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;stylesheet&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&amp;lt;!-- externally hosted JavaScript file --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">script&lt;/span> &lt;span class="na">src&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;https://not.mademistakes.com/assets/app.js&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&amp;lt;/&lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="about-siteurl-and-sitebaseurl">&lt;a href="#about-siteurl-and-sitebaseurl">About &lt;code>site.url&lt;/code> and &lt;code>site.baseurl&lt;/code>&lt;/a>&lt;/h3>
&lt;p>
&lt;img alt="Illustration of Jekylls site.url and site.baseurl variables" class="img--md" decoding="async" fetchpriority="auto" height="169" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/jekyll-site-url-baseurl-illustration_hu2908973656200094340.jpg" style="background: #292824;" width="300"
srcset="https://mademistakes.com/images/jekyll-site-url-baseurl-illustration_hu2908973656200094340.jpg 300w, https://mademistakes.com/images/jekyll-site-url-baseurl-illustration_hu10931180283428978987.jpg 620w"
>&lt;/p>
&lt;p>These two Jekyll &lt;code>_config.yml&lt;/code> variables are notorious for breaking links, stylesheets, JavaScript and more. Knowing how they function is important for building working relative and absolute URLs in Jekyll theme files like layouts and includes.&lt;/p>
&lt;aside class="notice">&lt;h3>Level-up opportunity&lt;/h3>&lt;p>In this &lt;a href="/mastering-jekyll/site-url-baseurl/" title="About site.url and site.baseurl">&lt;strong>Mastering Jekyll&lt;/strong> tutorial&lt;/a> learn what Jekyll&amp;rsquo;s site &lt;code>url&lt;/code> and &lt;code>baseurl&lt;/code> variables are and how to properly set them &amp;mdash; avoiding broken links and 404 errors.&lt;/p>
&lt;p>
&lt;img alt="illustration describing site url and baseurl permalink structure" class="img--md" decoding="async" fetchpriority="auto" height="134" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/site-url-baseurl-permalink-example_hu3217062979227421737.jpg" style="background: #28252c;" width="300"
&lt;pre>&lt;code> srcset=&amp;quot;https://mademistakes.com/images/site-url-baseurl-permalink-example_hu3217062979227421737.jpg 300w, https://mademistakes.com/images/site-url-baseurl-permalink-example_hu10431701330470072207.jpg 620w&amp;quot;
&amp;gt;&amp;lt;/p&amp;gt;
&lt;/code>&lt;/pre>
&lt;p>&lt;strong>TL/DR:&lt;/strong> &lt;code>{{ site.url }}&lt;/code> is the protocol and domain your site lives at e.g., &lt;code>https://mmistakes.github.io&lt;/code>. While &lt;code>{{ site.baseurl }}&lt;/code> is something you almost never use unless you&amp;rsquo;re building your site in a non-root directory e.g., &lt;code>/blog&lt;/code> or hosting a project site on GitHub Pages.&lt;/p>
&lt;/aside>
&lt;h3 id="linking-posts">&lt;a href="#linking-posts">Linking posts&lt;/a>&lt;/h3>
&lt;p>There are multiple ways to link to other Jekyll posts:&lt;/p>
&lt;ol>
&lt;li>Reference the post&amp;rsquo;s full root-relative URL e.g., &lt;code>/2021-01-01-how-to-make-pizza.html&lt;/code>.&lt;/li>
&lt;li>Use Jekyll&amp;rsquo;s &lt;a href="https://jekyllrb.com/docs/liquid/tags/#linking-to-posts" rel="noopener">&lt;code>{% post_url %}&lt;/code> tag&lt;/a>.&lt;/li>
&lt;li>Use Jekyll&amp;rsquo;s &lt;a href="https://jekyllrb.com/docs/liquid/tags/#link" rel="noopener">&lt;code>{% link %}&lt;/code> tag&lt;/a>.&lt;/li>
&lt;/ol>
&lt;p>The best way I&amp;rsquo;ve found is to use Jekyll&amp;rsquo;s &lt;code>{% post_url %}&lt;/code> tag as it generates the correct URL even if the &lt;code>permalink&lt;/code> style changes &amp;mdash; &lt;code>baseurl&lt;/code> folder and all.&lt;/p>
&lt;h4 id="-post_url--and--link--tags">&lt;a href="#-post_url--and--link--tags">&lt;code>{% post_url %}&lt;/code> and &lt;code>{% link %}&lt;/code> tags&lt;/a>&lt;/h4>
&lt;p>Jekyll comes with a set of Liquid tags that makes linking easier for content authors and editors. If following the Jekyll convention of organizing posts in the &lt;code>_posts&lt;/code> folder&amp;hellip;&lt;/p>
&lt;pre tabindex="0">&lt;code>├── _posts
│ ├── 2021-01-01-how-to-make-pizza.md
│ └── 2021-05-20-birthday-bash.md
&lt;/code>&lt;/pre>&lt;p>You&amp;rsquo;d link to post &lt;code>2021-01-01-how-to-make-pizza&lt;/code> by referencing its filename, omitting the extension at the end, e.g., &lt;code>.md&lt;/code>.&lt;/p>
&lt;pre tabindex="0">&lt;code class="language-liquid" data-lang="liquid">{% post_url 2021-01-01-how-to-make-pizza %}
&lt;/code>&lt;/pre>&lt;p>Jekyll will then output the following root-relative URL (if using the default permalink pattern of &lt;code>permalink: date&lt;/code>.)&lt;/p>
&lt;pre tabindex="0">&lt;code>/2021/01/01/how-to-make-pizza.html
&lt;/code>&lt;/pre>&lt;hr>
&lt;p>Jekyll also has the &lt;code>{% link %}&lt;/code> tag, which can be used to link to a post, page, collection document, or file. The big difference between &lt;code>link&lt;/code> and &lt;code>post_url&lt;/code> is that the link tag requires the file&amp;rsquo;s full path e.g., directory and extension.&lt;/p>
&lt;p>Using the same &amp;ldquo;How to make pizza&amp;rdquo; post from above, here&amp;rsquo;s how to link to it using Jekyll&amp;rsquo;s &lt;code>link&lt;/code> tag:&lt;/p>
&lt;pre tabindex="0">&lt;code class="language-liquid" data-lang="liquid">{% link _posts/2021-01-01-how-to-make-pizza.md %}
&lt;/code>&lt;/pre>&lt;p>Jekyll then outputs the same root-relative URL as before, &lt;code>/2021/01/01/how-to-make-pizza.html&lt;/code>.&lt;/p>
&lt;p>For links created using either &lt;code>{% post_url %}&lt;/code> or &lt;code>{% link %}&lt;/code>, if the &lt;code>baseurl&lt;/code> config has been set because the site is hosted on GitHub Pages, for example something like this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># _config.yml&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">basewebsite&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">/project-name&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Jekyll will output this root-relative URL instead: &lt;code>/project-name/2021/01/01/post-to-link-to.html&lt;/code>.&lt;/p>
&lt;aside class="notice">&lt;h3>⚠ &lt;code>post_url&lt;/code> and &lt;code>link&lt;/code> with &lt;code>baseurl&lt;/code>&lt;/h3>&lt;p>Jekyll sites build with version 3.0 and configured with a &lt;code>baseurl&lt;/code>, need to prepend &lt;code>post_url&lt;/code> or &lt;code>link&lt;/code> tags with &lt;code>site.baseurl&lt;/code> like this:&lt;/p>
&lt;pre tabindex="0">&lt;code class="language-liquid" data-lang="liquid">{{ site.baseurl }}{% post_url 2021-01-01-how-to-make-pizza %}
{{ site.baseurl }}{% link _posts/2021-01-01-how-to-make-pizza.md %}
&lt;/code>&lt;/pre>&lt;p>Check these &lt;a href="/mastering-jekyll/site-url-baseurl/#examples">baseurl URL examples&lt;/a> if you&amp;rsquo;re still unsure on how to set them up.&lt;/p>
&lt;/aside>
&lt;aside class="notice">&lt;h3>Pro tip: Link validation&lt;/h3>&lt;p>Using the &lt;code>{% post_url %}&lt;/code> tag has the added benefit of letting you know if the post linked to is valid. If a post was renamed or removed this would help identify a potential 404 error before being pushed into production.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">Could not find post &lt;span class="s2">&amp;#34;2099-01-01-welcome-to-the-future.markdown&amp;#34;&lt;/span> in tag &lt;span class="s1">&amp;#39;post_url&amp;#39;&lt;/span>. &lt;span class="o">(&lt;/span>Jekyll::Errors::PostURLError&lt;span class="o">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Make sure the post exists and the name is correct.
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;/aside>
&lt;h4 id="post-link-examples">&lt;a href="#post-link-examples">Post link examples&lt;/a>&lt;/h4>
&lt;p>Creating internal links to other posts is the quickest when &lt;a href="https://daringfireball.net/projects/markdown/syntax#link" title="inline link syntax" rel="noopener">written in Markdown&lt;/a> as it doesn&amp;rsquo;t require as much markup. If you know a post&amp;rsquo;s full path you can reference that entire URL or use Jekyll&amp;rsquo;s &lt;code>post_url&lt;/code> or &lt;code>link&lt;/code> tags that reference the source file.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-markdown" data-lang="markdown">&lt;span class="line">&lt;span class="cl">[&lt;span class="nt">How to make pizza&lt;/span>](&lt;span class="na">/2021/01/01/how-to-make-pizza.html&lt;/span>)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">[&lt;span class="nt">How to make pizza&lt;/span>](&lt;span class="na">{% post_url 2021-01-01-how-to-make-pizza %}&lt;/span>)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">[&lt;span class="nt">How to make pizza&lt;/span>](&lt;span class="na">{% link _posts/2021-01-01-how-to-make-pizza.md %}&lt;/span>)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Or if you prefer to create links in HTML instead, you can.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">a&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;/2021/01/01/how-to-make-pizza.html&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>How to make pizza&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">a&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">a&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{% post_url 2021-01-01-how-to-make-pizza %}&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>How to make pizza&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">a&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">a&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{% link _posts/2021-01-01-how-to-make-pizza.md %}&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>How to make pizza&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">a&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Either way you create the link, Jekyll outputs the same HTML:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">a&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;/2021/01/01/how-to-make-pizza.html&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>How to make pizza&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">a&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;aside class="notice">&lt;h3>&lt;code>post_url&lt;/code> and permalinks&lt;/h3>&lt;p>The &lt;code>post_url&lt;/code> tag respects file specific permalinks set in YAML front matter. Meaning if you have a Markdown link like &lt;code>[How to make pizza]({% post_url 2021-01-01-how-to-make-pizza %})&lt;/code> and this front matter:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nn">---&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">permalink&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">how-to-make-pizza.html&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nn">---&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Jekyll will respect the &lt;code>permalink&lt;/code> override set in front matter and output the following link:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">a&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;/how-to-make-pizza.html&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>How to make pizza&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">a&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;/aside>
&lt;h3 id="linking-pages">&lt;a href="#linking-pages">Linking pages&lt;/a>&lt;/h3>
&lt;p>Like posts, pages can be linked to in multiple ways:&lt;/p>
&lt;ol>
&lt;li>Reference the page&amp;rsquo;s full root-relative URL e.g., &lt;code>/about.html&lt;/code>.&lt;/li>
&lt;li>Use Jekyll&amp;rsquo;s &lt;a href="https://jekyllrb.com/docs/liquid/tags/#link" rel="noopener">&lt;code>{% link %}&lt;/code> tag&lt;/a>.&lt;/li>
&lt;/ol>
&lt;p>My preferred method is to use Jekyll&amp;rsquo;s &lt;code>{% link %}&lt;/code> tag as it has permalink &amp;ldquo;smarts&amp;rdquo; built into it and will warn you if trying to link to an invalid path.&lt;/p>
&lt;p>Let&amp;rsquo;s say you created a fresh Jekyll site using the &lt;code>jekyll new&lt;/code> command. After running that, you&amp;rsquo;d find an &lt;code>about.markdown&lt;/code> file in the root of the site:&lt;/p>
&lt;pre tabindex="0">&lt;code>├── _posts
├── _config.yml
├── about.markdown
├── index.html
&lt;/code>&lt;/pre>&lt;p>To link to that page you could create the link yourself if you know that the output path will be &lt;code>/articles.html&lt;/code>. But what if you&amp;rsquo;re new to Jekyll and not sure &lt;a href="#global-permalinks">how permalinks work&lt;/a>?&lt;/p>
&lt;h4 id="page-link-examples">&lt;a href="#page-link-examples">Page link examples&lt;/a>&lt;/h4>
&lt;p>Jekyll&amp;rsquo;s &lt;code>link&lt;/code> tag solves this problem. All you need to know is the path (i.e., the source file), and then it can be linked like this:&lt;/p>
&lt;pre tabindex="0">&lt;code class="language-liquid" data-lang="liquid">{% link about.markdown %}
&lt;/code>&lt;/pre>&lt;p>With files that are nested in sub-directories, you include the full path. For example, if we have a privacy policy page inside of a &lt;code>/terms&lt;/code> directory:&lt;/p>
&lt;pre tabindex="0">&lt;code>├── terms
│ └── privacy-policy.md
&lt;/code>&lt;/pre>&lt;p>It can be linked to like this (with or without the leading forward slash &lt;code>/&lt;/code>) in Markdown or HTML:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-markdown" data-lang="markdown">&lt;span class="line">&lt;span class="cl">[&lt;span class="nt">Privacy policy&lt;/span>](&lt;span class="na">{% link terms/privacy-policy.md %}&lt;/span>)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">a&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{% link terms/privacy-policy.md %}&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>Privacy policy&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">a&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Jekyll will output the same HTML for both:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">a&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;/terms/privacy-policy.html&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>Privacy policy&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">a&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Like the &lt;code>post_url&lt;/code> tag, if you link to a file that doesn&amp;rsquo;t exist or use the wrong path &amp;mdash; Jekyll will warn you:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">Liquid Exception: Could not find document &lt;span class="s1">&amp;#39;terms/privacy-policy.md&amp;#39;&lt;/span> in tag &lt;span class="s1">&amp;#39;link&amp;#39;&lt;/span>. Make sure the document exists and the path is correct. in index.markdown
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="jekyll-url-filters">&lt;a href="#jekyll-url-filters">Jekyll URL filters&lt;/a>&lt;/h3>
&lt;p>For theme developers and anyone looking to build or modify layouts, Jekyll has a set of Liquid filters that make working with URLs easier.
If you&amp;rsquo;re new to &lt;a href="https://shopify.github.io/liquid/" rel="noopener">&lt;strong>Liquid&lt;/strong>&lt;/a> (the template language used by Jekyll), &lt;a href="https://jekyllrb.com/docs/liquid/filters/#standard-liquid-filters" title="standard Liquid filters" rel="noopener">filters&lt;/a> are used to change the output of a string, object, or variable.&lt;/p>
&lt;p>You will find them inside of double curly braces &lt;code>{{ }}&lt;/code> and after the pipe character &lt;code>|&lt;/code>. For example this &lt;code>page.lang&lt;/code> variable that has a filter applied to it to assign a &lt;a href="https://shopify.github.io/liquid/filters/default/" rel="noopener">default value&lt;/a> of &lt;code>&amp;quot;en&amp;quot;&lt;/code>.&lt;/p>
&lt;pre tabindex="0">&lt;code class="language-liquid" data-lang="liquid">{{ page.lang | default: &amp;#34;en&amp;#34; }}
&lt;/code>&lt;/pre>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>-&lt;/th>
&lt;th>Filter&lt;/th>
&lt;th>Description&lt;/th>
&lt;th>Example output&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Relative URL&lt;/td>
&lt;td>&lt;code>relative_url&lt;/code>&lt;/td>
&lt;td>Prepends &lt;code>baseurl&lt;/code> configuration value to the input creating a relative URL. Recommended for sites placed in a sub-directory e.g., projects hosted on GitHub Pages.&lt;/td>
&lt;td>&lt;code>/my-project/assets/styles.css&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Absolute URL&lt;/td>
&lt;td>&lt;code>absolute_url&lt;/code>&lt;/td>
&lt;td>Prepends &lt;code>url&lt;/code> and &lt;code>baseurl&lt;/code> configurations values to the input creating an absolute URL.&lt;/td>
&lt;td>&lt;code>https://mmistakes.github.io/my-project/assets/styles.css&lt;/code>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The &lt;code>relative_url&lt;/code> and &lt;code>absolute_url&lt;/code> filters were created to ease the pain of prepending &lt;code>url&lt;/code> and &lt;code>baseurl&lt;/code> to links. Instead of needing to write &lt;code>{{ site.baseurl }}assets/css/styles.css&lt;/code>. You could write the following:&lt;/p>
&lt;pre tabindex="0">&lt;code class="language-liquid" data-lang="liquid">{{ &amp;#39;assets/css/styes.css&amp;#39; | relative_url }}
&lt;/code>&lt;/pre>&lt;p>Or if you needed to output the full URL of a page and were using something like: &lt;code>{{ site.url }}{{ site.baseurl }}{{ page.url }}&lt;/code> or &lt;code>{{ page.url | prepend: site.baseurl | prepend: site.url }}&lt;/code> in your layouts.&lt;/p>
&lt;p>Both could be shortened by using Jekyll&amp;rsquo;s &lt;code>absolute_url&lt;/code> filter:&lt;/p>
&lt;pre tabindex="0">&lt;code class="language-liquid" data-lang="liquid">{{ page.url | absolute_url }}
&lt;/code>&lt;/pre>&lt;p>Since filters are generally used in Jekyll layouts and includes, it&amp;rsquo;s unlikely content authors will have much use for them. In that case, the &lt;a href="/#-post_url--and--link--tags">&lt;code>{% post_url %}&lt;/code> and &lt;code>{% link %}&lt;/code> tags&lt;/a> will be more useful when creating internal links.&lt;/p>
&lt;aside class="notice">&lt;h3>URL filter examples&lt;/h3>&lt;p>Review these &lt;a href="/mastering-jekyll/site-url-baseurl/#root-relative-url-links" title="root-relative URL links">relative&lt;/a> and &lt;a href="/mastering-jekyll/site-url-baseurl/#absolute-url-links" title="absolute URL links">absolute&lt;/a> URL examples to learn more about when and when not to use each.&lt;/p>
&lt;/aside>
&lt;h2 id="jekyll-relative-links-plugin">&lt;a href="#jekyll-relative-links-plugin">Jekyll relative links plugin&lt;/a>&lt;/h2>
&lt;p>Another option for dealing with relative links may be to install the &lt;a href="https://github.com/benbalter/jekyll-relative-links" rel="noopener">&lt;strong>jekyll-relative-links&lt;/strong>&lt;/a> plugin.&lt;/p>
&lt;p>Perhaps you have repository of Markdown files on GitHub with links like &lt;code>[foo](bar.md)&lt;/code>. On GitHub.com these links are valid and work. But on the documentation site you built using these same Markdown source files &amp;mdash; the links are broken.&lt;/p>
&lt;p>Using the &lt;strong>jekyll-relative-links&lt;/strong> plugin can solve this by converting relative links to Markdown files into links to the appropriate Jekyll generated &lt;code>.html&lt;/code> files.&lt;/p>
&lt;p>For example, say we have two Markdown files in the root directory:&lt;/p>
&lt;pre tabindex="0">&lt;code>├── install.md
├── upgrade.md
&lt;/code>&lt;/pre>&lt;p>And in &lt;code>install.md&lt;/code> there is the following Markdown that links to &lt;code>upgrade.md&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-markdown" data-lang="markdown">&lt;span class="line">&lt;span class="cl">To [&lt;span class="nt">upgrade the package&lt;/span>](&lt;span class="na">upgrade.md&lt;/span>), run...
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>On GitHub.com the link will work as expected since &lt;code>upgrade.md&lt;/code> exists. But for a Jekyll built site, the browser expects the URL to be something like &lt;code>/upgrade.html&lt;/code> (or whatever permalink style is configured).&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>HTML output&lt;/th>
&lt;th style="text-align: center">-&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>&amp;lt;a href=&amp;quot;upgrade.md&amp;quot;&amp;gt;upgrade the package&amp;lt;/a&amp;gt;&lt;/code>&lt;/td>
&lt;td style="text-align: center">❌&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>With &lt;strong>jekyll-relative-links&lt;/strong> installed the same Markdown link of &lt;code>[upgrade the package](upgrade.md)&lt;/code> will output as a valid relative link to the rendered file &amp;mdash; custom permalinks and all!&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>HTML output&lt;/th>
&lt;th style="text-align: center">-&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>&amp;lt;a href=&amp;quot;/upgrade.html&amp;quot;&amp;gt;upgrade the package&amp;lt;/a&amp;gt;&lt;/code>&lt;/td>
&lt;td style="text-align: center">✅&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;aside class="notice">&lt;h3>⚠ Compatibility with posts&lt;/h3>&lt;p>By default &lt;strong>jekyll-relative-links&lt;/strong> is enabled for &lt;em>pages only&lt;/em>. To allow relative links in posts and other collections, set &lt;code>collections&lt;/code> to &lt;code>true&lt;/code> in the &lt;code>_config.yml&lt;/code> file:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">relative_links&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">collections&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;/aside>
&lt;div class="footnotes" role="doc-endnotes">
&lt;hr>
&lt;ol>
&lt;li id="fn:1">
&lt;p>&lt;a href="https://en.wikipedia.org/wiki/Permalink" rel="noopener">Wikipedia describes a permalink&lt;/a> or permanent link as a URL that is intended to remain unchanged for many years into the future, yielding a hyperlink that is less susceptible to link rot.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:2">
&lt;p>Unlike the &lt;code>_posts&lt;/code> directory, &lt;code>_pages&lt;/code> is not initially recognized by Jekyll. By design, directories (or folders) starting with an &lt;code>_&lt;/code> are ignored except for &lt;code>_includes&lt;/code>, &lt;code>_layouts&lt;/code>, &lt;code>_posts&lt;/code>, and &lt;code>_sass&lt;/code>. To force Jekyll to read from these &amp;ldquo;ignored&amp;rdquo; directories, add them to the &lt;a href="https://jekyllrb.com/docs/configuration/options/" rel="noopener">&lt;code>include&lt;/code> array&lt;/a> within &lt;code>_config.yml&lt;/code>.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;/div>&lt;p>&lt;a href="https://mademistakes.com/mastering-jekyll/how-to-link/?utm_source=atom_feed">URLs and links in Jekyll&lt;/a> was originally published on Made Mistakes.&lt;/p></content><category scheme="https://mademistakes.com/tag/static-sites" term="static-sites" label="static sites"/><category scheme="https://mademistakes.com/tag/tutorials" term="tutorials" label="tutorials"/><category scheme="https://mademistakes.com/tag/web-development" term="web-development" label="web development"/></entry><entry><title type="html">Jekyll’s site.url and baseurl</title><link href="https://mademistakes.com/mastering-jekyll/site-url-baseurl/?utm_source=atom_feed" rel="alternate" type="text/html"/><id>https://mademistakes.com/mastering-jekyll/site-url-baseurl/</id><published>2021-07-11T00:00:00-04:00</published><updated>2021-07-19T12:35:43-04:00</updated><summary type="html">Learn what Jekyll&amp;#39;s site url and baseurl variables are and how to properly set them with relative_url and absolute_url filters to avoid broken links and 404 errors.</summary><content type="html">
&lt;img alt="" decoding="async" fetchpriority="auto" height="169" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/jekyll-site-url-baseurl-illustration_hu2908973656200094340.jpg" style="background: #292824;" width="300"
srcset="https://mademistakes.com/images/jekyll-site-url-baseurl-illustration_hu2908973656200094340.jpg 300w,https://mademistakes.com/images/jekyll-site-url-baseurl-illustration_hu347085244714364859.jpg 800w, https://mademistakes.com/images/jekyll-site-url-baseurl-illustration.jpg 2660w"
>&lt;p>&lt;strong>Jekyll&amp;rsquo;s&lt;/strong> site &lt;code>url&lt;/code> and &lt;code>baseurl&lt;/code> variables cause a lot of confusion for users. I see it all the time in the &lt;a href="https://talk.jekyllrb.com/" rel="noopener">&lt;strong>Jekyll Talk&lt;/strong> forum&lt;/a>, &lt;a href="https://stackoverflow.com/questions/tagged/jekyll" rel="noopener">&lt;strong>Stackoverflow&lt;/strong>&lt;/a>, or as bug reports in &lt;a href="/work/jekyll-themes/">my themes&lt;/a>.&lt;/p>
&lt;blockquote>
&lt;p>My Jekyll site works locally but when I push it up to &lt;em>GitHub Pages&lt;/em> it is broken with no styling and looks like this. Help!&amp;quot;&lt;/p>
&lt;p>
&lt;img alt="Jekyll Minima site with missing CSS" class="img--md" decoding="async" fetchpriority="auto" height="161" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/jekyll-minima-stylesheet-404_hu3267044218156794432.jpg" style="background: #fefefe;" width="300"
srcset="https://mademistakes.com/images/jekyll-minima-stylesheet-404_hu3267044218156794432.jpg 300w, https://mademistakes.com/images/jekyll-minima-stylesheet-404_hu1391262826290306194.jpg 620w"
>&lt;/p>
&lt;/blockquote>
&lt;p>Improper use of Jekyll&amp;rsquo;s &lt;code>baseurl&lt;/code> can break links to CSS, posts, and more as seen above.&lt;/p>
&lt;h2 id="what-are-url-and-baseurl">&lt;a href="#what-are-url-and-baseurl">What are &lt;code>url&lt;/code> and &lt;code>baseurl&lt;/code>?&lt;/a>&lt;/h2>
&lt;p>So what exactly are the &lt;code>url&lt;/code> and &lt;code>baseurl&lt;/code> variables? To start, both are site-wide variables set in the &lt;code>_config.yml&lt;/code> file and affect how Jekyll builds URLs. I like to describe them like this:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Site variable&lt;/th>
&lt;th>Description&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>url&lt;/code>&lt;/td>
&lt;td>A site&amp;rsquo;s full URL including protocol, domain, and port (if applicable).&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>baseurl&lt;/code>&lt;/td>
&lt;td>Name of sub-directory the site is served from e.g., &lt;code>/blog&lt;/code>.&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>
&lt;img alt="illustration describing site url and baseurl permalink structure" class="img--md" decoding="async" fetchpriority="auto" height="134" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/site-url-baseurl-permalink-example_hu3217062979227421737.jpg" style="background: #28252c;" width="300"
srcset="https://mademistakes.com/images/site-url-baseurl-permalink-example_hu3217062979227421737.jpg 300w, https://mademistakes.com/images/site-url-baseurl-permalink-example_hu10431701330470072207.jpg 620w"
>&lt;/p>
&lt;h2 id="how-to-use-url-and-baseurl">&lt;a href="#how-to-use-url-and-baseurl">How to use &lt;code>url&lt;/code> and &lt;code>baseurl&lt;/code>&lt;/a>&lt;/h2>
&lt;p>A couple of important facts to keep in mind when using both:&lt;/p>
&lt;ol>
&lt;li>
&lt;p>Leave off trailing forward slashes when setting &lt;code>url&lt;/code>&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>✅ Do this&lt;/th>
&lt;th>❌ Don&amp;rsquo;t do this&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>website: https://mademistakes.com&lt;/code>&lt;/td>
&lt;td>&lt;code>website: https://mademistakes.com/&lt;/code>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;/li>
&lt;li>
&lt;p>&lt;code>baseurl&lt;/code> is &lt;em>not needed&lt;/em> for most sites and can be omitted.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;code>baseurl&lt;/code> is only necessary when hosting your site in a sub-directory. &lt;a href="https://docs.github.com/en/pages/getting-started-with-github-pages/about-github-pages#types-of-github-pages-sites" rel="noopener">Project sites&lt;/a> hosted on &lt;a href="https://pages.github.com/" rel="noopener">&lt;strong>GitHub Pages&lt;/strong>&lt;/a> are the common use-case of this variable.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;aside class="notice">&lt;h3>Remember to properly set links&lt;/h3>&lt;p>These URL variables are not magic and need to be applied to links in your layouts, includes, or themes. This can be done by prefixing all links with &lt;code>{{ site.url }}{{ site.baseurl }}&lt;/code> or by apply Jekyll&amp;rsquo;s &lt;a href="#absolute_url-filter">&lt;code>absolute_url&lt;/code>&lt;/a> or &lt;a href="#relative_url-filter">&lt;code>relative_url&lt;/code>&lt;/a> filters to them.&lt;/p>
&lt;/aside>
&lt;h2 id="jekyll-url-troubleshooting">&lt;a href="#jekyll-url-troubleshooting">Jekyll URL troubleshooting&lt;/a>&lt;/h2>
&lt;p>If we go back to the example above with the broken CSS link and inspect the HTML&amp;rsquo;s source. You will often find that the link to the CSS file is incorrect for any number of reasons:&lt;/p>
&lt;ol>
&lt;li>Double forward slashes e.g., &lt;code>//minima/assets/style.css&lt;/code>&lt;/li>
&lt;li>Missing base URL due to not using &lt;code>relative_url&lt;/code> filter, &lt;code>absolute_url&lt;/code> filter, or &lt;code>{{ site.baseurl }}&lt;/code> in theme files.&lt;/li>
&lt;/ol>
&lt;figure class="figure">
&lt;p>
&lt;img alt="Firefox web inspector showing HTML source and broken stylesheet reference" class="img--md" decoding="async" fetchpriority="auto" height="127" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/firefox-inspector-broken-stylesheet-ref_hu24919888205336808.jpg" style="background: #fafbfb;" width="300"
srcset="https://mademistakes.com/images/firefox-inspector-broken-stylesheet-ref_hu24919888205336808.jpg 300w, https://mademistakes.com/images/firefox-inspector-broken-stylesheet-ref_hu18170825188624440017.jpg 620w"
>&lt;/p>
&lt;figcaption>When the path to the stylesheet is wrong the browser can&amp;rsquo;t load it. No stylesheet == plain looking website with no styles and looks &lt;em>broken&lt;/em>.&lt;/figcaption>
&lt;/figure>
&lt;h2 id="jekyll-development-and-siteurl">&lt;a href="#jekyll-development-and-siteurl">Jekyll development and &lt;code>site.url&lt;/code>&lt;/a>&lt;/h2>
&lt;p>One other question I see asked over and over again from Jekyll users is:&lt;/p>
&lt;blockquote>
&lt;p>Why are my links broken? They all start with &lt;code>http://localhost:4000&lt;/code> or &lt;code>http://127.0.0.1:4000&lt;/code>.&lt;/p>
&lt;p>
&lt;img alt="Screenshot of GitHub Pages default 404 file not found page" class="img--md" decoding="async" fetchpriority="auto" height="161" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/github-pages-404-file-not-found_hu532975575296536021.jpg" style="background: #f0f0f0;" width="300"
srcset="https://mademistakes.com/images/github-pages-404-file-not-found_hu532975575296536021.jpg 300w, https://mademistakes.com/images/github-pages-404-file-not-found_hu2975548813714813601.jpg 620w"
>&lt;/p>
&lt;/blockquote>
&lt;p>This happens because in older versions of Jekyll (v3.3 through 4.1) it would reset &lt;code>site.url&lt;/code> to &lt;code>localhost:4000&lt;/code> when &lt;a href="https://jekyllrb.com/docs/configuration/environments/" rel="noopener">&lt;strong>&lt;code>JEKYLL_ENV=development&lt;/code>&lt;/strong>&lt;/a> (the default environment value), overriding whatever is set in the &lt;code>_config.yml&lt;/code>. In newer versions of Jekyll (&lt;a href="https://jekyllrb.com/docs/history/#v4-2-0" rel="noopener">v4.2 and up&lt;/a>) this &lt;a href="https://github.com/jekyll/jekyll/pull/7253" rel="noopener">reset of &lt;code>site.url&lt;/code> no longer happens&lt;/a>.&lt;/p>
&lt;p>In older versions of Jekyll, &lt;code>site.url&lt;/code> is also reset to &lt;code>http://localhost:4000&lt;/code> when spinning up a development server with the &lt;code>jekyll serve&lt;/code> command.&lt;/p>
&lt;h3 id="siteurl-values">&lt;a href="#siteurl-values">&lt;code>site.url&lt;/code> values&lt;/a>&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Jekyll version&lt;/th>
&lt;th>Environment&lt;/th>
&lt;th>CLI command&lt;/th>
&lt;th>&lt;code>site.url&lt;/code> value&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>4.2&lt;/strong> (✨latest)&lt;/td>
&lt;td>&lt;code>development&lt;/code>&lt;/td>
&lt;td>&lt;code>serve&lt;/code>&lt;/td>
&lt;td>value set in &lt;code>_config.yml&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>3.3&lt;/td>
&lt;td>&lt;code>development&lt;/code>&lt;/td>
&lt;td>&lt;code>serve&lt;/code>&lt;/td>
&lt;td>reset to &lt;code>http://localhost:4000&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>4.2&lt;/strong> (✨latest)&lt;/td>
&lt;td>&lt;code>development&lt;/code>&lt;/td>
&lt;td>&lt;code>build&lt;/code>&lt;/td>
&lt;td>value set in &lt;code>_config.yml&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>3.3&lt;/td>
&lt;td>&lt;code>development&lt;/code>&lt;/td>
&lt;td>&lt;code>build&lt;/code>&lt;/td>
&lt;td>value set in &lt;code>_config.yml&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>4.2&lt;/strong> (✨latest)&lt;/td>
&lt;td>&lt;code>production&lt;/code>&lt;/td>
&lt;td>&lt;code>serve&lt;/code>&lt;/td>
&lt;td>value set in &lt;code>_config.yml&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>3.3&lt;/td>
&lt;td>&lt;code>production&lt;/code>&lt;/td>
&lt;td>&lt;code>serve&lt;/code>&lt;/td>
&lt;td>value set in &lt;code>_config.yml&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>4.2&lt;/strong> (✨latest)&lt;/td>
&lt;td>&lt;code>production&lt;/code>&lt;/td>
&lt;td>&lt;code>build&lt;/code>&lt;/td>
&lt;td>value set in &lt;code>_config.yml&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>3.3&lt;/td>
&lt;td>&lt;code>production&lt;/code>&lt;/td>
&lt;td>&lt;code>build&lt;/code>&lt;/td>
&lt;td>value set in &lt;code>_config.yml&lt;/code>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Under normal circumstances these two conditions won&amp;rsquo;t cause any headaches, especially when hosting on &lt;a href="https://pages.github.com/" rel="noopener">&lt;strong>GitHub Pages&lt;/strong>&lt;/a> as the environment and &lt;code>url&lt;/code> are set automatically.&lt;/p>
&lt;p>The issue surfaces when building locally, using an older versions of Jekyll, and then forgetting to set &lt;code>JEKYLL_ENV&lt;/code> to &lt;code>production&lt;/code>. Or pushing up files from a &lt;code>_site&lt;/code> directory after running &lt;code>jekyll serve&lt;/code> instead of &lt;code>jekyll build&lt;/code>.&lt;/p>
&lt;aside class="notice">&lt;h3>⚠ GitHub Pages &lt;a href="https://pages.github.com/versions/" rel="noopener">dependencies and versions&lt;/a>&lt;/h3>&lt;p>Further confusing the issue, &lt;strong>GitHub Pages&lt;/strong> does not use the latest version of Jekyll to build sites. It is currently locked at 3.9.0, which means it doesn&amp;rsquo;t have parity with a lot of the new hotness found in version 4.&lt;/p>
&lt;/aside>
&lt;h2 id="examples">&lt;a href="#examples">Examples&lt;/a>&lt;/h2>
&lt;p>Below you will find examples of how to write different types of links that require &lt;code>site.url&lt;/code> or &lt;code>site.baseurl&lt;/code> values to work properly. Links marked as ✅ have valid URLs while those marked as ❌ are broken and will trigger a 404 file not found error.&lt;/p>
&lt;p>Each of the examples assume:&lt;/p>
&lt;ol>
&lt;li>
&lt;p>Jekyll v4.2 or greater is installed.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>The site is hosted on GitHub Pages in a sub-directory named &lt;code>blog&lt;/code>.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Posts follow &lt;a href="https://jekyllrb.com/docs/posts/" rel="noopener">standard naming conventions&lt;/a> of &lt;code>YYYY-MM-DD-filename.md&lt;/code> inside of a &lt;code>_posts&lt;/code> directory.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Image assets are in a &lt;code>images&lt;/code> directory inside of the root.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Have the following &lt;code>_config.yml&lt;/code> settings:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">website&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">https://mmistakes.github.io&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">basewebsite&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">/blog&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">permalink&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">date&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ol>
&lt;h3 id="markdown-links">&lt;a href="#markdown-links">Markdown links&lt;/a>&lt;/h3>
&lt;p>How to link to pages, posts, and images in Markdown.&lt;/p>
&lt;h4 id="page-in-root-directory">&lt;a href="#page-in-root-directory">Page in root directory&lt;/a>&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-markdown" data-lang="markdown">&lt;span class="line">&lt;span class="cl">[&lt;span class="nt">about page&lt;/span>](&lt;span class="na">/about.html&lt;/span>)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>HTML output&lt;/th>
&lt;th style="text-align: center">-&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>&amp;lt;a href=&amp;quot;/about.html&amp;quot;&amp;gt;about page&amp;lt;/a&amp;gt;&lt;/code>&lt;br />URL is missing the base URL of &lt;code>/blog&lt;/code> from the path.&lt;/td>
&lt;td style="text-align: center">❌&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h4 id="page-in-sub-directory">&lt;a href="#page-in-sub-directory">Page in sub-directory&lt;/a>&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-markdown" data-lang="markdown">&lt;span class="line">&lt;span class="cl">[&lt;span class="nt">about page&lt;/span>](&lt;span class="na">/blog/about.html&lt;/span>)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">[&lt;span class="nt">about page&lt;/span>](&lt;span class="na">{{ site.baseurl }}/about.html&lt;/span>)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">[&lt;span class="nt">about page&lt;/span>](&lt;span class="na">{{ &amp;#39;about.html&amp;#39; | relative_url }}&lt;/span>)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>HTML output&lt;/th>
&lt;th style="text-align: center">-&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>&amp;lt;a href=&amp;quot;/blog/about.html&amp;quot;&amp;gt;about page&amp;lt;/a&amp;gt;&lt;/code>&lt;/td>
&lt;td style="text-align: center">✅&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h4 id="page-in-date-sub-directories">&lt;a href="#page-in-date-sub-directories">Page in date sub-directories&lt;/a>&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-markdown" data-lang="markdown">&lt;span class="line">&lt;span class="cl">[&lt;span class="nt">Welcome to Jekyll post&lt;/span>](&lt;span class="na">/blog/2021/06/29/welcome-to-jekyll.html&lt;/span>)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">[&lt;span class="nt">Welcome to Jekyll post&lt;/span>](&lt;span class="na">{% post_url 2021-06-29-welcome-to-jekyll %}&lt;/span>)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">[&lt;span class="nt">Welcome to Jekyll post&lt;/span>](&lt;span class="na">{% link _posts/2021-06-29-welcome-to-jekyll.md %}&lt;/span>)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>HTML output&lt;/th>
&lt;th style="text-align: center">-&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>&amp;lt;a href=&amp;quot;/blog/2021/06/29/welcome-to-jekyll.html&amp;quot;&amp;gt;Welcome to Jekyll post&amp;lt;/a&amp;gt;&lt;/code>&lt;/td>
&lt;td style="text-align: center">✅&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h4 id="image-in-root-directory">&lt;a href="#image-in-root-directory">Image in root directory&lt;/a>&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-markdown" data-lang="markdown">&lt;span class="line">&lt;span class="cl">[&lt;span class="nt">cheese pizza&lt;/span>](&lt;span class="na">pizza.jpg&lt;/span>)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>HTML output&lt;/th>
&lt;th style="text-align: center">-&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>&amp;lt;img src=&amp;quot;pizza.jpg&amp;quot; alt=&amp;quot;cheese pizza&amp;quot;&amp;gt;&lt;/code>&lt;br />URL is missing the base URL of &lt;code>/blog&lt;/code> and sub-directory &lt;code>/images&lt;/code> from the path.&lt;/td>
&lt;td style="text-align: center">❌&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h4 id="image-in-sub-directory">&lt;a href="#image-in-sub-directory">Image in sub-directory&lt;/a>&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-markdown" data-lang="markdown">&lt;span class="line">&lt;span class="cl">[&lt;span class="nt">cheese pizza&lt;/span>](&lt;span class="na">images/pizza.jpg&lt;/span>)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>HTML output&lt;/th>
&lt;th style="text-align: center">-&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>&amp;lt;img src=&amp;quot;images/pizza.jpg&amp;quot; alt=&amp;quot;cheese pizza&amp;quot;&amp;gt;&lt;/code>&lt;br />URL is document-relative when it should be root-relative and is missing the base URL of &lt;code>/blog&lt;/code> from the path.&lt;/td>
&lt;td style="text-align: center">❌&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-markdown" data-lang="markdown">&lt;span class="line">&lt;span class="cl">[&lt;span class="nt">cheese pizza&lt;/span>](&lt;span class="na">/images/pizza.jpg&lt;/span>)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>HTML output&lt;/th>
&lt;th style="text-align: center">-&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>&amp;lt;img src=&amp;quot;/images/pizza.jpg&amp;quot; alt=&amp;quot;cheese pizza&amp;quot;&amp;gt;&lt;/code>&lt;br />URL is missing the base URL of &lt;code>/blog&lt;/code> from the path.&lt;/td>
&lt;td style="text-align: center">❌&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-markdown" data-lang="markdown">&lt;span class="line">&lt;span class="cl">[&lt;span class="nt">cheese pizza&lt;/span>](&lt;span class="na">/blog/images/pizza.jpg&lt;/span>)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">[&lt;span class="nt">cheese pizza&lt;/span>](&lt;span class="na">{{ &amp;#39;/images/pizza.jpg&amp;#39; | relative_url }}&lt;/span>)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">[&lt;span class="nt">cheese pizza&lt;/span>](&lt;span class="na">{{ site.baseurl }}/images/pizza.jpg&lt;/span>)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>HTML output&lt;/th>
&lt;th style="text-align: center">-&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>&amp;lt;img src=&amp;quot;/blog/images/pizza.jpg&amp;quot; alt=&amp;quot;cheese pizza&amp;quot;&amp;gt;&lt;/code>&lt;/td>
&lt;td style="text-align: center">✅&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="root-relative-url-links">&lt;a href="#root-relative-url-links">Root-relative URL links&lt;/a>&lt;/h3>
&lt;p>How to link resources with root-relative URLs, like stylesheets, favicons, and JavaScript.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">link&lt;/span> &lt;span class="na">rel&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;stylesheet&amp;#34;&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;/assets/css/style.css&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>HTML output&lt;/th>
&lt;th style="text-align: center">-&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>&amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;/assets/css/style.css&amp;quot;&amp;gt;&lt;/code>&lt;br />URL is missing the base URL of &lt;code>/blog&lt;/code> from the path.&lt;/td>
&lt;td style="text-align: center">❌&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">link&lt;/span> &lt;span class="na">rel&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;stylesheet&amp;#34;&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;/blog/assets/css/style.css&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">link&lt;/span> &lt;span class="na">rel&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;stylesheet&amp;#34;&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ site.baseurl }}/assets/css/style.css&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">link&lt;/span> &lt;span class="na">rel&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;stylesheet&amp;#34;&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ &amp;#39;/assets/css/style.css&amp;#39; | relative_url }}&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>HTML output&lt;/th>
&lt;th style="text-align: center">-&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>&amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;/blog/assets/css/style.css&amp;quot;&amp;gt;&lt;/code>&lt;/td>
&lt;td style="text-align: center">✅&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="absolute-url-links">&lt;a href="#absolute-url-links">Absolute URL links&lt;/a>&lt;/h3>
&lt;p>How to link to resources that require the full URL, like a canonical URL in the page&amp;rsquo;s &lt;code>&amp;lt;head&amp;gt;&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">link&lt;/span> &lt;span class="na">rel&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;canonical&amp;#34;&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ page.url }}&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>HTML output&lt;/th>
&lt;th style="text-align: center">-&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>&amp;lt;link rel=&amp;quot;canonical&amp;quot; href=&amp;quot;/2021/06/29/welcome-to-jekyll.html&amp;quot;&amp;gt;&lt;/code>&lt;br />URL is missing the site URL and base URL of &lt;code>/blog&lt;/code> from the path.&lt;/td>
&lt;td style="text-align: center">❌&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">link&lt;/span> &lt;span class="na">rel&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;canonical&amp;#34;&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ page.url | absolute_url }}&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">link&lt;/span> &lt;span class="na">rel&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;canonical&amp;#34;&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ page.url | prepend: site.baseurl | prepend: site.url }}&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">link&lt;/span> &lt;span class="na">rel&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;canonical&amp;#34;&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ site.url }}{{ site.baseurl }}{{ page.url }}&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>HTML output&lt;/th>
&lt;th style="text-align: center">-&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>&amp;lt;link rel=&amp;quot;canonical&amp;quot; href=&amp;quot;https://mmistakes.github.io/blog/2021/06/29/welcome-to-jekyll.html&amp;quot;&amp;gt;&lt;/code>&lt;/td>
&lt;td style="text-align: center">✅&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>As you can see from above examples, Jekyll is flexible and allows for multiple ways of creating links. Personally I tend to lean on the &lt;code>relative_url&lt;/code> and &lt;code>absolute_url&lt;/code> filters as they require less typing and have some &amp;ldquo;smarts&amp;rdquo; to them.&lt;/p>
&lt;p>The best option is the one that works for you!&lt;/p>&lt;p>&lt;a href="https://mademistakes.com/mastering-jekyll/site-url-baseurl/?utm_source=atom_feed">Jekyll’s site.url and baseurl&lt;/a> was originally published on Made Mistakes.&lt;/p></content><category scheme="https://mademistakes.com/tag/static-sites" term="static-sites" label="static sites"/><category scheme="https://mademistakes.com/tag/tutorials" term="tutorials" label="tutorials"/><category scheme="https://mademistakes.com/tag/web-development" term="web-development" label="web development"/></entry><entry><title type="html">Not so quick with the Gatsby site optimizations</title><link href="https://mademistakes.com/notes/gatsby-site-optimizations-2021/?utm_source=atom_feed" rel="alternate" type="text/html"/><id>https://mademistakes.com/notes/gatsby-site-optimizations-2021/</id><published>2021-06-22T16:14:38-04:00</published><updated>2023-12-08T09:17:53-05:00</updated><summary type="html">This is the post where I talk about neglecting the site and pledge to blog more... wait, wasn&amp;#39;t I suppose to do this on January 1st?</summary><content type="html">&lt;p>This is the post where I talk about neglecting the site and pledge to blog more&amp;hellip; &lt;em>wait, wasn&amp;rsquo;t I suppose to do this on January 1st?&lt;/em>&lt;/p>
&lt;p>Jokes aside, the last year and half I haven&amp;rsquo;t felt like writing. Between my &amp;ldquo;real&amp;rdquo; job, open source projects, COVID-19, buying and selling a house during a global pandemic, and moving the twins to a new school &amp;mdash; where does someone find the time?&lt;/p>
&lt;p>With life stabilized, I had time to dust the cobwebs off this site.&lt;/p>
&lt;h2 id="build-process">&lt;a href="#build-process">Build process&lt;/a>&lt;/h2>
&lt;p>In the time that I &lt;a href="/notes/twenty-nineteen/#web-development">ported over from Jekyll to Gatsby&lt;/a>, GitHub released Actions to build, test, and deploy code. Migrating my build process over to &lt;a href="https://github.com/features/actions" rel="noopener">GitHub Actions&lt;/a> seemed obvious as I was hitting the limits of Travis CI&amp;rsquo;s free tier.&lt;/p>
&lt;h3 id="github-actions-migration">&lt;a href="#github-actions-migration">GitHub Actions migration&lt;/a>&lt;/h3>
&lt;p>The process of updating involved creating a new YAML config file and adding environment variables. The cherry on top was leveraging an &lt;a href="https://github.com/actions/cache" rel="noopener">action to cache&lt;/a> Gatsby&amp;rsquo;s &lt;code>.cache&lt;/code> and &lt;code>public&lt;/code> folders between builds.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">jobs&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">build&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">runs-on&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">ubuntu-latest&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">steps&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Caching Gatsby&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">id&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">gatsby-cache-build&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">actions/cache@v2&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">with&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">path&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">|&lt;/span>&lt;span class="sd">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> public
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> .cache&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">key&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">${{ runner.os }}-gatsby-build-${{ github.run_id }}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">restore-keys&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">|&lt;/span>&lt;span class="sd">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> ${{ runner.os }}-gatsby-build-&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Building the site from a cold start and no cache takes about 35 minutes to chug through 1,426 pages and resize/optimize 13,194 images. Each cached builds after takes ~1 minute to finish &amp;mdash; that&amp;rsquo;s what I call an improvement!&lt;/p>
&lt;div class="browser-frame">&lt;img alt="Deploy to Netlify GitHub Action log." class="img--md" decoding="async" fetchpriority="auto" height="165" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/deploy-to-netlify-github-action_hu17888290899638654134.jpg" style="background: #242a2f;" width="300"
&lt;pre>&lt;code> srcset=&amp;quot;https://mademistakes.com/images/deploy-to-netlify-github-action_hu17888290899638654134.jpg 300w, https://mademistakes.com/images/deploy-to-netlify-github-action_hu8357628151382755290.jpg 620w&amp;quot;
&amp;gt;
&lt;/code>&lt;/pre>
&lt;/div>
&lt;h3 id="upgrade-gatsby-to-3x">&lt;a href="#upgrade-gatsby-to-3x">Upgrade Gatsby to 3.x&lt;/a>&lt;/h3>
&lt;p>&lt;a href="https://www.gatsbyjs.com/blog/gatsby-v3/" rel="noopener">Gatsby v3 added all sorts of improvements&lt;/a> to the local development experience, which naturally I wanted to take part in. Unfortunately I had to wait for a few community plugins to be updated as the old Gatsby v2 versions broke my builds.&lt;/p>
&lt;h4 id="plugin-gatsby-remark-source-namehttpsgithubcomelbomangatsby-remark-source-name">&lt;a href="#plugin-gatsby-remark-source-namehttpsgithubcomelbomangatsby-remark-source-name">Plugin: &lt;a href="https://github.com/elboman/gatsby-remark-source-name" rel="noopener">gatsby-remark-source-name&lt;/a>&lt;/a>&lt;/h4>
&lt;p>Coming from Jekyll I&amp;rsquo;m used to organizing Markdown files into different collections like &lt;code>posts&lt;/code>, &lt;code>pages&lt;/code>, and &lt;code>comments&lt;/code>. With Gatsby I place each in their own folder with a matching &lt;code>name&lt;/code> in &lt;code>gatsby-config.js&lt;/code> as shown below:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// excerpt from gatsby-config.js
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">plugins&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">resolve&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;gatsby-source-filesystem&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">options&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line hl">&lt;span class="cl"> &lt;span class="nx">name&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;posts&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">path&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="sb">`&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">__dirname&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">/src/posts`&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">resolve&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;gatsby-source-filesystem&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">options&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line hl">&lt;span class="cl"> &lt;span class="nx">name&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;pages&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">path&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="sb">`&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">__dirname&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">/src/pages`&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">resolve&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;gatsby-source-filesystem&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">options&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">name&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;comments&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">path&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="sb">`&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">__dirname&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">/src/comments`&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The &lt;strong>gatsby-remark-source-name&lt;/strong> plugin then adds these as a &lt;code>sourceName&lt;/code> field to Remark nodes. Allowing for an easy way to filter on collection type in my GraphQL queries:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-graphql" data-lang="graphql">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># query for all &amp;#34;posts&amp;#34; Markdown files&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="py">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="py">graphql&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="err">`&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="py">posts&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nc">allMarkdownRemark&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line hl">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="py">filter&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line hl">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nc">fields&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nc">sourceName&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nc">eq&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;posts&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line hl">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nc">edges&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="py">node&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="py">html&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="py">frontmatter&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># Assumes you&amp;#39;re using title in your frontmatter&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="py">title&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="err">`&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This broke with Gatsby v3 and threw the following error:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">TypeError: Cannot destructure property &lt;span class="s1">&amp;#39;createNodeField&amp;#39;&lt;/span> of &lt;span class="s1">&amp;#39;boundActionCreators&amp;#39;&lt;/span> as it is undefined.
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Turns out a &lt;a href="https://github.com/elboman/gatsby-remark-source-name/pull/1#issuecomment-835423884" title="add sourceName nodes to Markdown files" rel="noopener">plugin isn&amp;rsquo;t needed&lt;/a> and you can achieve the same result by adding the following to &lt;code>gatsby-node.js&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">exports&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">onCreateNode&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">({&lt;/span> &lt;span class="nx">node&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">getNode&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">actions&lt;/span> &lt;span class="p">})&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">createNodeField&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">actions&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">node&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">internal&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">type&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="s1">&amp;#39;MarkdownRemark&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">fileNode&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">getNode&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">node&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">parent&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line hl">&lt;span class="cl"> &lt;span class="nx">createNodeField&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">node&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">name&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;sourceName&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">value&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">fileNode&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">sourceInstanceName&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="plugin-gatsby-source-github-apihttpsgithubcomlddgatsby-source-github-api">&lt;a href="#plugin-gatsby-source-github-apihttpsgithubcomlddgatsby-source-github-api">Plugin: &lt;a href="https://github.com/ldd/gatsby-source-github-api" rel="noopener">gatsby-source-github-api&lt;/a>&lt;/a>&lt;/h4>
&lt;p>Not sure why &lt;strong>gatsby-source-github-api&lt;/strong> had problems with Gatsby 3.x, but this was the error message I encountered:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">ERROR &lt;span class="c1">#11321 PLUGIN&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;gatsby-source-github-api&amp;#34;&lt;/span> threw an error &lt;span class="k">while&lt;/span> running the sourceNodes lifecycle:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Cannot destructure property &lt;span class="s1">&amp;#39;createNode&amp;#39;&lt;/span> of &lt;span class="s1">&amp;#39;boundActionCreators&amp;#39;&lt;/span> as it is undefined.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="m">8&lt;/span> &lt;span class="p">|&lt;/span> &lt;span class="o">)&lt;/span> &lt;span class="o">=&lt;/span>&amp;gt; &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="m">9&lt;/span> &lt;span class="p">|&lt;/span> console.log&lt;span class="o">(&lt;/span>boundActionCreators&lt;span class="o">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;gt; &lt;span class="m">10&lt;/span> &lt;span class="p">|&lt;/span> const &lt;span class="o">{&lt;/span> createNode &lt;span class="o">}&lt;/span> &lt;span class="o">=&lt;/span> boundActionCreators&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">|&lt;/span> ^
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="m">11&lt;/span> &lt;span class="p">|&lt;/span> &lt;span class="k">return&lt;/span> new Promise&lt;span class="o">((&lt;/span>resolve, reject&lt;span class="o">)&lt;/span> &lt;span class="o">=&lt;/span>&amp;gt; &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="m">12&lt;/span> &lt;span class="p">|&lt;/span> // we need a token to use this plugin
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="m">13&lt;/span> &lt;span class="p">|&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="o">(&lt;/span>&lt;span class="nv">token&lt;/span> &lt;span class="o">===&lt;/span> undefined&lt;span class="o">)&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">File: node_modules&lt;span class="se">\g&lt;/span>atsby-source-github-api&lt;span class="se">\g&lt;/span>atsby-node.js:10:11
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>A quick &lt;a href="https://github.com/ldd/gatsby-source-github-api/pull/29" title="Pull request to add support for Gatsby v3" rel="noopener">update&lt;/a> of the npm package and all was well with the world.&lt;/p>
&lt;h4 id="migrating-gatsby-from-v2-to-v3">&lt;a href="#migrating-gatsby-from-v2-to-v3">Migrating Gatsby from v2 to v3&lt;/a>&lt;/h4>
&lt;p>For everything that wasn&amp;rsquo;t related to outdated community plugins, I followed the &lt;a href="https://www.gatsbyjs.com/docs/reference/release-notes/migrating-from-v2-to-v3/" title="Migrating from Gatsby v2 to v3" rel="noopener">official migration guide&lt;/a>. A couple of find/replace commands across my project and I was done.&lt;/p>
&lt;p>The other big change came from &lt;a href="https://www.gatsbyjs.com/docs/reference/release-notes/image-migration-guide" rel="noopener">migrating &lt;strong>gatsby-image&lt;/strong> to &lt;strong>gatsby-plugin-image&lt;/strong>&lt;/a>. This involved updating dependencies, fixing GraphQL queries, and replacing components.&lt;/p>
&lt;p>Running &lt;code>npx gatsby-codemods gatsby-plugin-image&lt;/code> did a lot of this work for me. Gatsby&amp;rsquo;s CLI tool is pretty good at letting you know if you&amp;rsquo;re using the deprecated &lt;strong>gatsby-image&lt;/strong> component. It even let me know about instances where I should add &lt;code>alt&lt;/code> attributes, which is good for accessibility.&lt;/p>
&lt;h2 id="passing-core-web-vitals">&lt;a href="#passing-core-web-vitals">Passing Core Web Vitals&lt;/a>&lt;/h2>
&lt;div class="browser-frame">&lt;img alt="Perfect 100 Lighthouse scores for mademistakes.com" class="img--md" decoding="async" fetchpriority="auto" height="246" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/mm-lighthouse-scores-062021_hu13498284394947058712.jpg" style="background: #212121;" width="300"
&lt;pre>&lt;code> srcset=&amp;quot;https://mademistakes.com/images/mm-lighthouse-scores-062021_hu13498284394947058712.jpg 300w, https://mademistakes.com/images/mm-lighthouse-scores-062021_hu1908794635994168237.jpg 620w&amp;quot;
&amp;gt;
&lt;/code>&lt;/pre>
&lt;/div>
&lt;p>Only took me a year of tinkering to get 100&amp;rsquo;s in &lt;strong>Google Lighthouse&lt;/strong> for performance, accessibility, best practices, and SEO. Shipping less JavaScript by installing &lt;a href="https://github.com/gatsbyjs/gatsby/tree/master/packages/gatsby-plugin-preact" rel="noopener">&lt;strong>gatsby-plugin-preact&lt;/strong>&lt;/a> certainly helped improve performance scores too.&lt;/p>
&lt;p>But on some of my heavier pages with tons of DOM elements, 3rd party embeds from Twitter, and large animated GIFs &amp;mdash; perf scores still take a hit.&lt;/p>
&lt;p>Gatsby supports both &lt;a href="https://www.gatsbyjs.com/docs/reference/built-in-components/gatsby-plugin-image#formats" rel="noopener">WebP and AVIF image formats&lt;/a> now, so I&amp;rsquo;ll need to experiment with those to see if I can shave even more time off. Just as long as my build times don&amp;rsquo;t increase too much&amp;hellip;&lt;/p>
&lt;h3 id="accessibility-improvements">&lt;a href="#accessibility-improvements">Accessibility improvements&lt;/a>&lt;/h3>
&lt;ul>
&lt;li>Adjusted the &lt;code>:focus&lt;/code> and &lt;code>:hover&lt;/code> styling of grid and list entries to render an outline around each when selected.&lt;/li>
&lt;li>Bumped up the padding around footer links so they&amp;rsquo;re easier to tap.&lt;/li>
&lt;/ul>
&lt;h2 id="dark-mode-updates">&lt;a href="#dark-mode-updates">Dark mode updates&lt;/a>&lt;/h2>
&lt;p>Flipping between light and dark themes on the site worked before, but it had room to improve. For starters it wasn&amp;rsquo;t checking if a user had color theme preference (light or dark) set in their operating system.&lt;/p>
&lt;p>To remedy this I installed &lt;a href="https://github.com/wKovacs64/gatsby-plugin-use-dark-mode" rel="noopener">&lt;strong>gatsby-plugin-use-dark-mode&lt;/strong>&lt;/a> to add a custom React Hook for use in my &amp;ldquo;dark mode toggle&amp;rdquo; component.&lt;/p>
&lt;p>As an added benefit it also &lt;a href="https://github.com/donavon/use-dark-mode#that-flash" rel="noopener">injects a small bit of JavaScript&lt;/a> that helps with the &lt;em>dreaded flash of default theme&lt;/em> plaguing sites with dark modes.&lt;/p>
&lt;h2 id="table-of-contents-updates">&lt;a href="#table-of-contents-updates">Table of contents updates&lt;/a>&lt;/h2>
&lt;p>I&amp;rsquo;m still using a feature of Gatsby&amp;rsquo;s Markdown transformer to &lt;a href="https://www.gatsbyjs.com/plugins/gatsby-transformer-remark/#configuring-the-tableofcontents" rel="noopener">automatically create table of contents&lt;/a> from headings. But instead of dropping it into a collapsible &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details" rel="noopener">&lt;code>&amp;lt;details&amp;gt;&lt;/code> element&lt;/a> I styled the unordered list as a true sidebar.&lt;/p>
&lt;p>Could just be me, but putting it off to the side has a nicer flow than before.&lt;/p>
&lt;p>
&lt;img alt="Post template with optional table of contents pushed to the left of main content" class="img--md" decoding="async" fetchpriority="auto" height="219" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/mm-2021-gatsby-post-template-toc_hu5337866498822768069.jpg" style="background: #f9faf9;" width="300"
srcset="https://mademistakes.com/images/mm-2021-gatsby-post-template-toc_hu5337866498822768069.jpg 300w, https://mademistakes.com/images/mm-2021-gatsby-post-template-toc_hu16865904177148773300.jpg 620w"
>&lt;/p>
&lt;h2 id="visual-refinements">&lt;a href="#visual-refinements">Visual refinements&lt;/a>&lt;/h2>
&lt;p>And because I can&amp;rsquo;t leave anything alone I started tinkering with the design of a few things.&lt;/p>
&lt;h3 id="home-page">&lt;a href="#home-page">Home page&lt;/a>&lt;/h3>
&lt;p>Replaced the glitched image of me on the home page with a faux Polaroid frame and fixed it&amp;rsquo;s alignment with the rest of the page.&lt;/p>
&lt;p>
&lt;img alt="Home template with intro and Polaroid photo" class="img--md" decoding="async" fetchpriority="auto" height="139" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/mm-2021-home-page-intro_hu401166712802752131.jpg" style="background: #f6f4f3;" width="300"
srcset="https://mademistakes.com/images/mm-2021-home-page-intro_hu401166712802752131.jpg 300w, https://mademistakes.com/images/mm-2021-home-page-intro_hu5151932309229409306.jpg 620w"
>&lt;/p>
&lt;h3 id="archive-pages">&lt;a href="#archive-pages">Archive pages&lt;/a>&lt;/h3>
&lt;p>Changed the styling of the related tags component from a multi-column to an inline list to save space. I also reused this component on archive pages to help surface related topics that might have been buried before.&lt;/p>
&lt;p>
&lt;img alt="Related topics inline list component" class="img--md" decoding="async" fetchpriority="auto" height="56" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/mm-2021-related-topics-component_hu8754432499337832632.jpg" style="background: #fefefe;" width="300"
srcset="https://mademistakes.com/images/mm-2021-related-topics-component_hu8754432499337832632.jpg 300w, https://mademistakes.com/images/mm-2021-related-topics-component_hu8589634834953997869.jpg 620w"
>&lt;/p>
&lt;p>Added the featured posts component (used on the home page) to all archive pages&amp;hellip; if applicable.&lt;/p>
&lt;p>
&lt;img alt="Featured posts component" class="img--md" decoding="async" fetchpriority="auto" height="137" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/mm-2021-featured-posts_hu9972145697890657328.jpg" style="background: #fbfcfb;" width="300"
srcset="https://mademistakes.com/images/mm-2021-featured-posts_hu9972145697890657328.jpg 300w, https://mademistakes.com/images/mm-2021-featured-posts_hu16335469294879292098.jpg 620w"
>&lt;/p>
&lt;h3 id="works-page">&lt;a href="#works-page">Works page&lt;/a>&lt;/h3>
&lt;p>Pulled in more GitHub repositories under the &lt;strong>Open source contributions&lt;/strong> section, added fork counts, and switched to a staggered card layout to add interest.&lt;/p>
&lt;p>
&lt;img alt="GitHub repositories arranged in a grid of cards" class="img--md" decoding="async" fetchpriority="auto" height="201" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/mm-2021-github-repository-cards_hu632097396701752448.jpg" style="background: #fcfdfc;" width="300"
srcset="https://mademistakes.com/images/mm-2021-github-repository-cards_hu632097396701752448.jpg 300w, https://mademistakes.com/images/mm-2021-github-repository-cards_hu6215063667979427014.jpg 620w"
>&lt;/p>
&lt;p>Speaking of open source, I switched this repository to private on GitHub. Way too many lazy developers were reusing my content with minimal changes. I have nothing against people forking my work and making it their own. But replacing my name and social media links with your own and calling it a day is weak.&lt;/p>
&lt;p>I may make it public again if I have time to submodule out the content, but that might be too much pain for what it&amp;rsquo;s worth.&lt;/p>
&lt;h2 id="experimenting-with-ads">&lt;a href="#experimenting-with-ads">Experimenting with ads&lt;/a>&lt;/h2>
&lt;p>To help offset the small costs of keeping this site up I&amp;rsquo;m experimenting with Google AdSense again. I was ready to ditch them once I saw how bad they degraded page speed performance.&lt;/p>
&lt;p>After messing around with an &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API" rel="noopener">intersection observer&lt;/a>, I was able to keep things in check by waiting to load the Adsense scripts. I&amp;rsquo;m sure this JavaScript I added to Gatsby&amp;rsquo;s &lt;code>html.js&lt;/code> file could be improved on. But for now it&amp;rsquo;s doing the trick and I&amp;rsquo;m pennies richer for it.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="nx">script&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">dangerouslySetInnerHTML&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">__html&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="sb">`
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> (function(window, document) {
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> function loadAds() {
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> // Load Google AdSense
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> var ad = document.createElement(&amp;#39;script&amp;#39;);
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> ad.type = &amp;#39;text/javascript&amp;#39;;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> ad.async = true;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> ad.dataset.adClient = &amp;#39;ca-pub-xxxxxxxxxxxxxxxx&amp;#39;;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> ad.src = &amp;#39;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&amp;#39;;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> var sc = document.getElementsByTagName(&amp;#39;script&amp;#39;)[0];
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> sc.parentNode.insertBefore(ad, sc);
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> }
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> var lazyLoad = false;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> function onLazyLoad() {
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> if (lazyLoad === false) {
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> lazyLoad = true;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> loadAds();
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> console.log(&amp;#34;advertisements loaded&amp;#34;);
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> }
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> }
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> if(!!window.IntersectionObserver){
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> let observer = new IntersectionObserver((entries, observer) =&amp;gt; {
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> entries.forEach(entry =&amp;gt; {
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> if(entry.isIntersecting){
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> onLazyLoad();
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> observer.unobserve(entry.target);
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> }
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> });
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> }, { rootMargin: &amp;#34;0px 0px 500px 0px&amp;#34; });
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> document.querySelectorAll(&amp;#39;.adsbygoogle&amp;#39;).forEach(ad =&amp;gt; { observer.observe(ad) });
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> }
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> })(window, document);
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> `&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;a href="https://mademistakes.com/notes/gatsby-site-optimizations-2021/?utm_source=atom_feed">Not so quick with the Gatsby site optimizations&lt;/a> was originally published on Made Mistakes.&lt;/p></content><category scheme="https://mademistakes.com/tag/open-source" term="open-source" label="open source"/><category scheme="https://mademistakes.com/tag/static-sites" term="static-sites" label="static sites"/><category scheme="https://mademistakes.com/tag/tutorials" term="tutorials" label="tutorials"/><category scheme="https://mademistakes.com/tag/web-development" term="web-development" label="web development"/></entry><entry><title type="html">2 Little Rosebuds</title><link href="https://mademistakes.com/work/2-little-rosebuds/?utm_source=atom_feed" rel="alternate" type="text/html"/><id>https://mademistakes.com/work/2-little-rosebuds/</id><published>2020-01-21T00:00:00-05:00</published><updated>2020-01-22T10:12:52-05:00</updated><summary type="html">Creative and site development for 2 Little Rosebuds — a blog all about subscription boxes.</summary><content type="html">
&lt;img alt="" decoding="async" fetchpriority="auto" height="169" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/2-little-rosebuds-feature_hu18018707797974906216.jpg" style="background: #bae2d3;" width="300"
srcset="https://mademistakes.com/images/2-little-rosebuds-feature_hu18018707797974906216.jpg 300w,https://mademistakes.com/images/2-little-rosebuds-feature_hu13227185302485708702.jpg 800w, https://mademistakes.com/images/2-little-rosebuds-feature.jpg 1580w"
>&lt;p>&lt;a href="https://2littlerosebuds.com" rel="noopener">&lt;strong>2 Little Rosebuds&lt;/strong>&lt;/a> is a blog written by my wife Wendy Rose. The site started as a place for her to post about our twin girls and morphed into an outlet for her growing addiction to subscription boxes.&lt;/p>
&lt;p>My responsibilities on the site include:&lt;/p>
&lt;ul>
&lt;li>Wordpress installation and maintenance.&lt;/li>
&lt;li>Creative, theme design, and development.&lt;/li>
&lt;li>Content management.&lt;/li>
&lt;/ul>
&lt;h2 id="logo-design">&lt;a href="#logo-design">Logo design&lt;/a>&lt;/h2>
&lt;p>
&lt;img alt="2 Little Rosebuds logotype" class="img--md" decoding="async" fetchpriority="auto" height="107" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/two-little-rosebuds-logo_hu12917230745767727092.png" style="background: #ffffff;" width="300"
srcset="https://mademistakes.com/images/two-little-rosebuds-logo_hu12917230745767727092.png 300w, https://mademistakes.com/images/two-little-rosebuds-logo_hu17588431752857852313.png 620w"
>&lt;/p>
&lt;p>The &lt;strong>2 Little Rosebuds&lt;/strong> word mark has gone through several versions over the years. For this iteration I wanted something feminine &amp;mdash; with subtle hints at veganism and &amp;ldquo;the twins,&amp;rdquo; to relate back to the blog&amp;rsquo;s identity and subject matter.&lt;/p>
&lt;h2 id="style-tiles">&lt;a href="#style-tiles">Style tiles&lt;/a>&lt;/h2>
&lt;p>For all my projects I like to start with a quick sketch to help capture the overall tone and feel I&amp;rsquo;m going for. Creating a set of style tiles in Adobe Photoshop or Illustrator is my preferred way of doing that. Colors, basic typography, image treatments, and any other design elements I&amp;rsquo;m toying with all end up here.&lt;/p>
&lt;p>
&lt;img alt="2 Little Rosebuds identity style tiles" class="img--md" decoding="async" fetchpriority="auto" height="722" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/2lrb-style-tiles_hu16837459979724735401.png" style="background: #e7e3e3;" width="300"
srcset="https://mademistakes.com/images/2lrb-style-tiles_hu16837459979724735401.png 300w, https://mademistakes.com/images/2lrb-style-tiles_hu445581395204826250.png 620w"
>&lt;/p>
&lt;h2 id="wordpress-layouts">&lt;a href="#wordpress-layouts">Wordpress layouts&lt;/a>&lt;/h2>
&lt;p>With over 3,500 posts in the last eight years, &lt;strong>2 Little Rosebuds&lt;/strong> has become an authority on all things &amp;ldquo;subscription box.&amp;rdquo; My main goals were to keep the experience familiar and to simplify the underlying code and design wherever possible.&lt;/p>
&lt;p>This meant keeping the established color palette, but with minor adjustments to maintain adequate contrast for improved accessibility.&lt;/p>
&lt;p>Reducing the amount of typefaces used to improve page loading speeds. And combining the masthead, navigation bar, and search form into a single organism to give space back to what&amp;rsquo;s important &amp;mdash; post content.&lt;/p>
&lt;figure class="figure">
&lt;div class="browser-frame">&lt;p>
&lt;img alt="Screenshot of post layout." class="img--md" decoding="async" fetchpriority="auto" height="629" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/2lrb-post-layout_hu9383348575185666775.jpg" style="background: #f9f7f6;" width="300"
&lt;pre>&lt;code> srcset=&amp;quot;https://mademistakes.com/images/2lrb-post-layout_hu9383348575185666775.jpg 300w, https://mademistakes.com/images/2lrb-post-layout_hu16998727187537031759.jpg 620w&amp;quot;
&amp;gt;&amp;lt;/p&amp;gt;
&lt;/code>&lt;/pre>
&lt;/div>
&lt;figcaption>Single post layout.&lt;/figcaption>
&lt;/figure>
&lt;p>Post content is given prominence by using a centered single column layout to make reading comfortable. Just Wendy&amp;rsquo;s words and photographs &amp;mdash; no side bar clutter or distracting advertisements.&lt;/p>
&lt;figure class="figure">
&lt;div class="browser-frame">&lt;p>
&lt;img alt="Screenshot of category archive layout." class="img--md" decoding="async" fetchpriority="auto" height="313" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/2lrb-category-archive-layout_hu8164571630906025463.jpg" style="background: #f9f3f3;" width="300"
&lt;pre>&lt;code> srcset=&amp;quot;https://mademistakes.com/images/2lrb-category-archive-layout_hu8164571630906025463.jpg 300w, https://mademistakes.com/images/2lrb-category-archive-layout_hu8255706024119937359.jpg 620w&amp;quot;
&amp;gt;&amp;lt;/p&amp;gt;
&lt;/code>&lt;/pre>
&lt;/div>
&lt;figcaption>Category archive layout.&lt;/figcaption>
&lt;/figure>
&lt;p>Layouts used for the directory got a refresher to better showcase the 650 subscription boxes listed within. Search, sort controls, box type refinements, and review scores are placed in familiar locations to help discover subscriptions while browsing.&lt;/p>
&lt;figure class="figure">
&lt;div class="browser-frame">&lt;p>
&lt;img alt="Screenshot of subscription box directory." class="img--md" decoding="async" fetchpriority="auto" height="251" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/2lrb-subscription-box-directory_hu221677947028435887.jpg" style="background: #fbf8f8;" width="300"
&lt;pre>&lt;code> srcset=&amp;quot;https://mademistakes.com/images/2lrb-subscription-box-directory_hu221677947028435887.jpg 300w, https://mademistakes.com/images/2lrb-subscription-box-directory_hu4241703625271259545.jpg 620w&amp;quot;
&amp;gt;&amp;lt;/p&amp;gt;
&lt;/code>&lt;/pre>
&lt;/div>
&lt;figcaption>Subscription box directory layout.&lt;/figcaption>
&lt;/figure>
&lt;figure class="figure">
&lt;div class="browser-frame">&lt;p>
&lt;img alt="Screenshot of subscription box page." class="img--md" decoding="async" fetchpriority="auto" height="367" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/2lrb-subscription-box-layout_hu15658623637715645819.jpg" style="background: #f6f6f6;" width="300"
&lt;pre>&lt;code> srcset=&amp;quot;https://mademistakes.com/images/2lrb-subscription-box-layout_hu15658623637715645819.jpg 300w, https://mademistakes.com/images/2lrb-subscription-box-layout_hu17569311635511069824.jpg 620w&amp;quot;
&amp;gt;&amp;lt;/p&amp;gt;
&lt;/code>&lt;/pre>
&lt;/div>
&lt;figcaption>Subscription box detail page layout.&lt;/figcaption>
&lt;/figure>&lt;p>&lt;a href="https://mademistakes.com/work/2-little-rosebuds/?utm_source=atom_feed">2 Little Rosebuds&lt;/a> was originally published on Made Mistakes.&lt;/p></content><category scheme="https://mademistakes.com/tag/design" term="design" label="design"/><category scheme="https://mademistakes.com/tag/web-development" term="web-development" label="web development"/></entry><entry><title type="html">Twenty Nineteen</title><link href="https://mademistakes.com/notes/twenty-nineteen/?utm_source=atom_feed" rel="alternate" type="text/html"/><id>https://mademistakes.com/notes/twenty-nineteen/</id><published>2020-01-01T00:00:00-05:00</published><updated>2024-02-21T08:42:53-05:00</updated><summary type="html">First time doing a year in review post. The statistics contained are just for fun as there&amp;#39;s nothing to compare them against yet.</summary><content type="html">
&lt;img alt="" decoding="async" fetchpriority="auto" height="169" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/twenty-19_hu15397861078631767788.jpg" style="background: #101d2f;" width="300"
srcset="https://mademistakes.com/images/twenty-19_hu15397861078631767788.jpg 300w,https://mademistakes.com/images/twenty-19_hu17933888122944094007.jpg 800w, https://mademistakes.com/images/twenty-19.jpg 1280w"
>&lt;p>First time doing a &lt;em>year in review&lt;/em> post thingy. The statistics below are just for fun as there&amp;rsquo;s nothing to compare them against yet. Should be interesting to see how they deviate next year as I would have never guessed some would be as high as they were.&lt;/p>
&lt;h2 id="2019-by-the-numbers">&lt;a href="#2019-by-the-numbers">2019 by the numbers&lt;/a>&lt;/h2>
&lt;ul>
&lt;li>&lt;strong>1&lt;/strong> blog post&lt;/li>
&lt;li>&lt;strong>4&lt;/strong> side projects&lt;/li>
&lt;li>&lt;strong>365&lt;/strong> days spent growing a beard&lt;/li>
&lt;li>&lt;strong>33&lt;/strong> video games played&lt;/li>
&lt;li>&lt;strong>64&lt;/strong> movies watched&lt;/li>
&lt;li>&lt;strong>56&lt;/strong> days closing all rings on Apple Watch&lt;/li>
&lt;li>&lt;strong>6,522&lt;/strong> steps/day average&lt;/li>
&lt;/ul>
&lt;h3 id="web-development">&lt;a href="#web-development">Web development&lt;/a>&lt;/h3>
&lt;p>Starting way back in 2017 I experimented with migrating my &lt;a href="https://jekyllrb.com/" rel="noopener">&lt;strong>Jekyll&lt;/strong>&lt;/a> built personal site over to &lt;a href="https://www.gatsbyjs.com/" rel="noopener">&lt;strong>Gatsby&lt;/strong>&lt;/a>&amp;hellip; things didn&amp;rsquo;t work out so well. It took until 2019 for Gatsby and its ecosystem to mature enough for me to take it seriously.&lt;/p>
&lt;p>Up until then it couldn&amp;rsquo;t handle building my site of ~1,000 posts, paginated category/tag archives, and 13,000 images. Plagued with &lt;code>FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory&lt;/code> messages I waited as Gatsby received performance optimizations that fixed my build woes.&lt;/p>
&lt;p>With the right cocktail of plugins I finally launched a Gatsby powered site on the last day of 2019, December 31st. Expect a post mortem in a future article as there was a lot I struggled with:&lt;/p>
&lt;ul>
&lt;li>Adapting Liquid templates to vanilla JavaScript and React.&lt;/li>
&lt;li>Finding Markdown parity with Kramdown features.&lt;/li>
&lt;li>Deploying my own instance of &lt;a href="/mastering-jekyll/static-comments/">&lt;strong>Staticman&lt;/strong>&lt;/a> and getting comments working with Gatsby.&lt;/li>
&lt;li>Replacing custom Jekyll plugin tags.&lt;/li>
&lt;li>Working through &lt;strong>Lighthouse&lt;/strong> scores that were often worse than the pre-Gatsby site.&lt;/li>
&lt;li>Build timeouts on &lt;strong>Netlify&lt;/strong>.&lt;/li>
&lt;/ul>
&lt;h3 id="open-source-contributions">&lt;a href="#open-source-contributions">Open source contributions&lt;/a>&lt;/h3>
&lt;p>
&lt;img alt="2019 GitHub contributions" class="img--md" decoding="async" fetchpriority="auto" height="60" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/git-commits-2019_hu2209307076834167913.png" style="background: #f5f9f1;" width="300"
srcset="https://mademistakes.com/images/git-commits-2019_hu2209307076834167913.png 300w, https://mademistakes.com/images/git-commits-2019_hu4433882826022145944.png 620w"
>&lt;/p>
&lt;p>In 2019 I pushed &lt;strong>1,056&lt;/strong> commits to GitHub, most of which were to my three most popular Jekyll themes: &lt;a href="/work/jekyll-themes/minimal-mistakes/">Minimal Mistakes&lt;/a>, &lt;a href="/work/jekyll-themes/so-simple/">So Simple&lt;/a>, and &lt;a href="/work/jekyll-themes/basically-basic/">Basically Basic&lt;/a>.&lt;/p>
&lt;p>It boggles my mind that a repository that started out as a stripped down theme of my personal site has been: downloaded 96,901 times, forked 10,100 times, and starred 5,993 times. Thanks to everyone who has supported its continued development with kind words and donations!&lt;/p>
&lt;h3 id="a-yeard-and-a-half">&lt;a href="#a-yeard-and-a-half">A yeard and a half&lt;/a>&lt;/h3>
&lt;p>I&amp;rsquo;ve had a love/hate relationship with beard growing for as long as I can remember. For whatever stupid reason I decided I wanted to grow a yeard in 2018&amp;hellip; during the middle of the summer.&lt;/p>
&lt;figure class="figure" data-columns="3">
&lt;p>
&lt;img alt="Beard growth after 3 months." class="img--md" decoding="async" fetchpriority="auto" height="400" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/michael-rose-beard-3mos_hu13258348151181717979.jpg" style="background: #1e2422;" width="300"
srcset="https://mademistakes.com/images/michael-rose-beard-3mos_hu13258348151181717979.jpg 300w, https://mademistakes.com/images/michael-rose-beard-3mos_hu8892534833422467487.jpg 620w"
>&lt;/p>
&lt;p>
&lt;img alt="Beard growth after 6 months." class="img--md" decoding="async" fetchpriority="auto" height="399" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/michael-rose-beard-6mos_hu1693904278468741537.jpg" style="background: #181716;" width="300"
srcset="https://mademistakes.com/images/michael-rose-beard-6mos_hu1693904278468741537.jpg 300w, https://mademistakes.com/images/michael-rose-beard-6mos_hu10530434163348098423.jpg 620w"
>&lt;/p>
&lt;p>
&lt;img alt="Beard growth after a year." class="img--md" decoding="async" fetchpriority="auto" height="400" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/michael-rose-beard-12mos_hu6316028053095863570.jpg" style="background: #393637;" width="300"
srcset="https://mademistakes.com/images/michael-rose-beard-12mos_hu6316028053095863570.jpg 300w, https://mademistakes.com/images/michael-rose-beard-12mos_hu6739417172786139642.jpg 620w"
>&lt;/p>
&lt;figcaption>Beard progression at 3 months, 6 months, and 12 months.&lt;/figcaption>
&lt;/figure>
&lt;p>At the close of 2019 I&amp;rsquo;ve gone eighteen months without shaving and plan on trying for whatever you call a 2x yeard. We&amp;rsquo;ll see though as eating soup, snagging hairs in zippers, and daily beard maintenance does get old on occassion.&lt;/p>
&lt;h3 id="video-games-played">&lt;a href="#video-games-played">Video games played&lt;/a>&lt;/h3>
&lt;p>If it wasn&amp;rsquo;t for the Nintendo Switch and being a portable console, I would have never played this many games. Being able to quickly resume a game before dinner or when the twins are in bed makes a difference.&lt;/p>
&lt;blockquote class="twitter-tweet" data-dnt="true">&lt;p lang="en" dir="ltr">This game is ridic.&lt;a href="https://twitter.com/hashtag/UntitledGooseGame?src=hash&amp;amp;ref_src=twsrc%5Etfw">#UntitledGooseGame&lt;/a> &lt;a href="https://twitter.com/hashtag/NintendoSwitch?src=hash&amp;amp;ref_src=twsrc%5Etfw">#NintendoSwitch&lt;/a> &lt;a href="https://t.co/0YHhFVmrpU">pic.twitter.com/0YHhFVmrpU&lt;/a>&lt;/p>&amp;mdash; 𝔐𝔦𝔠𝔥𝔞𝔢𝔩 ℜ𝔬𝔰𝔢 (@mmistakes) &lt;a href="https://twitter.com/mmistakes/status/1175410210359238657?ref_src=twsrc%5Etfw">September 21, 2019&lt;/a>&lt;/blockquote>
&lt;script async src="https://platform.twitter.com/widgets.js" charset="utf-8">&lt;/script>
&lt;p>I&amp;rsquo;m slowly making it through my backlog of Playstation must plays, but sneaking those in has been difficult. As most have mature ratings, they&amp;rsquo;re not something I can play with my kids. And the fact that it&amp;rsquo;s not a portable console means I need to wrestle the television from my wife, who sure as shit doesn&amp;rsquo;t want to watch me game for an hour.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Game&lt;/th>
&lt;th>Rating&lt;/th>
&lt;th>Note&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>Sayonara Wild Hearts&lt;/strong> (Switch)&lt;/td>
&lt;td>★★★★&lt;/td>
&lt;td>Not quite as rhythm heavy as I was expecting, but solid.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Untitled Goose Game&lt;/strong> (Switch)&lt;/td>
&lt;td>★★★★★&lt;/td>
&lt;td>A game I never knew I wanted till I did.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Beyond Two Souls&lt;/strong> (PS4)&lt;/td>
&lt;td>★★★&lt;/td>
&lt;td>About 2 hours in, I&amp;rsquo;m a sucker for David Cage games.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Blasphemous&lt;/strong> (Switch)&lt;/td>
&lt;td>★★★&lt;/td>
&lt;td>Started the demo, will revist later.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>River City Girls&lt;/strong> (Switch)&lt;/td>
&lt;td>★★★★&lt;/td>
&lt;td>Music, art, brawling is all great. Has me pumped for &lt;strong>Streets of Rage 4&lt;/strong>.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Luigi&amp;rsquo;s Mansion 3&lt;/strong> (Switch)&lt;/td>
&lt;td>★★★&lt;/td>
&lt;td>Controls take some getting used to. CO-OP is fun once you unlock Gooigi. Challenging boss battles.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Detroit: Become Human&lt;/strong> (PS4)&lt;/td>
&lt;td>★★★★&lt;/td>
&lt;td>Would like to replay to see all missed story paths&amp;hellip; if I ever have the time.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Super Mario Maker 2&lt;/strong> (Switch)&lt;/td>
&lt;td>★★★★★&lt;/td>
&lt;td>Consumer of Mario levels more so than a creator. Suffered through online lag to hit Rank A in multiplayer.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Uncharted: The Lost Legacy&lt;/strong> (PS4)&lt;/td>
&lt;td>★★★★&lt;/td>
&lt;td>Completed the story.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>The Legend of Zelda: A Link to the Past&lt;/strong> (Switch SNES)&lt;/td>
&lt;td>★★★★★&lt;/td>
&lt;td>Playing through for the first time on an actual console. At the last dungeon, ready to fight Ganon.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Super Mario World&lt;/strong> (Switch SNES)&lt;/td>
&lt;td>★★★★★&lt;/td>
&lt;td>Mario Maker 2 got me revisiting the classics.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Ninja Gaiden&lt;/strong> (Switch NES)&lt;/td>
&lt;td>★★★★★&lt;/td>
&lt;td>How I ever beat this as a kid is beyond me.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Zelda II: The Adventure of Linke&lt;/strong> Switch (NES)&lt;/td>
&lt;td>★★★★&lt;/td>
&lt;td>That last palace is a doozy&amp;hellip;&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Uncharted 4: A Thief&amp;rsquo;s End&lt;/strong> (PS4)&lt;/td>
&lt;td>★★★★★&lt;/td>
&lt;td>Completed the story, missed a bunch of collectibles.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Uncharted The Nathan Drake Collection&lt;/strong> (PS4)&lt;/td>
&lt;td>★★★&lt;/td>
&lt;td>Rushed my way through 1, 2, and 3 so I could start 4.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>New Super Mario Bros. U Deluxe&lt;/strong> (Switch)&lt;/td>
&lt;td>★★★★&lt;/td>
&lt;td>I&amp;rsquo;m cool with Nintendo re-releasing Wii U games I never played.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Cuphead&lt;/strong> (Switch)&lt;/td>
&lt;td>★★★★&lt;/td>
&lt;td>20% complete. This game isn&amp;rsquo;t fooling around.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Sonic Mania&lt;/strong> (Xbox One)&lt;/td>
&lt;td>★★★★&lt;/td>
&lt;td>Fits in perfectly with the Sega Genesis era of games. Challenge ramps up at the end.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Super Kirby Clash&lt;/strong> (Switch)&lt;/td>
&lt;td>★★&lt;/td>
&lt;td>Micro-transaction hell.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Super Phantom Cat: Remake&lt;/strong> (Switch)&lt;/td>
&lt;td>★★★&lt;/td>
&lt;td>Purchased on the cheap, OK platformer.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Yoshi&amp;rsquo;s Crafted World&lt;/strong> (Switch)&lt;/td>
&lt;td>★★★★&lt;/td>
&lt;td>Great art direction. CO-OP&amp;rsquo;d with the twins.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Super Mario Party&lt;/strong> (Switch)&lt;/td>
&lt;td>★★★&lt;/td>
&lt;td>Great amount of mini-games but really wish it would let you use Pro Controllers.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Super Mario Odyssey&lt;/strong> (Switch)&lt;/td>
&lt;td>★★★★★&lt;/td>
&lt;td>Completed the story and bought most costumes. Collected 596 moons.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Tetris 99&lt;/strong> (Switch)&lt;/td>
&lt;td>★★★★&lt;/td>
&lt;td>Love me some Tetris. Won 2 of 388 games.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Thumper&lt;/strong> (Switch)&lt;/td>
&lt;td>★★★★&lt;/td>
&lt;td>So much dread.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Gris&lt;/strong> (Switch)&lt;/td>
&lt;td>★★★★&lt;/td>
&lt;td>Walking simulator with a watercolor aesthetic.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Hollow Knight&lt;/strong> (Switch)&lt;/td>
&lt;td>★★★★&lt;/td>
&lt;td>Sank over 20 hours into it and no idea if I&amp;rsquo;m near the end or not.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>What Remains of Edith Finch&lt;/strong> (PS4)&lt;/td>
&lt;td>★★★★&lt;/td>
&lt;td>Surprisingly good Playstation Plus free game.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Hellblade: Senua&amp;rsquo;s Sacrifice&lt;/strong> (PS4)&lt;/td>
&lt;td>★★★★★&lt;/td>
&lt;td>Loved everything about this game. Can&amp;rsquo;t wait for the sequel.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Bulletstorm: Full Clip Edition&lt;/strong> (PS4)&lt;/td>
&lt;td>★★★&lt;/td>
&lt;td>Picked up for free via Playstation Plus, about 75% the way through. Potty mouth shooter fare.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>The Last Guardian&lt;/strong>&lt;/td>
&lt;td>★★&lt;/td>
&lt;td>Really wanted to like this one, but the floaty ass controls ruins it for me. Made it about midway through the story&amp;hellip; I think.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Shadow of the Tomb Raider&lt;/strong> (PS4)&lt;/td>
&lt;td>★★★★&lt;/td>
&lt;td>Enjoyed the tropical scenery more than the snow in Rise.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>The Order: 1886&lt;/strong> (PS4)&lt;/td>
&lt;td>★★★★&lt;/td>
&lt;td>Purchased on sale, not bad.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Super Smash Bros. Ultimate&lt;/strong> (Switch)&lt;/td>
&lt;td>★★★&lt;/td>
&lt;td>Awful at fighting games. Unlocked all the characters, stopped playing shortly after.&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="movies-watched">&lt;a href="#movies-watched">Movies watched&lt;/a>&lt;/h3>
&lt;p>According to &lt;a href="https://letterboxd.com/mmistakes/" rel="noopener">&lt;strong>Letterboxd&lt;/strong>&lt;/a> below are all the flicks I watched last year. And here I thought I binged more television series than movies &amp;mdash; guess not.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Movie&lt;/th>
&lt;th>Rating&lt;/th>
&lt;th>Note&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>Star Wars: The Rise of Skywalker&lt;/strong> (2019)&lt;/td>
&lt;td>★★★&lt;/td>
&lt;td>Should have been split into two parts.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Mission: Impossible - Fallout&lt;/strong> (2018)&lt;/td>
&lt;td>★★★&lt;/td>
&lt;td>By the Numbers.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Marriage Story&lt;/strong> (2019)&lt;/td>
&lt;td>★★★&lt;/td>
&lt;td>Meh.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Frozen 2&lt;/strong> (2019)&lt;/td>
&lt;td>★★★&lt;/td>
&lt;td>Too much singing.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>The Grinch&lt;/strong> (2018)&lt;/td>
&lt;td>★★★&lt;/td>
&lt;td>Didn&amp;rsquo;t bore me as much as the Jim Carrey version.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Klaus&lt;/strong> (2019)&lt;/td>
&lt;td>★★★&lt;/td>
&lt;td>For the kids&amp;hellip; and it’s the Christmas season right?&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>The Irishman&lt;/strong> (2019)&lt;/td>
&lt;td>★★★&lt;/td>
&lt;td>The digital de-aging didn&amp;rsquo;t bother me.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>The Game Changers&lt;/strong> (2018)&lt;/td>
&lt;td>★★★&lt;/td>
&lt;td>Tough vegans.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>The Lobster&lt;/strong> (2015)&lt;/td>
&lt;td>★★★&lt;/td>
&lt;td>I have questions.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Carrie&lt;/strong> (1976)&lt;/td>
&lt;td>★★★&lt;/td>
&lt;td>So that’s where all the references came from.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Tell Me Who I Am&lt;/strong> (2019)&lt;/td>
&lt;td>★★★★&lt;/td>
&lt;td>I&amp;rsquo;m a sucker for documentaries of this nature.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>El Camino: A Breaking Bad Movie&lt;/strong> (2019)&lt;/td>
&lt;td>★★★&lt;/td>
&lt;td>Unnecessary but good.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Skin&lt;/strong> (2018)&lt;/td>
&lt;td>★★★★&lt;/td>
&lt;td>If it has a hint of a true story to it, I&amp;rsquo;ll bite.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Between Two Ferns: The Movie&lt;/strong> (2019)&lt;/td>
&lt;td>★★★★&lt;/td>
&lt;td>Yup, what I expected.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>In the Shadow of the Moon&lt;/strong> (2019)&lt;/td>
&lt;td>★★★&lt;/td>
&lt;td>Sort of like &lt;strong>Looper&lt;/strong> I guess.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Pulp Fiction&lt;/strong> (1994)&lt;/td>
&lt;td>★★★★★&lt;/td>
&lt;td>Rewatched.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Don&amp;rsquo;t Break Down: A Film About Jawbreaker&lt;/strong> (2017)&lt;/td>
&lt;td>★★★★&lt;/td>
&lt;td>Probably need to enjoy their music to get something out of this.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Screwball&lt;/strong> (2018)&lt;/td>
&lt;td>★★★&lt;/td>
&lt;td>Roids.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Ralph Breaks the Internet&lt;/strong> (2018)&lt;/td>
&lt;td>★★★&lt;/td>
&lt;td>A few memes a lot of zzzZzzs.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Jack of all Trades&lt;/strong> (2018)&lt;/td>
&lt;td>★★★&lt;/td>
&lt;td>Hard toilet paper.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Pokémon Detective Pikachu&lt;/strong> (2019)&lt;/td>
&lt;td>★★&lt;/td>
&lt;td>I call bullshit.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Bob Lazar: Area 51 and Flying Saucers&lt;/strong> (2018)&lt;/td>
&lt;td>★★&lt;/td>
&lt;td>Zzzzzz.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Being Frank: The Chris Sievey Story&lt;/strong> (2018)&lt;/td>
&lt;td>★★★&lt;/td>
&lt;td>The true story behind that one Michael Fassbender flick.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Toy Story 4&lt;/strong> (2019)&lt;/td>
&lt;td>★★★&lt;/td>
&lt;td>Spoony.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Us&lt;/strong> (2019)&lt;/td>
&lt;td>★★★&lt;/td>
&lt;td>I have questions.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Avengers: Endgame&lt;/strong> (2019)&lt;/td>
&lt;td>★★★&lt;/td>
&lt;td>Ten years of plot threads tied up in 3 hours&amp;hellip; exactly how you&amp;rsquo;d expect.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>The Transformers: The Movie&lt;/strong> (1986)&lt;/td>
&lt;td>★★★★★&lt;/td>
&lt;td>Rewatched with commentary. Not much value added from the director who explained exactly what we saw on screen. More insight from the writer would have been better.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Extremely Wicked, Shockingly Evil and Vile&lt;/strong> (2019)&lt;/td>
&lt;td>★★★&lt;/td>
&lt;td>Found the tapes documentary more interesting.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>The Billboard Boys&lt;/strong> (2016)&lt;/td>
&lt;td>★★★&lt;/td>
&lt;td>&lt;em>&amp;hellip;took a shower, ready to go another two years.&lt;/em>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Mids 90s&lt;/strong> (2018)&lt;/td>
&lt;td>★★★&lt;/td>
&lt;td>Didn&amp;rsquo;t disappoint with the non-ending.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Zodiac&lt;/strong> (2007)&lt;/td>
&lt;td>★★★&lt;/td>
&lt;td>Rewatched.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Glass&lt;/strong> (2019)&lt;/td>
&lt;td>★★★&lt;/td>
&lt;td>Of course it was a purple iMac.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Christopher Robin&lt;/strong> (2018)&lt;/td>
&lt;td>★★★&lt;/td>
&lt;td>Meh.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>This is 40&lt;/strong> (2012)&lt;/td>
&lt;td>★★★★&lt;/td>
&lt;td>Relatable.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>The Endless&lt;/strong> (2017)&lt;/td>
&lt;td>★★★&lt;/td>
&lt;td>Groundhog day for hipsters.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>The Stanford Prison Experiment&lt;/strong> (2015)&lt;/td>
&lt;td>★★★&lt;/td>
&lt;td>So many bad fake mustaches.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>The Fear of 13&lt;/strong> (2015)&lt;/td>
&lt;td>★★★&lt;/td>
&lt;td>Prison.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Kong: Skull Island&lt;/strong> (2017)&lt;/td>
&lt;td>★★★&lt;/td>
&lt;td>Dong.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Aquaman&lt;/strong> (2018)&lt;/td>
&lt;td>★★★★&lt;/td>
&lt;td>&lt;em>We could have just peed on it.&lt;/em>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Creed II&lt;/strong> (2018)&lt;/td>
&lt;td>★★★&lt;/td>
&lt;td>Nostalgic for &lt;strong>Rocky IV&lt;/strong>.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>The Hateful Eight&lt;/strong> (2015)&lt;/td>
&lt;td>★★★&lt;/td>
&lt;td>Mmm, jelly beans.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Triple Frontier&lt;/strong> (2019)&lt;/td>
&lt;td>★★★&lt;/td>
&lt;td>Kept waiting for the other shoe to drop, then &lt;strong>Metallica&lt;/strong> did.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>The Matrix Revolutions&lt;/strong> (2003)&lt;/td>
&lt;td>★★★&lt;/td>
&lt;td>Rewatched.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>The Matrix Reloaded&lt;/strong> (2003)&lt;/td>
&lt;td>★★★&lt;/td>
&lt;td>Rewatched.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Eighth Grade&lt;/strong> (2018)&lt;/td>
&lt;td>★★★&lt;/td>
&lt;td>Just wait until high school.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>The Matrix&lt;/strong> (1999)&lt;/td>
&lt;td>★★★★&lt;/td>
&lt;td>Rewatched.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>The Lost Arcade&lt;/strong> (2015)&lt;/td>
&lt;td>★★★&lt;/td>
&lt;td>Fighting game snobbery.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Free Solo&lt;/strong> (2018)&lt;/td>
&lt;td>★★★★&lt;/td>
&lt;td>&amp;ldquo;So delighted.&amp;rdquo;&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Mandy&lt;/strong> (2018)&lt;/td>
&lt;td>★★★&lt;/td>
&lt;td>There&amp;rsquo;s no arguing this thing oozed style. Not sure what the fuck I just dreamed though.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Lords of Chaos&lt;/strong> (2018)&lt;/td>
&lt;td>★★★&lt;/td>
&lt;td>Strangely enough, black metal and Sigur Rós compliment each other well.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Paddleton&lt;/strong> (2019)&lt;/td>
&lt;td>★★★&lt;/td>
&lt;td>Slow burn.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Shirkers&lt;/strong> (2018)&lt;/td>
&lt;td>★★&lt;/td>
&lt;td>This score really annoyed the piss outta me.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Isle of Dogs&lt;/strong> (2018)&lt;/td>
&lt;td>★★★&lt;/td>
&lt;td>Yup, it&amp;rsquo;s a W.A. film.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Alt-Right: Age of Rage&lt;/strong> (2018)&lt;/td>
&lt;td>★★★&lt;/td>
&lt;td>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>The Edge of Seventeen&lt;/strong> (2016)&lt;/td>
&lt;td>★★★&lt;/td>
&lt;td>I feel like I&amp;rsquo;ve seen this movie before&amp;hellip;&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Abducted in Plain Sight&lt;/strong> (2017)&lt;/td>
&lt;td>★★★★&lt;/td>
&lt;td>Wow&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Root Cause&lt;/strong> (2019)&lt;/td>
&lt;td>★★★&lt;/td>
&lt;td>No comment.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>The Family I Had&lt;/strong> (2017)&lt;/td>
&lt;td>★★★&lt;/td>
&lt;td>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Toy Story 2&lt;/strong> (1999)&lt;/td>
&lt;td>★★★&lt;/td>
&lt;td>Rewatched.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Toy Story&lt;/strong> (1995)&lt;/td>
&lt;td>★★★&lt;/td>
&lt;td>Rewatched.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Fyre&lt;/strong> (2019)&lt;/td>
&lt;td>★★★&lt;/td>
&lt;td>Living the dream.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Unbreakable&lt;/strong> (2000)&lt;/td>
&lt;td>★★★★&lt;/td>
&lt;td>Rewatched.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>A Fat Wreck&lt;/strong> (2016)&lt;/td>
&lt;td>★★★★&lt;/td>
&lt;td>Nostalgic for pop punk.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Solo: A Star Wars Story&lt;/strong> (2018)&lt;/td>
&lt;td>★★★★&lt;/td>
&lt;td>Can&amp;rsquo;t believe all the negative press.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Black Mirror: Bandersnatch&lt;/strong> (2018)&lt;/td>
&lt;td>★★★&lt;/td>
&lt;td>Would have been fine with this not being &lt;em>interactive&lt;/em> at all.&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="health-and-activity">&lt;a href="#health-and-activity">Health and activity&lt;/a>&lt;/h3>
&lt;p>The last couple of years I&amp;rsquo;ve been wanting to ditch FitBit devices for Apple Watch. My hestitation being battery life and needing to charge it daily. As someone who wore a Fitbit for 5 days straight without charging &amp;mdash; this was a deal breaker.&lt;/p>
&lt;p>When Apple released a Watch this fall with an always-on screen, I decided to try it out. On paper it does pretty much everything the Fitbits I&amp;rsquo;ve owned for years do. But some reason it&amp;rsquo;s motivated me to be active in ways they couldn&amp;rsquo;t.&lt;/p>
&lt;figure class="figure" data-columns="3">
&lt;p>
&lt;img alt="Closed rings for December 2019 in the Activity iOS app." class="img--md" decoding="async" fetchpriority="auto" height="516" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/activity-app-01-2020_hu13862892587147219655.jpg" style="background: #070605;" width="300"
srcset="https://mademistakes.com/images/activity-app-01-2020_hu13862892587147219655.jpg 300w, https://mademistakes.com/images/activity-app-01-2020_hu6433386675963613535.jpg 620w"
>&lt;/p>
&lt;p>
&lt;img alt="Activity in the Health app for 2019." class="img--md" decoding="async" fetchpriority="auto" height="516" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/health-app-activity-2019_hu13943310903236922467.jpg" style="background: #f9fafa;" width="300"
srcset="https://mademistakes.com/images/health-app-activity-2019_hu13943310903236922467.jpg 300w, https://mademistakes.com/images/health-app-activity-2019_hu196034954084066646.jpg 620w"
>&lt;/p>
&lt;p>
&lt;img alt="Attain app dashbaord in January of 2019." class="img--md" decoding="async" fetchpriority="auto" height="516" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/attain-app-january-2020_hu7207459174202217990.jpg" style="background: #d00892;" width="300"
srcset="https://mademistakes.com/images/attain-app-january-2020_hu7207459174202217990.jpg 300w, https://mademistakes.com/images/attain-app-january-2020_hu17563866788389250117.jpg 620w"
>&lt;/p>
&lt;figcaption>iOS health app screenshots for 2019.&lt;/figcaption>
&lt;/figure>
&lt;p>I&amp;rsquo;ve gone from hitting move and workout goals 3 days a week to all 7. Earning gift card rewards via &lt;a href="https://www.attainbyaetna.com/" rel="noopener">Aetna&amp;rsquo;s &lt;strong>Attain&lt;/strong> app&lt;/a> is a great motivator as well.&lt;/p>
&lt;p>I still don&amp;rsquo;t like that I need to charge it everyday since I use it for sleep tracking. But a well timed charge during my morning shower is usually enough to top it off for the day.&lt;/p>
&lt;h2 id="heading-into-2020">&lt;a href="#heading-into-2020">Heading into 2020&lt;/a>&lt;/h2>
&lt;p>I don&amp;rsquo;t consciously set goals or make resolutions really. If I can do more of the same as 2019 I&amp;rsquo;ll be happy. Now that I&amp;rsquo;m more active, I&amp;rsquo;d like to amp up my consumption of fruits and vegetables this year. I eat a lot of beige processed food that I&amp;rsquo;m sure is up to no good in my body.&lt;/p>
&lt;p>We&amp;rsquo;ll see I guess&amp;hellip;&lt;/p>&lt;p>&lt;a href="https://mademistakes.com/notes/twenty-nineteen/?utm_source=atom_feed">Twenty Nineteen&lt;/a> was originally published on Made Mistakes.&lt;/p></content><category scheme="https://mademistakes.com/tag/entertainment" term="entertainment" label="entertainment"/><category scheme="https://mademistakes.com/tag/life" term="life" label="life"/><category scheme="https://mademistakes.com/tag/open-source" term="open-source" label="open source"/><category scheme="https://mademistakes.com/tag/static-sites" term="static-sites" label="static sites"/><category scheme="https://mademistakes.com/tag/web-development" term="web-development" label="web development"/></entry><entry><title type="html">Faster Netlify builds</title><link href="https://mademistakes.com/notes/faster-netlify-builds/?utm_source=atom_feed" rel="alternate" type="text/html"/><id>https://mademistakes.com/notes/faster-netlify-builds/</id><published>2019-02-26T00:00:00-05:00</published><updated>2021-04-29T13:35:10-04:00</updated><summary type="html">Discover how I optimized my Jekyll site’s build time on Netlify with practical tips to streamline static site performance and speed up deployments.</summary><content type="html">&lt;p>Slowly but surely, I&amp;rsquo;ve been chipping away at my site&amp;rsquo;s &lt;a href="/notes/autumn-refresh/#build-and-deploy">build time on &lt;strong>Netlify&lt;/strong>&lt;/a>.&lt;/p>
&lt;p>There&amp;rsquo;s little left for me to optimize until Jekyll drops some nice updates in &lt;a href="https://github.com/jekyll/jekyll/projects/2" rel="noopener">version 4.0&lt;/a>. I&amp;rsquo;ve &lt;a href="https://github.com/mmistakes/made-mistakes-jekyll/issues/629" rel="noopener">cached the rendering of Liquid includes&lt;/a> across &lt;code>_layouts&lt;/code> via the &lt;a href="https://github.com/benbalter/jekyll-include-cache" rel="noopener">&lt;code>{% include_cached %}&lt;/code>&lt;/a> tag, limited use of &lt;code>{% for %}&lt;/code> loops over large collections like &lt;code>{% site.posts %}&lt;/code>, and &lt;a href="/articles/using-jekyll-2017/#optimization">stripped Jekyll&amp;rsquo;s duties down&lt;/a> to solely a HTML generator.&lt;/p>
&lt;p>The &amp;ldquo;little&amp;rdquo; that remains, is about a gigabyte of images I pipe through &lt;strong>Gulp&lt;/strong>. Thousands of high resolution images are processed by &lt;a href="https://github.com/lovell/sharp" rel="noopener">&lt;strong>Sharp&lt;/strong>&lt;/a> into various sizes. I&amp;rsquo;ve been able to knock this down from 18 minutes, to six on a fresh build&amp;hellip;&lt;/p>
&lt;div class="browser-frame">&lt;img alt="Screenshot of Netlifys deploy log for Made Mistakes." class="img--md" decoding="async" fetchpriority="auto" height="183" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/netlify-deploy-log_hu14507604561895696040.png" style="background: #0e1f25;" width="300"
&lt;pre>&lt;code> srcset=&amp;quot;https://mademistakes.com/images/netlify-deploy-log_hu14507604561895696040.png 300w, https://mademistakes.com/images/netlify-deploy-log_hu12734368324671917220.png 620w&amp;quot;
&amp;gt;
&lt;/code>&lt;/pre>
&lt;/div>
&lt;p>When building the site locally I store the image artifacts in a temporary folder, and generate new ones if the source changes. When Netlify generates the site it processes these images each build &amp;mdash; regardless if they have changed or not.&lt;/p>
&lt;p>By stashing the processed images in a &lt;em>secret&lt;/em> Netlify cache folder&lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup> and using &lt;a href="https://github.com/mmistakes/made-mistakes-jekyll/tree/master/gulp" rel="noopener">Gulp to move files&lt;/a> around, I cut the build time in half. Which is fantastic since I&amp;rsquo;m now averaging 5&amp;ndash;8 minutes for dependencies to install, Jekyll to run, and Netlify to deploy the site.&lt;/p>
&lt;p>&lt;strong>Excerpt from my Gulp build task&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// &amp;#39;gulp copy:images:cached&amp;#39; -- copies cached images from Netlify&amp;#39;s `/opt/build/cache/` folder to `/dist/`
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">gulp&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">task&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;copy:images:cached&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">gulp&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">src&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">paths&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">imageFilesCachePath&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="s2">&amp;#34;/**/*&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">pipe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">newer&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">paths&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">imageFilesSite&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">pipe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">gulp&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">dest&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">paths&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">imageFilesSite&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="footnotes" role="doc-endnotes">
&lt;hr>
&lt;ol>
&lt;li id="fn:1">
&lt;p>Netlify has an undocumented &lt;code>/opt/build/cache/&lt;/code> folder that is cached and persists between builds. &lt;a href="https://www.contentful.com/blog/2018/05/17/faster-static-site-builds-part-one-process-only-what-you-need/#caching-for-the-win" rel="noopener">Faster static site builds Part 1&lt;/a>&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;/div>&lt;p>&lt;a href="https://mademistakes.com/notes/faster-netlify-builds/?utm_source=atom_feed">Faster Netlify builds&lt;/a> was originally published on Made Mistakes.&lt;/p></content></entry><entry><title type="html">Autumn refresh</title><link href="https://mademistakes.com/notes/autumn-refresh/?utm_source=atom_feed" rel="alternate" type="text/html"/><id>https://mademistakes.com/notes/autumn-refresh/</id><published>2018-11-12T00:00:00-05:00</published><updated>2022-12-02T10:15:45-05:00</updated><summary type="html">Using CSS Grid Layout Module to redesign a Jekyll site and further improve performance and accessibility.</summary><content type="html">
&lt;img alt="" decoding="async" fetchpriority="auto" height="165" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/autumn-refresh-feature_hu9443629564769537473.jpg" style="background: #232f2f;" width="300"
srcset="https://mademistakes.com/images/autumn-refresh-feature_hu9443629564769537473.jpg 300w,https://mademistakes.com/images/autumn-refresh-feature_hu18299219995911039084.jpg 800w, https://mademistakes.com/images/autumn-refresh-feature.jpg 1920w"
>&lt;p>Lately I&amp;rsquo;ve been toying with the idea of migrating from &lt;a href="https://jekyllrb.com/" rel="noopener">&lt;strong>Jekyll&lt;/strong>&lt;/a> to &lt;a href="https://www.gatsbyjs.com/" rel="noopener">&lt;strong>GatsbyJS&lt;/strong>&lt;/a> (more on that in a future post). Initial tests look promising, but there are &lt;a href="https://github.com/mmistakes/gatsby-test/issues/1" rel="noopener">some issues&lt;/a> I&amp;rsquo;m still working through.&lt;/p>
&lt;p>In the meantime I&amp;rsquo;ve taken visual cues from &lt;a href="https://reactjs.org/docs/react-component.html" rel="noopener">React components&lt;/a> I built in a repo where I was &lt;a href="https://github.com/mmistakes/gatsby-test" rel="noopener">playing around with Gatsby&lt;/a>, and adapted them here as standard HTML/CSS.&lt;/p>
&lt;h2 id="design-changes">&lt;a href="#design-changes">Design changes&lt;/a>&lt;/h2>
&lt;p>In &lt;a href="https://github.com/mmistakes/made-mistakes-jekyll/releases/tag/12.0.0" rel="noopener">Made Mistakes v12&lt;/a> text and image where large and readible, but didn&amp;rsquo;t exactly fill the canvas elegantly. I wanted to change that with this design refresh.&lt;/p>
&lt;figure class="figure">
&lt;div class="browser-frame">&lt;p>
&lt;img alt="Screenshot of mademistakes.coms design before the refresh." class="img--md" decoding="async" fetchpriority="auto" height="344" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/mm-desktop-design-old_hu10846213963406749627.jpg" style="background: #e0e0e4;" width="300"
&lt;pre>&lt;code> srcset=&amp;quot;https://mademistakes.com/images/mm-desktop-design-old_hu10846213963406749627.jpg 300w, https://mademistakes.com/images/mm-desktop-design-old_hu8043782310772017601.jpg 620w&amp;quot;
&amp;gt;&amp;lt;/p&amp;gt;
&lt;/code>&lt;/pre>
&lt;/div>
&lt;figcaption>Site design before the refresh.&lt;/figcaption>
&lt;/figure>
&lt;p>Since majority of the visitors to my site use modern browsers, I had a good excuse to play with &lt;code>display: grid&lt;/code>. I&amp;rsquo;ve removed the &lt;a href="http://oddbird.net/susy/" rel="noopener">&lt;strong>Susy&lt;/strong> mixins&lt;/a> and most of the &lt;code>float&lt;/code> based columns, which cut the amount of CSS I had to write considerably.&lt;/p>
&lt;figure class="figure">
&lt;div class="browser-frame">&lt;p>
&lt;img alt="Screenshot of mademistakes.coms design after the refresh." class="img--md" decoding="async" fetchpriority="auto" height="204" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/mm-desktop-design-new_hu131218572327163915.jpg" style="background: #ebecee;" width="300"
&lt;pre>&lt;code> srcset=&amp;quot;https://mademistakes.com/images/mm-desktop-design-new_hu131218572327163915.jpg 300w, https://mademistakes.com/images/mm-desktop-design-new_hu9270831330690345807.jpg 620w&amp;quot;
&amp;gt;&amp;lt;/p&amp;gt;
&lt;/code>&lt;/pre>
&lt;/div>
&lt;figcaption>Site design after the refresh.&lt;/figcaption>
&lt;/figure>
&lt;p>To my eye this new layout breaks up the page better. Content comes into view earlier on the page, and there&amp;rsquo;s plenty of room for ancillary information on the right. With a little &lt;code>position: sticky&lt;/code> sprinkled on the &lt;code>aside&lt;/code> column, internal skip links stay fixed in view &amp;mdash; when space allows.&lt;/p>
&lt;aside class="notice">&lt;h3>Pro tip: Use the Firefox DevTools when working with CSS grid layouts&lt;/h3>&lt;p>
&lt;img alt="Firefox Grid Inspector screenshot" class="img--md" decoding="async" fetchpriority="auto" height="185" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/firefox-grid-inspector-screenshot_hu5358388887493757617.jpg" style="background: #f4f4f5;" width="300"
&lt;pre>&lt;code> srcset=&amp;quot;https://mademistakes.com/images/firefox-grid-inspector-screenshot_hu5358388887493757617.jpg 300w, https://mademistakes.com/images/firefox-grid-inspector-screenshot_hu3370023357035096720.jpg 620w&amp;quot;
&amp;gt;&amp;lt;/p&amp;gt;
&lt;/code>&lt;/pre>
&lt;p>Out of all the major browsers, Firefox&amp;rsquo;s &lt;a href="https://developer.mozilla.org/en-US/docs/Tools/Page_Inspector/How_to/Examine_grid_layouts" rel="noopener">&lt;strong>Grid Inspector&lt;/strong>&lt;/a> is arguably the best for examining, modifying, or debugging grid layouts.&lt;/p>
&lt;/aside>
&lt;h2 id="accessibility-improvements">&lt;a href="#accessibility-improvements">Accessibility improvements&lt;/a>&lt;/h2>
&lt;p>This refresh prompted me to test how accessible the site is and fix any glaring issues.&lt;/p>
&lt;h3 id="buttons-have-an-accessible-name">&lt;a href="#buttons-have-an-accessible-name">Buttons have an accessible name&lt;/a>&lt;/h3>
&lt;blockquote>
&lt;p>When a button doesn&amp;rsquo;t have an accessible name, screen readers announce it as &amp;ldquo;button&amp;rdquo;, making it unusable for users who rely on screen readers.&lt;/p>
&lt;/blockquote>
&lt;p>Digging in, I discovered &lt;a href="http://bigfootjs.com/" rel="noopener">&lt;strong>bigfoot.js&lt;/strong>&lt;/a>&lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup> didn&amp;rsquo;t name &lt;code>&amp;lt;button&amp;gt;&lt;/code> elements that it created. Thankfully the button markup is configurable so I was able replace a set of presentational-only &lt;code>&amp;lt;svg&amp;gt;&lt;/code> elements with unique names instead.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">var&lt;/span> &lt;span class="nx">bigfoot&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">$&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">bigfoot&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">actionOriginalFN&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;delete&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">buttonMarkup&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;&amp;lt;div class=&amp;#34;bigfoot-footnote__container&amp;#34;&amp;gt;&amp;#39;&lt;/span> &lt;span class="o">+&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39; &amp;lt;button href=&amp;#34;#&amp;#34; class=&amp;#34;bigfoot-footnote__button&amp;#34; rel=&amp;#34;footnote&amp;#34;&amp;#39;&lt;/span> &lt;span class="o">+&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39; id=&amp;#34;{{SUP:data-footnote-backlink-ref}}&amp;#34;&amp;#39;&lt;/span> &lt;span class="o">+&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39; data-footnote-number=&amp;#34;{{FOOTNOTENUM}}&amp;#34;&amp;#39;&lt;/span> &lt;span class="o">+&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39; data-footnote-identifier=&amp;#34;{{FOOTNOTEID}}&amp;#34;&amp;#39;&lt;/span> &lt;span class="o">+&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39; alt=&amp;#34;See Footnote {{FOOTNOTENUM}}&amp;#34;&amp;#39;&lt;/span> &lt;span class="o">+&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39; data-bigfoot-footnote=&amp;#34;{{FOOTNOTECONTENT}}&amp;#34;&amp;gt;&amp;#39;&lt;/span> &lt;span class="o">+&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39; &amp;lt;span class=&amp;#34;visually-hidden&amp;#34;&amp;gt;{{FOOTNOTENUM}}&amp;lt;/span&amp;gt;&amp;#39;&lt;/span> &lt;span class="o">+&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39; &amp;lt;/button&amp;gt;&amp;#39;&lt;/span> &lt;span class="o">+&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;&amp;lt;/div&amp;gt;&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="color-contrast-is-satisfactory">&lt;a href="#color-contrast-is-satisfactory">Color contrast is satisfactory&lt;/a>&lt;/h3>
&lt;p>The color of elements like captions and footer text were too light, so I darkened them. I did the same for links by giving them a contrast ratio of &lt;code>4.77&lt;/code>.&lt;/p>
&lt;p>
&lt;img alt="link color contrast ratio screenshot" class="img--md" decoding="async" fetchpriority="auto" height="199" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/mm-link-color-contrast-raio_hu4303712217067830.png" style="background: #dddddd;" width="300"
srcset="https://mademistakes.com/images/mm-link-color-contrast-raio_hu4303712217067830.png 300w, https://mademistakes.com/images/mm-link-color-contrast-raio_hu873347517854538263.png 620w"
>&lt;/p>
&lt;p>The Color Picker in Chrome&amp;rsquo;s DevTools will show you the contrast ratio of text elements to help make your site more accessible to users with low-vision impairments or color-vision deficiencies.&lt;/p>
&lt;h2 id="performance-improvements">&lt;a href="#performance-improvements">Performance improvements&lt;/a>&lt;/h2>
&lt;p>Optimizations with the biggest impact (minifying, concatenating, inlining critical CSS) I was already doing, but there was still room for improvement.&lt;/p>
&lt;h3 id="defer-offscreen-images">&lt;a href="#defer-offscreen-images">Defer offscreen images&lt;/a>&lt;/h3>
&lt;blockquote>
&lt;p>Consider lazy-loading offscreen and hidden images after all critical resources have finished loading to lower time to interactive.&lt;/p>
&lt;/blockquote>
&lt;p>Large feature images were already lazy-loaded and served responsively using &lt;code>srcset&lt;/code> and a handful of sized images. Images found in the body &lt;code>{{ content }}&lt;/code> of my Markdown files were not.&lt;/p>
&lt;p>Taking a cue from &lt;a href="https://github.com/gatsbyjs/gatsby/tree/master/packages/gatsby-remark-images" rel="noopener">&lt;strong>gatsby-remark-images&amp;rsquo;&lt;/strong>&lt;/a> playbook, I wrote a small plugin to convert Markdown image syntax into an &lt;code>&amp;lt;img&amp;gt;&lt;/code> element with synatactically sugar for lazy-loading. To my surprise this actually worked.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-ruby" data-lang="ruby">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Description: Jekyll plugin to replace Markdown image syntax with lazy-load HTML markup&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="no">Jekyll&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="no">Hooks&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">register&lt;/span> &lt;span class="ss">:posts&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="ss">:pre_render&lt;/span> &lt;span class="k">do&lt;/span> &lt;span class="o">|&lt;/span>&lt;span class="n">post&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">payload&lt;/span>&lt;span class="o">|&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">docExt&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">post&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">extname&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">tr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;.&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># only process Markdown files&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">payload&lt;/span>&lt;span class="o">[&lt;/span>&lt;span class="s1">&amp;#39;site&amp;#39;&lt;/span>&lt;span class="o">][&lt;/span>&lt;span class="s1">&amp;#39;markdown_ext&amp;#39;&lt;/span>&lt;span class="o">].&lt;/span>&lt;span class="n">include?&lt;/span> &lt;span class="n">docExt&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">newContent&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">post&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">content&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">gsub&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sr">/(?:!\[(.*?)\]\((.*?)\))/&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;&amp;lt;noscript&amp;gt;&amp;lt;img src=&amp;#34;\2&amp;#34;&amp;gt;&amp;lt;/noscript&amp;gt;&amp;lt;img src=&amp;#34;data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==&amp;#34; data-src=&amp;#34;\2&amp;#34; alt=&amp;#34;\1&amp;#34; class=&amp;#34;lazyload fade-in&amp;#34;&amp;gt;&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">post&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">content&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">newContent&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">end&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">end&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="avoids-an-excessive-dom-size">&lt;a href="#avoids-an-excessive-dom-size">Avoids an excessive DOM size&lt;/a>&lt;/h3>
&lt;blockquote>
&lt;p>Browser engineers recommend pages contain fewer than ~1,500 DOM nodes. The sweet spot is a tree depth &amp;lt; 32 elements and fewer than 60 children/parent element. A large DOM can increase memory usage, cause longer style calculations, and produce costly layout reflows.&lt;/p>
&lt;/blockquote>
&lt;p>Trimming &lt;code>&amp;lt;div&amp;gt;&lt;/code> fat where I could helped cut page weight down. Some hefty pages remain (I&amp;rsquo;m looking at your &lt;a href="/paperfaces/">PaperFaces gallery&lt;/a>, but most fall under the 1,500 DOM node threshold. In the future I hope to fix this issue with a &lt;a href="https://awesome-lewin-0d1356.netlify.com/grid-example/" rel="noopener">gallery pagination component&lt;/a> when switching to Gatsby.&lt;/p>
&lt;h2 id="lighthouse-audits-comparison">&lt;a href="#lighthouse-audits-comparison">Lighthouse audits comparison&lt;/a>&lt;/h2>
&lt;figure class="figure">
&lt;p>
&lt;img alt="mademistakes.coms audit results before applying optimizations." class="img--md" decoding="async" fetchpriority="auto" height="133" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/mm-lighthouse-audit-before_hu6895789052545736028.png" style="background: #fdfdfd;" width="300"
srcset="https://mademistakes.com/images/mm-lighthouse-audit-before_hu6895789052545736028.png 300w, https://mademistakes.com/images/mm-lighthouse-audit-before_hu1079498020572905694.png 620w"
>&lt;/p>
&lt;figcaption>&lt;strong>Lighthouse&lt;/strong> audit results before optimizations.&lt;/figcaption>
&lt;/figure>
&lt;figure class="figure">
&lt;p>
&lt;img alt="mademistakes.coms audit results after applying optimizations." class="img--md" decoding="async" fetchpriority="auto" height="132" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/mm-lighthouse-audit-after_hu1867304533317741922.png" style="background: #fdfdfd;" width="300"
srcset="https://mademistakes.com/images/mm-lighthouse-audit-after_hu1867304533317741922.png 300w, https://mademistakes.com/images/mm-lighthouse-audit-after_hu1968218641353261166.png 620w"
>&lt;/p>
&lt;figcaption>&lt;strong>Lighthouse&lt;/strong> audit results after optimizations.&lt;/figcaption>
&lt;/figure>
&lt;p>Not sure why metrics like &lt;em>Speed Index&lt;/em> increased. But &lt;em>Time to Interactive&lt;/em>, &lt;em>First Meaningful Paint&lt;/em>, and &lt;em>First CPI Idle&lt;/em> all show improvements, so I guess that&amp;rsquo;s why the site went from a 79 in performance, to an 89.&lt;/p>
&lt;h2 id="build-and-deploy">&lt;a href="#build-and-deploy">Build and deploy&lt;/a>&lt;/h2>
&lt;p>And if all of this wasn&amp;rsquo;t enough, I also made the switch away from &lt;a href="https://www.travis-ci.com/" rel="noopener">&lt;strong>Travis CI&lt;/strong>&lt;/a>, to &lt;a href="https://www.netlify.com/" rel="noopener">&lt;strong>Netlify&lt;/strong>&lt;/a>. Instead of a 48 line &lt;code>.travis.yml&lt;/code> file I now have a three line &lt;code>netlify.toml&lt;/code>.&lt;/p>
&lt;p>The Netlify builds are about 2-4 minutes faster than those with Travis CI. And look to be even quicker if I leverage undocumented &lt;a href="https://www.contentful.com/blog/2018/05/17/faster-static-site-builds-part-one-process-only-what-you-need/#caching-for-the-win" rel="noopener">Netlify cache folders&lt;/a> that persists between builds. Using these to store the thousands of images I pipe through resizing tasks could shave another 6-8 minutes off my build.&lt;/p>
&lt;div class="footnotes" role="doc-endnotes">
&lt;hr>
&lt;ol>
&lt;li id="fn:1">
&lt;p>A jQuery plugin used to make footnotes less visually distracting.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;/div>&lt;p>&lt;a href="https://mademistakes.com/notes/autumn-refresh/?utm_source=atom_feed">Autumn refresh&lt;/a> was originally published on Made Mistakes.&lt;/p></content><category scheme="https://mademistakes.com/tag/static-sites" term="static-sites" label="static sites"/><category scheme="https://mademistakes.com/tag/web-development" term="web-development" label="web development"/></entry><entry><title type="html">Water tension</title><link href="https://mademistakes.com/work/procreate-paintings/marie-m-portrait/?utm_source=atom_feed" rel="alternate" type="text/html"/><id>https://mademistakes.com/work/procreate-paintings/marie-m-portrait/</id><published>2018-06-04T00:00:00-04:00</published><updated>2018-06-04T00:00:00-04:00</updated><summary type="html">Portrait of Marie M. digitally painted with Procreate on an iPad.</summary><content type="html">
&lt;img alt="" decoding="async" fetchpriority="auto" height="169" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/marie-m-procreate_hu9365299765344966115.jpg" style="background: #333333;" width="300"
srcset="https://mademistakes.com/images/marie-m-procreate_hu9365299765344966115.jpg 300w,https://mademistakes.com/images/marie-m-procreate_hu3013696237765657304.jpg 800w, https://mademistakes.com/images/marie-m-procreate.jpg 1600w"
>&lt;p>Portrait of Marie M. inspired by a &lt;a href="https://sktchy.com/FVAa3D" rel="noopener">photograph on Sktchy&lt;/a>. Digitally painted with Procreate on a 12.9″ iPad Pro.&lt;/p>
&lt;p>Approximately 6 hours of work spread across a couple of weeks.&lt;/p>
&lt;blockquote class="twitter-tweet" data-dnt="true">&lt;p lang="en" dir="ltr">~6 hours of work spread across a couple of weeks, because who has time? &lt;a href="https://t.co/faECZVYPSP">pic.twitter.com/faECZVYPSP&lt;/a>&lt;/p>&amp;mdash; 𝔐𝔦𝔠𝔥𝔞𝔢𝔩 ℜ𝔬𝔰𝔢 (@mmistakes) &lt;a href="https://twitter.com/mmistakes/status/1003311926024294402?ref_src=twsrc%5Etfw">June 3, 2018&lt;/a>&lt;/blockquote>
&lt;script async src="https://platform.twitter.com/widgets.js" charset="utf-8">&lt;/script>
&lt;p>&lt;strong>Tools used:&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://www.apple.com/apple-pencil/" rel="noopener">Apple Pencil&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.apple.com/ipad-pro/" rel="noopener">iPad Pro 12.9″ (2nd generation)&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://procreate.art/" rel="noopener">Procreate&lt;/a>&lt;/li>
&lt;/ul>&lt;p>&lt;a href="https://mademistakes.com/work/procreate-paintings/marie-m-portrait/?utm_source=atom_feed">Water tension&lt;/a> was originally published on Made Mistakes.&lt;/p></content><category scheme="https://mademistakes.com/tag/procreate" term="procreate" label="Procreate"/><category scheme="https://mademistakes.com/tag/portrait" term="portrait" label="portrait"/><category scheme="https://mademistakes.com/tag/sktchy" term="sktchy" label="Sktchy"/><category scheme="https://mademistakes.com/tag/black-and-white" term="black-and-white" label="black and white"/><category scheme="https://mademistakes.com/tag/time-lapse" term="time-lapse" label="time-lapse"/></entry><entry><title type="html">The two hour beard</title><link href="https://mademistakes.com/work/procreate-paintings/ale-b-portrait/?utm_source=atom_feed" rel="alternate" type="text/html"/><id>https://mademistakes.com/work/procreate-paintings/ale-b-portrait/</id><published>2018-04-30T00:00:00-04:00</published><updated>2018-04-30T16:22:39-04:00</updated><summary type="html">Portrait of Ale B. digitally painted with Procreate on an iPad.</summary><content type="html">
&lt;img alt="" decoding="async" fetchpriority="auto" height="400" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/ale-b-procreate_hu879172586197716156.jpg" style="background: #2a2a2a;" width="300"
srcset="https://mademistakes.com/images/ale-b-procreate_hu879172586197716156.jpg 300w,https://mademistakes.com/images/ale-b-procreate_hu1764108550516667394.jpg 800w, https://mademistakes.com/images/ale-b-procreate.jpg 1600w"
>&lt;p>Portrait of Ale B. inspired by a &lt;a href="https://sktchy.com/pGkOfH" rel="noopener">photograph on Sktchy&lt;/a>. Digitally painted with Procreate on a 12.9&amp;quot; iPad Pro.&lt;/p>
&lt;p>Two hours and 30 minutes of beard scribbling over the last couple of weeks. Because who has time to relax draw in a single sitting?&lt;/p>
&lt;p>
&lt;img alt="work in progress screenshot" class="img--md" decoding="async" fetchpriority="auto" height="207" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/ale-b-progress-1-lg_hu1888304225249182454.jpg" style="background: #e4e0dc;" width="300"
srcset="https://mademistakes.com/images/ale-b-progress-1-lg_hu1888304225249182454.jpg 300w, https://mademistakes.com/images/ale-b-progress-1-lg_hu2588322928956185867.jpg 620w"
>&lt;/p>
&lt;blockquote class="twitter-tweet" data-dnt="true">&lt;p lang="en" dir="ltr">Time-lapse export video via &lt;a href="https://twitter.com/hashtag/Procreate?src=hash&amp;amp;ref_src=twsrc%5Etfw">#Procreate&lt;/a> &lt;a href="https://twitter.com/hashtag/WIP?src=hash&amp;amp;ref_src=twsrc%5Etfw">#WIP&lt;/a> &lt;a href="https://twitter.com/hashtag/Sktchy?src=hash&amp;amp;ref_src=twsrc%5Etfw">#Sktchy&lt;/a> &lt;a href="https://t.co/uqMtzknWYp">pic.twitter.com/uqMtzknWYp&lt;/a>&lt;/p>&amp;mdash; 𝔐𝔦𝔠𝔥𝔞𝔢𝔩 ℜ𝔬𝔰𝔢 (@mmistakes) &lt;a href="https://twitter.com/mmistakes/status/990746870380195840?ref_src=twsrc%5Etfw">April 30, 2018&lt;/a>&lt;/blockquote>
&lt;script async src="https://platform.twitter.com/widgets.js" charset="utf-8">&lt;/script>
&lt;p>&lt;strong>Tools used:&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://www.apple.com/apple-pencil/" rel="noopener">Apple Pencil&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.apple.com/ipad-pro/" rel="noopener">iPad Pro 12.9&amp;quot; (2nd generation)&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://procreate.art/" rel="noopener">Procreate&lt;/a>&lt;/li>
&lt;/ul>&lt;p>&lt;a href="https://mademistakes.com/work/procreate-paintings/ale-b-portrait/?utm_source=atom_feed">The two hour beard&lt;/a> was originally published on Made Mistakes.&lt;/p></content><category scheme="https://mademistakes.com/tag/procreate" term="procreate" label="Procreate"/><category scheme="https://mademistakes.com/tag/portrait" term="portrait" label="portrait"/><category scheme="https://mademistakes.com/tag/sktchy" term="sktchy" label="Sktchy"/><category scheme="https://mademistakes.com/tag/black-and-white" term="black-and-white" label="black and white"/><category scheme="https://mademistakes.com/tag/beard" term="beard" label="beard"/><category scheme="https://mademistakes.com/tag/time-lapse" term="time-lapse" label="time-lapse"/></entry><entry><title type="html">Turn away</title><link href="https://mademistakes.com/work/procreate-paintings/tatha-s-portrait/?utm_source=atom_feed" rel="alternate" type="text/html"/><id>https://mademistakes.com/work/procreate-paintings/tatha-s-portrait/</id><published>2018-04-14T00:00:00-04:00</published><updated>2018-04-14T14:27:36-04:00</updated><summary type="html">Portrait of Tatha S. digitally painted with Procreate on an iPad.</summary><content type="html">
&lt;img alt="" decoding="async" fetchpriority="auto" height="400" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/tatha-s-procreate_hu2507019731056260927.jpg" style="background: #3b3b3b;" width="300"
srcset="https://mademistakes.com/images/tatha-s-procreate_hu2507019731056260927.jpg 300w,https://mademistakes.com/images/tatha-s-procreate_hu5890556235895604023.jpg 800w, https://mademistakes.com/images/tatha-s-procreate.jpg 1600w"
>&lt;p>Portrait of Tatha S. inspired by a &lt;a href="https://sktchy.com/Eoee2D" rel="noopener">photograph on Sktchy&lt;/a>. Digitally painted with Procreate on a 12.9″ iPad Pro.&lt;/p>
&lt;p>
&lt;img alt="work in progress screenshot" class="img--md" decoding="async" fetchpriority="auto" height="400" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/tatha-s-progress-1-lg_hu7829788630468649983.jpg" style="background: #d9d8d5;" width="300"
srcset="https://mademistakes.com/images/tatha-s-progress-1-lg_hu7829788630468649983.jpg 300w, https://mademistakes.com/images/tatha-s-progress-1-lg_hu4597889162497062652.jpg 620w"
>&lt;/p>
&lt;p>Using Apple Pencil to lay lines.&lt;/p>
&lt;p>
&lt;img alt="work in progress screenshot" class="img--md" decoding="async" fetchpriority="auto" height="225" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/tatha-s-progress-2-lg_hu7672346566460892391.jpg" style="background: #b5b5b5;" width="300"
srcset="https://mademistakes.com/images/tatha-s-progress-2-lg_hu7672346566460892391.jpg 300w, https://mademistakes.com/images/tatha-s-progress-2-lg_hu10995006472839700893.jpg 620w"
>&lt;/p>
&lt;blockquote class="twitter-tweet" data-dnt="true">&lt;p lang="en" dir="ltr">Procreate time-lapse export &lt;a href="https://t.co/Xw5ozxENYI">pic.twitter.com/Xw5ozxENYI&lt;/a>&lt;/p>&amp;mdash; 𝔐𝔦𝔠𝔥𝔞𝔢𝔩 ℜ𝔬𝔰𝔢 (@mmistakes) &lt;a href="https://twitter.com/mmistakes/status/984246043511607297?ref_src=twsrc%5Etfw">April 12, 2018&lt;/a>&lt;/blockquote>
&lt;script async src="https://platform.twitter.com/widgets.js" charset="utf-8">&lt;/script>
&lt;p>&lt;strong>Tools used:&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://www.apple.com/apple-pencil/" rel="noopener">Apple Pencil&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.apple.com/ipad-pro/" rel="noopener">iPad Pro 12.9″ (2nd generation)&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://procreate.art/" rel="noopener">Procreate&lt;/a>&lt;/li>
&lt;/ul>&lt;p>&lt;a href="https://mademistakes.com/work/procreate-paintings/tatha-s-portrait/?utm_source=atom_feed">Turn away&lt;/a> was originally published on Made Mistakes.&lt;/p></content><category scheme="https://mademistakes.com/tag/procreate" term="procreate" label="Procreate"/><category scheme="https://mademistakes.com/tag/portrait" term="portrait" label="portrait"/><category scheme="https://mademistakes.com/tag/sktchy" term="sktchy" label="Sktchy"/><category scheme="https://mademistakes.com/tag/black-and-white" term="black-and-white" label="black and white"/><category scheme="https://mademistakes.com/tag/time-lapse" term="time-lapse" label="time-lapse"/></entry><entry><title type="html">Corpse paint your tears</title><link href="https://mademistakes.com/work/procreate-paintings/maya-z-portrait/?utm_source=atom_feed" rel="alternate" type="text/html"/><id>https://mademistakes.com/work/procreate-paintings/maya-z-portrait/</id><published>2018-04-03T00:00:00-04:00</published><updated>2018-04-03T00:00:00-04:00</updated><summary type="html">Portrait of Maya Z. digitally painted with Procreate on an iPad.</summary><content type="html">
&lt;img alt="" decoding="async" fetchpriority="auto" height="400" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/maya-z-procreate_hu10848137502254138013.jpg" style="background: #373737;" width="300"
srcset="https://mademistakes.com/images/maya-z-procreate_hu10848137502254138013.jpg 300w,https://mademistakes.com/images/maya-z-procreate_hu10418383999387583826.jpg 800w, https://mademistakes.com/images/maya-z-procreate.jpg 1600w"
>&lt;p>Portrait of Maya Z. inspired by a &lt;a href="https://sktchy.com/efNbvC" rel="noopener">photograph on Sktchy&lt;/a>. Digitally painted with Procreate on a 12.9″ iPad Pro.&lt;/p>
&lt;p>
&lt;img alt="work in progress screenshot" class="img--md" decoding="async" fetchpriority="auto" height="225" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/maya-z-progress-1-lg_hu11072475188994797606.jpg" style="background: #484848;" width="300"
srcset="https://mademistakes.com/images/maya-z-progress-1-lg_hu11072475188994797606.jpg 300w, https://mademistakes.com/images/maya-z-progress-1-lg_hu14735766688497849261.jpg 620w"
>&lt;/p>
&lt;p>Slowly building up tones.&lt;/p>
&lt;p>
&lt;img alt="work in progress screenshot" class="img--md" decoding="async" fetchpriority="auto" height="225" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/maya-z-progress-2-lg_hu10502527171824048213.jpg" style="background: #292929;" width="300"
srcset="https://mademistakes.com/images/maya-z-progress-2-lg_hu10502527171824048213.jpg 300w, https://mademistakes.com/images/maya-z-progress-2-lg_hu8301740280585736009.jpg 620w"
>&lt;/p>
&lt;blockquote class="twitter-tweet" data-dnt="true">&lt;p lang="en" dir="ltr">&lt;a href="https://twitter.com/hashtag/Procreate?src=hash&amp;amp;ref_src=twsrc%5Etfw">#Procreate&lt;/a> time-lapse export &lt;a href="https://t.co/dSNE7fKHWb">pic.twitter.com/dSNE7fKHWb&lt;/a>&lt;/p>&amp;mdash; 𝔐𝔦𝔠𝔥𝔞𝔢𝔩 ℜ𝔬𝔰𝔢 (@mmistakes) &lt;a href="https://twitter.com/mmistakes/status/980117629473083394?ref_src=twsrc%5Etfw">March 31, 2018&lt;/a>&lt;/blockquote>
&lt;script async src="https://platform.twitter.com/widgets.js" charset="utf-8">&lt;/script>
&lt;p>&lt;strong>Tools used:&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://www.apple.com/apple-pencil/" rel="noopener">Apple Pencil&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.apple.com/ipad-pro/" rel="noopener">iPad Pro 12.9″ (2nd generation)&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://procreate.art/" rel="noopener">Procreate&lt;/a>&lt;/li>
&lt;/ul>&lt;p>&lt;a href="https://mademistakes.com/work/procreate-paintings/maya-z-portrait/?utm_source=atom_feed">Corpse paint your tears&lt;/a> was originally published on Made Mistakes.&lt;/p></content><category scheme="https://mademistakes.com/tag/procreate" term="procreate" label="Procreate"/><category scheme="https://mademistakes.com/tag/portrait" term="portrait" label="portrait"/><category scheme="https://mademistakes.com/tag/sktchy" term="sktchy" label="Sktchy"/><category scheme="https://mademistakes.com/tag/black-and-white" term="black-and-white" label="black and white"/><category scheme="https://mademistakes.com/tag/time-lapse" term="time-lapse" label="time-lapse"/></entry><entry><title type="html">Glowing pencil practice</title><link href="https://mademistakes.com/work/procreate-paintings/foggy-365-portrait/?utm_source=atom_feed" rel="alternate" type="text/html"/><id>https://mademistakes.com/work/procreate-paintings/foggy-365-portrait/</id><published>2018-04-03T00:00:00-04:00</published><updated>2018-04-03T00:00:00-04:00</updated><summary type="html">Portrait of Foggy 365 digitally painted with Procreate on an iPad.</summary><content type="html">
&lt;img alt="" decoding="async" fetchpriority="auto" height="400" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/foggy-365-procreate_hu7548994878279703287.jpg" style="background: #a4a4a4;" width="300"
srcset="https://mademistakes.com/images/foggy-365-procreate_hu7548994878279703287.jpg 300w,https://mademistakes.com/images/foggy-365-procreate_hu2058408351566134730.jpg 800w, https://mademistakes.com/images/foggy-365-procreate.jpg 1600w"
>&lt;p>Portrait of Foggy 365 inspired by a &lt;a href="https://sktchy.com/QFzi9D" rel="noopener">photograph on Sktchy&lt;/a>. Digitally painted with Procreate on a 12.9″ iPad Pro.&lt;/p>
&lt;p>Trying to emulate the look of graphite on paper&amp;hellip;&lt;/p>
&lt;blockquote class="twitter-tweet" data-dnt="true">&lt;p lang="en" dir="ltr">Time-lapse replay exported from &lt;a href="https://twitter.com/Procreate?ref_src=twsrc%5Etfw">@Procreate&lt;/a> &lt;a href="https://t.co/Ww2m40qdoI">pic.twitter.com/Ww2m40qdoI&lt;/a>&lt;/p>&amp;mdash; 𝔐𝔦𝔠𝔥𝔞𝔢𝔩 ℜ𝔬𝔰𝔢 (@mmistakes) &lt;a href="https://twitter.com/mmistakes/status/977574457278640128?ref_src=twsrc%5Etfw">March 24, 2018&lt;/a>&lt;/blockquote>
&lt;script async src="https://platform.twitter.com/widgets.js" charset="utf-8">&lt;/script>
&lt;p>&lt;strong>Tools used:&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://www.apple.com/apple-pencil/" rel="noopener">Apple Pencil&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.apple.com/ipad-pro/" rel="noopener">iPad Pro 12.9″ (2nd generation)&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://procreate.art/" rel="noopener">Procreate&lt;/a>&lt;/li>
&lt;/ul>&lt;p>&lt;a href="https://mademistakes.com/work/procreate-paintings/foggy-365-portrait/?utm_source=atom_feed">Glowing pencil practice&lt;/a> was originally published on Made Mistakes.&lt;/p></content><category scheme="https://mademistakes.com/tag/procreate" term="procreate" label="Procreate"/><category scheme="https://mademistakes.com/tag/portrait" term="portrait" label="portrait"/><category scheme="https://mademistakes.com/tag/sktchy" term="sktchy" label="Sktchy"/><category scheme="https://mademistakes.com/tag/black-and-white" term="black-and-white" label="black and white"/><category scheme="https://mademistakes.com/tag/time-lapse" term="time-lapse" label="time-lapse"/></entry><entry><title type="html">Back at it</title><link href="https://mademistakes.com/work/procreate-paintings/marion-k-portrait/?utm_source=atom_feed" rel="alternate" type="text/html"/><id>https://mademistakes.com/work/procreate-paintings/marion-k-portrait/</id><published>2018-03-22T00:00:00-04:00</published><updated>2020-01-06T10:37:48-05:00</updated><summary type="html">Portrait of Marion K. digitally painted with Procreate on an iPad.</summary><content type="html">
&lt;img alt="" decoding="async" fetchpriority="auto" height="400" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/marion-k-procreate_hu3195319005539713905.jpg" style="background: #a3a3a3;" width="300"
srcset="https://mademistakes.com/images/marion-k-procreate_hu3195319005539713905.jpg 300w,https://mademistakes.com/images/marion-k-procreate_hu16971678313938348158.jpg 800w, https://mademistakes.com/images/marion-k-procreate.jpg 1600w"
>&lt;p>Portrait of Marion K. inspired by a &lt;a href="https://sktchy.com/p6juFc" rel="noopener">photograph on Sktchy&lt;/a>. Digitally painted with Procreate on a 12.9″ iPad Pro.&lt;/p>
&lt;p>I finally got my hands on a new iPad Pro and the highly responsive Apple Pencil. This is &lt;a href="/articles/ipad-pro/">the stylus I have been waiting for&lt;/a>!&lt;/p>
&lt;blockquote class="twitter-tweet" data-dnt="true">&lt;p lang="en" dir="ltr">Process video &lt;a href="https://t.co/MfP9TmESlR">pic.twitter.com/MfP9TmESlR&lt;/a>&lt;/p>&amp;mdash; 𝔐𝔦𝔠𝔥𝔞𝔢𝔩 ℜ𝔬𝔰𝔢 (@mmistakes) &lt;a href="https://twitter.com/mmistakes/status/976620815662960640?ref_src=twsrc%5Etfw">March 22, 2018&lt;/a>&lt;/blockquote>
&lt;script async src="https://platform.twitter.com/widgets.js" charset="utf-8">&lt;/script>
&lt;p>&lt;strong>Tools used:&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://www.apple.com/apple-pencil/" rel="noopener">Apple Pencil&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.apple.com/ipad-pro/" rel="noopener">iPad Pro 12.9″ (2nd generation)&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://procreate.art/" rel="noopener">Procreate&lt;/a>&lt;/li>
&lt;/ul>&lt;p>&lt;a href="https://mademistakes.com/work/procreate-paintings/marion-k-portrait/?utm_source=atom_feed">Back at it&lt;/a> was originally published on Made Mistakes.&lt;/p></content><category scheme="https://mademistakes.com/tag/procreate" term="procreate" label="Procreate"/><category scheme="https://mademistakes.com/tag/portrait" term="portrait" label="portrait"/><category scheme="https://mademistakes.com/tag/sktchy" term="sktchy" label="Sktchy"/><category scheme="https://mademistakes.com/tag/black-and-white" term="black-and-white" label="black and white"/><category scheme="https://mademistakes.com/tag/time-lapse" term="time-lapse" label="time-lapse"/></entry><entry><title type="html">Choke&amp;rsquo;s embrace</title><link href="https://mademistakes.com/work/procreate-paintings/lunar-ashes-portrait/?utm_source=atom_feed" rel="alternate" type="text/html"/><id>https://mademistakes.com/work/procreate-paintings/lunar-ashes-portrait/</id><published>2017-08-31T00:00:00-04:00</published><updated>2017-08-31T14:31:18-04:00</updated><summary type="html">Portrait of Lunar Ashes digitally painted with Procreate on an iPad.</summary><content type="html">
&lt;img alt="" decoding="async" fetchpriority="auto" height="400" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/lunar-ashes-procreate_hu10704555948980416669.jpg" style="background: #424242;" width="300"
srcset="https://mademistakes.com/images/lunar-ashes-procreate_hu10704555948980416669.jpg 300w,https://mademistakes.com/images/lunar-ashes-procreate_hu14685612475730418495.jpg 800w, https://mademistakes.com/images/lunar-ashes-procreate.jpg 1600w"
>&lt;p>Portrait of Lunar Ashes inspired by a &lt;a href="https://sktchy.com/pSkSa" rel="noopener">photograph on Sktchy&lt;/a>. Digitally painted with Procreate on an iPad Air 2.&lt;/p>
&lt;p>A lovers embrace painting for a Valentine&amp;rsquo;s Day inspired Sktchy challenge titled #WAXcupid.&lt;/p>
&lt;blockquote class="twitter-tweet" data-dnt="true">&lt;p lang="en" dir="ltr">Stupid unresponsive stylus. Need a Pro + Pencil already 😭 &lt;a href="https://t.co/95x5rXVsyr">pic.twitter.com/95x5rXVsyr&lt;/a>&lt;/p>&amp;mdash; 𝔐𝔦𝔠𝔥𝔞𝔢𝔩 ℜ𝔬𝔰𝔢 (@mmistakes) &lt;a href="https://twitter.com/mmistakes/status/833362456743440384?ref_src=twsrc%5Etfw">February 19, 2017&lt;/a>&lt;/blockquote>
&lt;script async src="https://platform.twitter.com/widgets.js" charset="utf-8">&lt;/script>
&lt;p>&lt;strong>Tools used:&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://www.amazon.com/Adobe-Creative-Connected-Precision-Stylus/dp/B00LNECVN6/ref=as_li_ss_tl?ie=UTF8&amp;amp;qid=1461688574&amp;amp;sr=8-1&amp;amp;keywords=adobe&amp;#43;ink&amp;amp;linkCode=ll1&amp;amp;tag=mademist-20&amp;amp;linkId=85c30649adf50e2ff0b9c753f6dfe2c9" rel="noopener">Adobe Ink stylus&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://en.wikipedia.org/wiki/IPad_Air_2" rel="noopener">iPad Air 2&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://procreate.art/" rel="noopener">Procreate&lt;/a>&lt;/li>
&lt;/ul>&lt;p>&lt;a href="https://mademistakes.com/work/procreate-paintings/lunar-ashes-portrait/?utm_source=atom_feed">Choke's embrace&lt;/a> was originally published on Made Mistakes.&lt;/p></content><category scheme="https://mademistakes.com/tag/procreate" term="procreate" label="Procreate"/><category scheme="https://mademistakes.com/tag/portrait" term="portrait" label="portrait"/><category scheme="https://mademistakes.com/tag/sktchy" term="sktchy" label="Sktchy"/><category scheme="https://mademistakes.com/tag/black-and-white" term="black-and-white" label="black and white"/><category scheme="https://mademistakes.com/tag/time-lapse" term="time-lapse" label="time-lapse"/></entry><entry><title type="html">Optimizing and automating my Jekyll site build</title><link href="https://mademistakes.com/articles/using-jekyll-2017/?utm_source=atom_feed" rel="alternate" type="text/html"/><id>https://mademistakes.com/articles/using-jekyll-2017/</id><published>2017-08-30T00:00:00-04:00</published><updated>2021-08-03T09:47:47-04:00</updated><summary type="html">Lessons learned from building, optimizing, and deploying a huge static site with everyone&amp;#39;s favorite SSG — Jekyll.</summary><content type="html">
&lt;img alt="" decoding="async" fetchpriority="auto" height="169" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/using-jekyll-2017-feature_hu2323378274701364566.jpg" style="background: #25282b;" width="300"
srcset="https://mademistakes.com/images/using-jekyll-2017-feature_hu2323378274701364566.jpg 300w,https://mademistakes.com/images/using-jekyll-2017-feature_hu1078546614930399751.jpg 800w, https://mademistakes.com/images/using-jekyll-2017-feature.jpg 1600w"
>&lt;p>Not going to lie &amp;mdash; I love &lt;strong>Jekyll&lt;/strong>, but huge &lt;a href="/tag/static-sites/">static sites&lt;/a> like mine take forever to build. What used to take seconds now go on for over 30 minutes, as thousands of files are generated and optimized.&lt;/p>
&lt;p>Inspired by Anne Tomasevich&amp;rsquo;s post, &lt;a href="https://www.savaslabs.com/blog/optimizing-jekyll-performance-gulp" rel="noopener">&lt;strong>Optimizing Jekyll Performance with Gulp&lt;/strong>&lt;/a> &amp;mdash; I begun digging into my build process to identify performance bottlenecks.&lt;/p>
&lt;p>At the time of writing this post, my site contained roughly:&lt;/p>
&lt;ul>
&lt;li>1,028 images generated at different sizes (5,140 in total).&lt;/li>
&lt;li>1,052 total documents (&lt;a href="https://github.com/mmistakes/made-mistakes-jekyll/tree/master/src/_posts" rel="noopener">991 posts&lt;/a> / &lt;a href="https://github.com/mmistakes/made-mistakes-jekyll/tree/master/src/_pages" rel="noopener">14 pages&lt;/a> / 3 sets of collections).&lt;/li>
&lt;li>&lt;a href="https://github.com/mmistakes/made-mistakes-jekyll/tree/master/src/_data/comments" rel="noopener">535 comments&lt;/a> stored as YAML data files.&lt;/li>
&lt;/ul>
&lt;p>And was built with the following Jekyll plugins: &lt;a href="https://github.com/rbuchberger/jekyll_picture_tag" rel="noopener">jekyll-picture-tag&lt;/a>, &lt;a href="https://github.com/mmistakes/made-mistakes-jekyll/blob/master/src/_plugins/sort_name.rb" rel="noopener">sort_name&lt;/a>, &lt;a href="https://github.com/jekyll/jekyll-archives" rel="noopener">jekyll-archives&lt;/a>, &lt;a href="https://github.com/jekyll/jekyll-assets" rel="noopener">jekyll-assets&lt;/a>, &lt;a href="https://github.com/pattex/jekyll-tagging" rel="noopener">jekyll/tagging&lt;/a>, &lt;a href="https://github.com/toshimaru/jekyll-tagging-related_posts" rel="noopener">jekyll-tagging-related_posts&lt;/a>, &lt;a href="https://github.com/jekyll/jekyll-sitemap" rel="noopener">jekyll-sitemap&lt;/a>, &lt;a href="https://github.com/jekyll/jemoji" rel="noopener">jemoji&lt;/a>, and &lt;a href="https://github.com/myles/jekyll-typogrify" rel="noopener">jekyll-typogrify&lt;/a>.&lt;/p>
&lt;p>Using Jekyll&amp;rsquo;s profiler flag &lt;code>--profile&lt;/code>, I measured how long the following tasks took to complete&lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup>. Before each build I ran &lt;code>jekyll clean&lt;/code> to wipe &lt;code>_site&lt;/code>, &lt;code>.asset-cache&lt;/code> and any other temporary files to keep results more consistent.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Task description&lt;/th>
&lt;th style="text-align: right">Windows&lt;/th>
&lt;th style="text-align: right">Mac&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Generate responsively sized &lt;code>page.image.feature&lt;/code> images&lt;/td>
&lt;td style="text-align: right">1288.29s&lt;/td>
&lt;td style="text-align: right">1429.26s&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Minify HTML with &lt;code>layout: compress&lt;/code>&lt;/td>
&lt;td style="text-align: right">39.35s&lt;/td>
&lt;td style="text-align: right">11.18s&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Read &lt;code>/images/&lt;/code> and copy to &lt;code>_site/&lt;/code>&lt;/td>
&lt;td style="text-align: right">32.39s&lt;/td>
&lt;td style="text-align: right">23.63s&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Build tag pages with jekyll-archives&lt;/td>
&lt;td style="text-align: right">17.40s&lt;/td>
&lt;td style="text-align: right">9.99s&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Build tag index page with &lt;code>sort_tag&lt;/code> filter&lt;/td>
&lt;td style="text-align: right">2.62s&lt;/td>
&lt;td style="text-align: right">0.59s&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>List related posts with &lt;a href="https://github.com/toshimaru/jekyll-tagging-related_posts" rel="noopener">jekyll-tagging-related_posts&lt;/a>, &lt;a href="https://github.com/pattex/jekyll-tagging" rel="noopener">jekyll/tagging&lt;/a>, and &lt;code>LSI: true&lt;/code>&lt;/td>
&lt;td style="text-align: right">38.36s&lt;/td>
&lt;td style="text-align: right">17.87s&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>List related posts with vanilla &lt;code>related_posts&lt;/code>&lt;/td>
&lt;td style="text-align: right">1.65s&lt;/td>
&lt;td style="text-align: right">4.90s&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Preprocess, concatenate, minify, and cache bust SCSS and JS with &lt;a href="https://github.com/jekyll/jekyll-assets" rel="noopener">jekyll-assets&lt;/a>&lt;/td>
&lt;td style="text-align: right">78.75s&lt;/td>
&lt;td style="text-align: right">25.03s&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Display masthead and overlay menu from &lt;code>_data&lt;/code>&lt;/td>
&lt;td style="text-align: right">9.66s&lt;/td>
&lt;td style="text-align: right">7.82s&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>List post comments from &lt;code>_data&lt;/code>&lt;/td>
&lt;td style="text-align: right">7.10s&lt;/td>
&lt;td style="text-align: right">2.22s&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Display colophon from &lt;code>_data&lt;/code>&lt;/td>
&lt;td style="text-align: right">0.20s&lt;/td>
&lt;td style="text-align: right">0.49s&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>List post taxonomy metadata&lt;/td>
&lt;td style="text-align: right">3.48s&lt;/td>
&lt;td style="text-align: right">1.18s&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>List post breadcrumb links&lt;/td>
&lt;td style="text-align: right">2.83s&lt;/td>
&lt;td style="text-align: right">1.03s&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>List featured posts&lt;/td>
&lt;td style="text-align: right">0.20s&lt;/td>
&lt;td style="text-align: right">0.44s&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Build style guide collection&lt;/td>
&lt;td style="text-align: right">1.16s&lt;/td>
&lt;td style="text-align: right">0.31s&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>List post social sharing links&lt;/td>
&lt;td style="text-align: right">5.09s&lt;/td>
&lt;td style="text-align: right">0.97s&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>Is Jekyll faster on macOs or Windows?&lt;/strong>&lt;/p>
&lt;p>For giggles I also tested my Windows and Mac development environments against each other. While not exactly an apples to apples comparison I thought it would be interesting to note any variations.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Windows Environment&lt;/th>
&lt;th>Mac Environment&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Lenovo ThinkStation E30&lt;/td>
&lt;td>iMac 21.5-inch, Late 2013&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Processor 3.30 GHz Intel Xeon E31245&lt;/td>
&lt;td>Processor 2.7 GHz Intel Core i5&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Memory 16 GB&lt;/td>
&lt;td>Memory 16 GB 1600 MHz DDR3&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Intel SSDSA2BW160G3L 137GB&lt;/td>
&lt;td>1.12 TB Fusion Drive&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Windows 7 Professional Service Pack 1&lt;/td>
&lt;td>OS X El Capitan 10.11.6&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>ruby 2.2.4p230 [x64-mingw32]&lt;/td>
&lt;td>ruby 2.1.0p0 (2013-12-25 revision 44422) [x86_64-darwin12.0]&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="optimization">&lt;a href="#optimization">Optimization&lt;/a>&lt;/h2>
&lt;p>The numbers above don&amp;rsquo;t lie. Relying on Jekyll and friends to do jobs more suited for a task runner like &lt;a href="https://gulpjs.com/" rel="noopener">&lt;strong>Gulp&lt;/strong>&lt;/a> was slowing the build. Armed with this knowledge I started stripping down Jekyll to one of its core purposes &amp;mdash; &lt;em>converting Markdown files into HTML&lt;/em>.&lt;/p>
&lt;p>Doing this also allowed me to make my site&amp;rsquo;s content more portable and not as reliant on a specific static-site generator. In the off-chance I wanted to swap Jekyll for another SSG like &lt;a href="https://gohugo.io/" rel="noopener">&lt;strong>Hugo&lt;/strong>&lt;/a> or &lt;a href="https://github.com/gatsbyjs/gatsby" rel="noopener">&lt;strong>Gatsby&lt;/strong>&lt;/a>, I could.&lt;/p>
&lt;h3 id="css-and-javascript-assets">&lt;a href="#css-and-javascript-assets">CSS and JavaScript assets&lt;/a>&lt;/h3>
&lt;p>&lt;a href="https://github.com/jekyll/jekyll-assets" rel="noopener">&lt;strong>Jekyll Assets&lt;/strong>&lt;/a> is a great plugin that served me well for a long time by: preprocessing, vendor prefixing, concatenating, minifying, and fingerprinting assets. But with a site of my size it was painfully slow to iterate with during front-end development.&lt;/p>
&lt;p>Making a change to a Sass partial would trigger a full site rebuild, which meant waiting a few minutes before it could be previewed in a browser. Jekyll&amp;rsquo;s incremental build feature might have helped here, but I never had much luck getting it to work reliably.&lt;/p>
&lt;p>During development CSS or JavaScript changes would ideally be pushed instantly to the browser with something like &lt;a href="https://www.browsersync.io/" rel="noopener">Browsersync&lt;/a>. By replacing &lt;a href="https://github.com/jekyll/jekyll-assets" rel="noopener">Jekyll Assets&lt;/a> with the following Gulp alternatives I was able to do this, and improve build times by 93%:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://github.com/sass/node-sass" rel="noopener">&lt;strong>node-sass&lt;/strong>&lt;/a> and &lt;a href="https://github.com/dlmanning/gulp-sass" rel="noopener">&lt;strong>gulp-sass&lt;/strong>&lt;/a>: natively compile SCSS files to CSS.&lt;/li>
&lt;li>&lt;a href="https://github.com/sindresorhus/gulp-autoprefixer" rel="noopener">&lt;strong>gulp-autoprefixer&lt;/strong>&lt;/a>: vendor prefix CSS.&lt;/li>
&lt;li>&lt;a href="https://www.npmjs.com/package/gulp-cssnano" rel="noopener">&lt;strong>gulp-cssnano&lt;/strong>&lt;/a>: minify CSS.&lt;/li>
&lt;li>&lt;a href="https://github.com/contra/gulp-concat" rel="noopener">&lt;strong>gulp-concat&lt;/strong>&lt;/a>: concatenate JavaScript.&lt;/li>
&lt;li>&lt;a href="https://github.com/terinjokes/gulp-uglify" rel="noopener">&lt;strong>gulp-uglify&lt;/strong>&lt;/a>: minify JavaScript with &lt;a href="https://www.npmjs.com/package/uglifyjs" rel="noopener">UglifyJS&lt;/a>.&lt;/li>
&lt;li>&lt;a href="https://github.com/gulp-sourcemaps/gulp-sourcemaps" rel="noopener">&lt;strong>gulp-sourcemaps&lt;/strong>&lt;/a>: add source maps for CSS and JS.&lt;/li>
&lt;li>&lt;a href="https://github.com/jstuckey/gulp-gzip" rel="noopener">&lt;strong>gulp-gzip&lt;/strong>&lt;/a>: gzip CSS and JS.&lt;/li>
&lt;li>&lt;a href="https://github.com/sindresorhus/gulp-rev" rel="noopener">&lt;strong>gulp-rev&lt;/strong>&lt;/a>: append hash to CSS and JS filenames for cache busting.&lt;/li>
&lt;/ul>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Task description&lt;/th>
&lt;th>Jekyll Assets&lt;/th>
&lt;th>Gulp&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>CSS and JavaScript asset pipeline&lt;/td>
&lt;td>25.031s&lt;/td>
&lt;td>1.577s&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Here&amp;rsquo;s a small taste of the &lt;a href="https://github.com/mmistakes/made-mistakes-jekyll/blob/master/gulp/tasks/assets.js" rel="noopener">Gulp file&lt;/a> I&amp;rsquo;m using for the site&amp;rsquo;s styles if you&amp;rsquo;re curious:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// &amp;#39;gulp styles&amp;#39; -- creates a CSS file from SCSS, adds prefixes and creates a Sourcemap
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// &amp;#39;gulp styles --prod&amp;#39; -- creates a CSS file from your SCSS, adds prefixes,
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// minifies, and cache busts it (does not create a Sourcemap)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">gulp&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">task&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;styles&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">gulp&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">src&lt;/span>&lt;span class="p">([&lt;/span>&lt;span class="nx">paths&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">sassFiles&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="s1">&amp;#39;/main.scss&amp;#39;&lt;/span>&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">pipe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">when&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="nx">argv&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">prod&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">sourcemaps&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">init&lt;/span>&lt;span class="p">()))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// preprocess Sass
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">.&lt;/span>&lt;span class="nx">pipe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">sass&lt;/span>&lt;span class="p">({&lt;/span>&lt;span class="nx">precision&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">}).&lt;/span>&lt;span class="nx">on&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;error&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">sass&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">logError&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">pipe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">postcss&lt;/span>&lt;span class="p">([&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// add vendor prefixes
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">autoprefixer&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">browsers&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;last 2 versions&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;&amp;gt; 5%&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;IE 9&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// minify for production
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">.&lt;/span>&lt;span class="nx">pipe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">when&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">argv&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">prod&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">when&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;*.css&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">cssnano&lt;/span>&lt;span class="p">({&lt;/span>&lt;span class="nx">autoprefixer&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">}))))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">pipe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">size&lt;/span>&lt;span class="p">({&lt;/span>&lt;span class="nx">showFiles&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">}))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// output sourcemap for development
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">.&lt;/span>&lt;span class="nx">pipe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">when&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="nx">argv&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">prod&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">sourcemaps&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">write&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;.&amp;#39;&lt;/span>&lt;span class="p">)))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">pipe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">when&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">argv&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">prod&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">gulp&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">dest&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">paths&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">sassFilesTemp&lt;/span>&lt;span class="p">)))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// hash CSS for production
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">.&lt;/span>&lt;span class="nx">pipe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">when&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">argv&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">prod&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">rev&lt;/span>&lt;span class="p">()))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">pipe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">when&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">argv&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">prod&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">size&lt;/span>&lt;span class="p">({&lt;/span>&lt;span class="nx">showFiles&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">})))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// output hashed files
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">.&lt;/span>&lt;span class="nx">pipe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">gulp&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">dest&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">paths&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">sassFilesTemp&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// generate manifest of hashed CSS files
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">.&lt;/span>&lt;span class="nx">pipe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">rev&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">manifest&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;css-manifest.json&amp;#39;&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">pipe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">gulp&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">dest&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">paths&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">tempDir&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nx">paths&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">sourceDir&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nx">paths&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">dataFolderName&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">pipe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">when&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">argv&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">prod&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">size&lt;/span>&lt;span class="p">({&lt;/span>&lt;span class="nx">showFiles&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">})))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">pipe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">when&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="nx">argv&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">prod&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">browserSync&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">stream&lt;/span>&lt;span class="p">()))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// function to properly reload your browser
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kd">function&lt;/span> &lt;span class="nx">reload&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">done&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">browserSync&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">reload&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">done&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// &amp;#39;gulp serve&amp;#39; -- open site in browser and watch for changes
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// in source files and update them when needed
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">gulp&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">task&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;serve&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">done&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">browserSync&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">init&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// tunnel: true,
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// open: false,
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">port&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mi">4000&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1">// change port to match default Jekyll
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">ui&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">port&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mi">4001&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">server&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="nx">paths&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">tempFolderName&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">paths&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">siteFolderName&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">done&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// watch various files for changes
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">gulp&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">watch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">paths&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">sassFilesGlob&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">gulp&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">series&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;styles&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">reload&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Without going too deep into how these Gulp tasks work, the gist is:&lt;/p>
&lt;ol>
&lt;li>A glob of files have &amp;ldquo;stuff&amp;rdquo; done to them using various plugins.&lt;/li>
&lt;li>These files are placed in a temporary folder excluded from Jekyll so they don&amp;rsquo;t trigger a full site rebuild during development.&lt;/li>
&lt;li>Production builds move these temporary files and deploys them alongside the HTML files generated by Jekyll.&lt;/li>
&lt;/ol>
&lt;h3 id="image-assets">&lt;a href="#image-assets">Image assets&lt;/a>&lt;/h3>
&lt;p>Resizing a thousand or so images to serve them responsively is no joke &amp;mdash; taking over 20 minutes to complete in my site&amp;rsquo;s case. Up until now I was using the &lt;a href="https://github.com/rbuchberger/jekyll_picture_tag" rel="noopener">Jekyll Picture Tag&lt;/a> plugin to do this work for me&amp;hellip; which wasn&amp;rsquo;t ideal.&lt;/p>
&lt;p>To try and see if &lt;a href="https://nodejs.org/en/" rel="noopener">Node&lt;/a> and &lt;a href="https://gulpjs.com/" rel="noopener">Gulp&lt;/a> could do this faster I came up with a set of Gulp tasks to:&lt;/p>
&lt;ol>
&lt;li>Generate thousands of &amp;ldquo;feature&amp;rdquo; images&lt;sup id="fnref:2">&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref">2&lt;/a>&lt;/sup> at 4 different sizes with &lt;a href="https://github.com/mahnunchik/gulp-responsive" rel="noopener">&lt;strong>gulp-responsive&lt;/strong>&lt;/a>.&lt;/li>
&lt;li>Optimize all images using &lt;a href="https://github.com/sindresorhus/gulp-imagemin" rel="noopener">&lt;strong>gulp-imagemin&lt;/strong>&lt;/a>.&lt;/li>
&lt;li>Save the optimized images directly to the destination folder &amp;mdash; bypassing Jekyll and a full-site rebuild.&lt;/li>
&lt;/ol>
&lt;p>This helped some, but it wasn&amp;rsquo;t until I &lt;a href="https://github.com/mmistakes/made-mistakes-jekyll/commit/56bbd9bf5429a269047a41e045cc2ef0bf34e62b" rel="noopener">dropped GraphicsMagick&lt;/a> for &lt;a href="https://github.com/lovell/sharp" rel="noopener">Sharp&lt;/a>&lt;sup id="fnref:3">&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref">3&lt;/a>&lt;/sup> did I see a noticeable improvement&amp;hellip;&lt;/p>
&lt;h4 id="sharp-really-is-as-fast-as-they-say">&lt;a href="#sharp-really-is-as-fast-as-they-say">Sharp really is as fast as they say&lt;/a>&lt;/h4>
&lt;p>I shaved 18 minutes off my build time using this high speed &lt;a href="https://nodejs.org/en/" rel="noopener">Node.js&lt;/a> module!&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Task description&lt;/th>
&lt;th>Graphics Magick&lt;/th>
&lt;th>Sharp&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Resize and optimize 1,014 source images into 5 target sizes&lt;/td>
&lt;td>1288.29s&lt;/td>
&lt;td>171.00s&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The other missing piece was generating the necessary markup for responsive images&lt;sup id="fnref:4">&lt;a href="#fn:4" class="footnote-ref" role="doc-noteref">4&lt;/a>&lt;/sup>. Because I was no longer using the &lt;code>{% picture %}&lt;/code> tag to output a fully formed &lt;a href="https://cloudfour.com/thinks/dont-use-picture-most-of-the-time/" rel="noopener">&lt;code>&amp;lt;picture&amp;gt;&lt;/code> element&lt;/a>, I had to roll my own responsive image markup.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">{% if page.image.feature %}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> {% assign f = page.image.feature | split: &amp;#39;.&amp;#39; %}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">img&lt;/span> &lt;span class="na">src&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ f[0] | relative_url }}-320.{{ f[1] }}&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">srcset&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ f[0] | relative_url }}-768.{{ f[1] }} 768w,
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s"> {{ f[0] | relative_url }}-1024.{{ f[1] }} 1024w,
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s"> {{ f[0] | relative_url }}.{{ f[1] }} 1920w&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">alt&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ page.title }}&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{% endif %}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This bit of Liquid takes the filename as defined by &lt;code>page.image.feature&lt;/code>, splits it at the extension, appends suffixes that match those used in the gulp-responsive task, and adds them to the &lt;code>srcset&lt;/code> attribute. Boom, resolution switching images!&lt;/p>
&lt;aside class="notice">&lt;h3>Liquid Limitations&lt;/h3>&lt;p>This Gulp + Liquid hybrid isn&amp;rsquo;t as nice as a plugin since there is no logic to actually verify if &lt;code>-lq&lt;/code>, &lt;code>-320&lt;/code>, &lt;code>-768&lt;/code>, or &lt;code>-1024&lt;/code> versions of the images exist. I&amp;rsquo;m willing to overlook that since it did shave 16 minutes off my build time.&lt;/p>
&lt;/aside>
&lt;h3 id="simplify-layouts">&lt;a href="#simplify-layouts">Simplify layouts&lt;/a>&lt;/h3>
&lt;p>With the bulk of the time savings coming from faster image resizing tasks, I went after some low-hanging fruit next by &lt;a href="https://github.com/mmistakes/made-mistakes-jekyll/issues/81" rel="noopener">decluttering my layouts&lt;/a>. Anything that detracted from the main content or added visual noise to the page was fair game for removal.&lt;/p>
&lt;h4 id="remove-social-sharing-module">&lt;a href="#remove-social-sharing-module">Remove social sharing module&lt;/a>&lt;/h4>
&lt;p>Including buttons at the bottom (or top of post) didn&amp;rsquo;t really boost shares of my content on Twitter or Facebook &amp;mdash; so I dropped them. If someone wants to share it on social media they&amp;rsquo;ll use the builtin methods of iOS/Android or do it the old fashioned way of copy-pasting a URL.&lt;/p>
&lt;h4 id="replace-layout-compress">&lt;a href="#replace-layout-compress">Replace &lt;code>layout: compress&lt;/code>&lt;/a>&lt;/h4>
&lt;p>Occasionally things on the site would break if inline JavaScript with &lt;code>//&lt;/code> styled comments were used. This happened because of a complex set of Liquid filters used in my &lt;a href="http://jch.penibelst.de/" rel="noopener">&lt;code>compress&lt;/code> layout&lt;/a> to reduce whitespace in the generated HTML.&lt;/p>
&lt;p>The fix was easy, just surround inline JavaScript comments with &lt;code>/* */&lt;/code> instead. Or do what I did, stop using &lt;code>layout: compress&lt;/code> and minify with these Gulp plugins instead: &lt;a href="https://github.com/jonschlinkert/gulp-htmlmin" rel="noopener">&lt;strong>gulp-htmlmin&lt;/strong>&lt;/a> and &lt;a href="https://github.com/jstuckey/gulp-gzip" rel="noopener">&lt;strong>gulp-gzip&lt;/strong>&lt;/a>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// &amp;#39;gulp html&amp;#39; -- does nothing
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// &amp;#39;gulp html --prod&amp;#39; -- minifies and gzips HTML files for production
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">gulp&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">task&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;html&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">gulp&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">src&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">paths&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">siteFolderName&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nx">paths&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">htmlPattern&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">pipe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">when&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">argv&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">prod&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">htmlmin&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">removeComments&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">collapseWhitespace&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">collapseBooleanAttributes&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">removeAttributeQuotes&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">removeRedundantAttributes&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">minifyJS&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">minifyCSS&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">pipe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">when&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">argv&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">prod&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">size&lt;/span>&lt;span class="p">({&lt;/span>&lt;span class="nx">title&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;optimized HTML&amp;#39;&lt;/span>&lt;span class="p">})))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">pipe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">when&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">argv&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">prod&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">gulp&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">dest&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">paths&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">siteFolderName&lt;/span>&lt;span class="p">)))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">pipe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">when&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">argv&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">prod&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">gzip&lt;/span>&lt;span class="p">({&lt;/span>&lt;span class="nx">append&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">})))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">pipe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">when&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">argv&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">prod&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">size&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">title&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;gzipped HTML&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">gzip&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">pipe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">when&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">argv&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">prod&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">gulp&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">dest&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">paths&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">siteFolderName&lt;/span>&lt;span class="p">)))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h5 id="minifying-html-with-gulp">&lt;a href="#minifying-html-with-gulp">Minifying HTML with Gulp&lt;/a>&lt;/h5>
&lt;p>In my tests, replacing &lt;a href="http://jch.penibelst.de/" rel="noopener">&lt;code>_layouts/compress.html&lt;/code>&lt;/a> with Gulp tasks to minify and gzip every HTML file ended up being slightly slower.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Task description&lt;/th>
&lt;th>compress layout&lt;/th>
&lt;th>gulp-html and gulp-gzip&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Minify HTML files&lt;/td>
&lt;td>39.348s&lt;/td>
&lt;td>47.00s&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>I&amp;rsquo;m willing to live with the extra 8 seconds of build time as it helps avoid potential site breakages due to badly minified scripts&amp;hellip;&lt;/p>
&lt;h4 id="reduce-and-replace-javascript">&lt;a href="#reduce-and-replace-javascript">Reduce and replace JavaScript&lt;/a>&lt;/h4>
&lt;p>I&amp;rsquo;m close to ditching jQuery and going vanilla, but I&amp;rsquo;m not quite there yet. Where possible I&amp;rsquo;ve ditched jQuery plugins and &lt;a href="https://github.com/mmistakes/made-mistakes-jekyll/issues/84" rel="noopener">replaced with &amp;ldquo;lighter&amp;rdquo; alternatives&lt;/a>. Here&amp;rsquo;s a few highlights:&lt;/p>
&lt;ul>
&lt;li>Replaced &lt;a href="https://github.com/dimsemenov/Magnific-Popup" rel="noopener">&lt;strong>Magnific Popup&lt;/strong>&lt;/a> with &lt;a href="https://sorgalla.com/lity/" rel="noopener">&lt;strong>Lity&lt;/strong>&lt;/a>.&lt;/li>
&lt;li>Replaced &lt;a href="https://github.com/verlok/lazyload" rel="noopener">&lt;strong>Lazyload&lt;/strong>&lt;/a> with &lt;a href="https://github.com/aFarkas/lazysizes" rel="noopener">&lt;strong>Lazysizes&lt;/strong>&lt;/a> + &lt;a href="https://github.com/aFarkas/lazysizes/tree/gh-pages/plugins/respimg" rel="noopener">Lazysizes responsive images polyfill extension&lt;/a>.&lt;/li>
&lt;li>Added &lt;a href="https://github.com/jonathantneal/svg4everybody" rel="noopener">&lt;strong>SVG 4 Everybody&lt;/strong>&lt;/a> for external SVG spritemap support.&lt;/li>
&lt;li>Replaced &lt;a href="http://fitvidsjs.com/" rel="noopener">&lt;strong>FitVids.JS&lt;/strong>&lt;/a> with responsive embed CSS and a custom Jekyll &lt;a href="https://github.com/mmistakes/made-mistakes-jekyll/blob/master/src/_plugins/video_embed.rb" rel="noopener">video embed tag&lt;/a>.&lt;/li>
&lt;/ul>
&lt;h3 id="results">&lt;a href="#results">Results&lt;/a>&lt;/h3>
&lt;p>Decoupling the asset pipeline from Jekyll and Gulp-ifying it made the biggest splash in terms of build time. Along with &lt;a href="https://www.browsersync.io/" rel="noopener">Browsersync&lt;/a>, any asset (CSS, JavaScript, images and icons) updates could be previewed almost instantly. Greatly speeding up the time it takes to develop and iterate the site&amp;rsquo;s front-end.&lt;/p>
&lt;aside class="notice">&lt;h3>Made Mistakes Gulp files&lt;/h3>&lt;p>This set of Gulp tasks were heavily inspired by those in Sondre Nilsen&amp;rsquo;s &lt;a href="https://github.com/sondr3/generator-jekyllized" rel="noopener">&lt;strong>Jekyllized Yeoman generator&lt;/strong>&lt;/a>. The main &lt;a href="https://github.com/mmistakes/made-mistakes-jekyll/blob/master/gulpfile.js" rel="noopener">&lt;code>gulpfile.js&lt;/code>&lt;/a> is broken down into &lt;a href="https://github.com/mmistakes/made-mistakes-jekyll/tree/master/gulp/tasks" rel="noopener">smaller bits&lt;/a>: assets, build, clean, copy, html, images, and uploading.&lt;/p>
&lt;p>With &lt;a href="https://github.com/mmistakes/made-mistakes-jekyll/blob/master/gulp/paths.js" rel="noopener">&lt;code>paths.js&lt;/code>&lt;/a> assigning all of the various paths used throughout the files to DRY things up.&lt;/p>
&lt;/aside>
&lt;h2 id="automation--continuous-integration">&lt;a href="#automation--continuous-integration">Automation &amp;amp; Continuous Integration&lt;/a>&lt;/h2>
&lt;p>After ditching Disqus last year to &lt;a href="/mastering-jekyll/static-comments/">roll my own static-based solution&lt;/a> powered by &lt;strong>Staticman&lt;/strong>, I needed to find a better way of deploying the site. Merging in new comments, pulling those commits down, manually building the site locally, and then deploying to my web server wasn&amp;rsquo;t ideal.&lt;/p>
&lt;p>With some research I determined a continuous integration&lt;sup id="fnref:5">&lt;a href="#fn:5" class="footnote-ref" role="doc-noteref">5&lt;/a>&lt;/sup> platform like &lt;strong>Travis CI&lt;/strong>&lt;sup id="fnref:6">&lt;a href="#fn:6" class="footnote-ref" role="doc-noteref">6&lt;/a>&lt;/sup> that integrates with GitHub was what I needed.&lt;/p>
&lt;div class="browser-frame">&lt;img alt="Travis CI interface." class="img--md" decoding="async" fetchpriority="auto" height="197" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/mm-travis-ci-screenshot_hu10714485042521995175.png" style="background: #fafafa;" width="300"
&lt;pre>&lt;code> srcset=&amp;quot;https://mademistakes.com/images/mm-travis-ci-screenshot_hu10714485042521995175.png 300w, https://mademistakes.com/images/mm-travis-ci-screenshot_hu9072102099428930831.png 620w&amp;quot;
&amp;gt;
&lt;/code>&lt;/pre>
&lt;/div>
&lt;p>Setting things up with Travis CI wasn&amp;rsquo;t too painful, but there was some trial and error getting dependencies squared away. I&amp;rsquo;d suggest reading through &lt;a href="https://docs.travis-ci.com/" rel="noopener">their documentation&lt;/a>, but the basic idea is:&lt;/p>
&lt;ol>
&lt;li>&lt;a href="https://travis-ci.org/auth" rel="noopener">Sign in to Travis CI&lt;/a> with your GitHub account and grant it access.&lt;/li>
&lt;li>Configure the build and deployment scripts with a &lt;a href="https://github.com/mmistakes/made-mistakes-jekyll/blob/aaf88a5223e757eba1e19ee52d0b55dbd0a8328c/.travis.yml" rel="noopener">&lt;code>.travis.yml&lt;/code>&lt;/a> file.&lt;/li>
&lt;li>Set the type of triggers&lt;sup id="fnref:7">&lt;a href="#fn:7" class="footnote-ref" role="doc-noteref">7&lt;/a>&lt;/sup> that initiate a build.&lt;/li>
&lt;/ol>
&lt;h3 id="travis-ci-configuration">&lt;a href="#travis-ci-configuration">Travis CI configuration&lt;/a>&lt;/h3>
&lt;p>Let&amp;rsquo;s take a closer look my &lt;code>.travis.yml&lt;/code> config file and how I&amp;rsquo;ve set it up.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">language&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">ruby&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">rvm&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="m">2.2&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Since Jekyll is built on Ruby I set the language to &lt;code>ruby&lt;/code>. Depending on what version of Jekyll you&amp;rsquo;re using you can change &lt;code>rvm&lt;/code> to meet your needs. I went with &lt;code>2.2&lt;/code> since that&amp;rsquo;s the latest &lt;a href="https://docs.travis-ci.com/user/languages/ruby/#Supported-Ruby-Versions-and-RVM" rel="noopener">Travis CI provides&lt;/a>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">sudo&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This enables quicker build start times using the new &lt;a href="https://docs.travis-ci.com/user/migrating-from-legacy/" rel="noopener">container-based infrastructure&lt;/a>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">branches&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">only&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">master&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Fairly obvious, build only from the &lt;code>master&lt;/code> branch.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">cache&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">bundler&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">yarn&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">directories&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">node_modules&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># NPM packages&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Travis CI pulls down and installs gem and Node.js modules before every build. Depending on what you have in your &lt;code>Gemfile&lt;/code> and &lt;code>package.json&lt;/code> files this can take a bit of time.&lt;/p>
&lt;p>The above lines enable caching of these dependencies eliminating this installation step.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">addons&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">apt&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">sources&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">ubuntu-toolchain-r-test&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">packages&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">g++-4.8&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">env&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="l">CXX=g++-4.8&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Enables the &lt;a href="https://gcc.gnu.org/" rel="noopener">GCC, GNU Compiler Collection&lt;/a> using &lt;a href="https://docs.travis-ci.com/user/installing-dependencies/#Installing-Packages-with-the-APT-Addon" rel="noopener">APT Addon&lt;/a> and a newer version of &lt;code>gcc/g++&lt;/code> to properly install &lt;a href="https://github.com/lovell/sharp" rel="noopener">Sharp&lt;/a>.&lt;/p>
&lt;h3 id="travis-ci-build-lifecycle">&lt;a href="#travis-ci-build-lifecycle">Travis CI build lifecycle&lt;/a>&lt;/h3>
&lt;p>My &lt;a href="https://docs.travis-ci.com/user/customizing-the-build/#The-Build-Lifecycle" rel="noopener">build lifecycle&lt;/a> breaks down like this:&lt;/p>
&lt;ol>
&lt;li>Clone repo and any submodules.&lt;/li>
&lt;li>Install &lt;code>apt addons&lt;/code>.&lt;/li>
&lt;li>Install Ruby gems.&lt;/li>
&lt;li>Upgrade Node.js and install &lt;a href="https://yarnpkg.com/en/" rel="noopener">Yarn&lt;/a> + Node modules.&lt;/li>
&lt;li>Run Gulp production build task &lt;code>gulp build --prod&lt;/code>.&lt;/li>
&lt;li>&lt;a href="https://oncletom.io/2016/travis-ssh-deploy/" rel="noopener">Decrypt private SSH keys&lt;/a> needed to rsync files to my server.&lt;/li>
&lt;li>Run Gulp deploy task:
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">deploy&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">provider&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">script&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">skip_cleanup&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">gulp deploy&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">on&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">branch&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">master&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ol>
&lt;p>With everything configured I no longer need to build locally and deploy. I can merge in comments or make small edits to Markdown files directly on GitHub, and automatically trigger a site build/deploy.&lt;/p>
&lt;p>If there&amp;rsquo;s a problem Travis CI will notify me, otherwise in ~15 minutes (or however long the build takes) any changes committed will be live on the site.&lt;/p>
&lt;aside class="notice">&lt;h3>Pro tip: use Travis CI with sites hosted with GitHub Pages&lt;/h3>&lt;p>Travis CI comes in handy if you want to use Jekyll plugins or a more advanced Gulp workflow like I am. There are &lt;a href="https://docs.travis-ci.com/user/deployment/pages/" rel="noopener">deployment scripts&lt;/a> specifically for this purpose.&lt;/p>
&lt;p>&lt;a href="https://www.netlify.com/" rel="noopener">Netlify&lt;/a>, &lt;a href="https://pages.gitlab.io/" rel="noopener">GitLab&lt;/a>, and friends also do similar things if you&amp;rsquo;re feeling constrained by what&amp;rsquo;s currently allowed by GitHub Pages.&lt;/p>
&lt;/aside>
&lt;h2 id="other-jekyll-related-bits">&lt;a href="#other-jekyll-related-bits">Other Jekyll related bits&lt;/a>&lt;/h2>
&lt;h3 id="pagination-upgrades">&lt;a href="#pagination-upgrades">Pagination upgrades&lt;/a>&lt;/h3>
&lt;p>Looking to wring a little more #WebPerf juice out of my site, I went after category and tag archive pages next. Depending on the tag, these &lt;code>index.html&lt;/code> pages could be quite hefty due to the amount of HTML needed to display hundreds of post teasers.&lt;/p>
&lt;p>To trim them down in size I needed a way of paginating these pages into small chunks. Jekyll has an &lt;a href="http://jekyllrb.com/docs/pagination/" rel="noopener">official pagination plugin&lt;/a>, but unfortunately it&amp;rsquo;s limited (and deprecated) in what it can actually paginate. &lt;a href="https://github.com/sverrirs/jekyll-paginate-v2" rel="noopener">&lt;strong>Jekyll Paginate v2&lt;/strong>&lt;/a> on the other hand is fully featured, backwards compatible, and actively being worked on.&lt;/p>
&lt;p>In addition to paginating posts it can handle: &lt;a href="https://github.com/sverrirs/jekyll-paginate-v2/blob/master/README-GENERATOR.md#paginating-collections" rel="noopener">collections&lt;/a>, &lt;a href="https://github.com/sverrirs/jekyll-paginate-v2/blob/master/README-GENERATOR.md#filtering-categories" rel="noopener">categories&lt;/a>, &lt;a href="https://github.com/sverrirs/jekyll-paginate-v2/blob/master/README-GENERATOR.md#filtering-tags" rel="noopener">tags&lt;/a>, and &lt;a href="https://github.com/sverrirs/jekyll-paginate-v2/blob/master/README-GENERATOR.md#filtering-locales" rel="noopener">locales&lt;/a> (useful if you have a multi-language site).&lt;/p>
&lt;p>It doesn&amp;rsquo;t stop there though! It has a built-in generator called &lt;a href="https://github.com/sverrirs/jekyll-paginate-v2/blob/master/README-AUTOPAGES.md" rel="noopener">&lt;strong>Auto-Pages&lt;/strong>&lt;/a> to create tag, category, and collection archives. Which unlike &lt;a href="https://github.com/jekyll/jekyll-archives" rel="noopener">&lt;strong>Jekyll Archives&lt;/strong>&lt;/a>, can all be paginated.&lt;/p>
&lt;p>With a few changes to my &lt;code>_config.yml&lt;/code> file and adapting my Jekyll Archive &lt;a href="https://github.com/mmistakes/made-mistakes-jekyll/blob/master/src/_layouts/autopage_tags.html" rel="noopener">layout&lt;/a> to work with &lt;a href="https://github.com/sverrirs/jekyll-paginate-v2" rel="noopener">Jekyll Paginate v2&lt;/a>, I was ready to roll.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># Plugin: Pagination (jekyll-paginate-v2)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">pagination&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">enabled &lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">debug &lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">per_page &lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">15&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">path &lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;/page/:num/&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">title &lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;:title - Page :num of :max&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">limit &lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">0&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">sort_field &lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;date&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">sort_reverse &lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c"># Plugin: Auto Pages (jekyll-paginate-v2)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">autopages&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">enabled &lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">categories&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">enabled &lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">collections&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">enabled &lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">tags&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">enabled &lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">layouts&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;autopage_tags.html&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">title &lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;:tag&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># :tag is replaced by the tag name&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">path &lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;/tag/:tag&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>For category archives I created my own bespoke pages since there are only a handful of them. Mostly because it was easier to customize them than generating with &lt;a href="https://github.com/sverrirs/jekyll-paginate-v2/blob/master/README-AUTOPAGES.md" rel="noopener">Auto-Pages&lt;/a>.&lt;/p>
&lt;p>&lt;strong>Step 1:&lt;/strong> Create an archive page&amp;hellip; let&amp;rsquo;s use my &lt;a href="/articles/">Articles archive&lt;/a> as an example. I like to keep all of my source pages grouped together, so I created &lt;code>articles.md&lt;/code> and placed it in a &lt;a href="/articles/using-jekyll-2016/#pages-for-everything-else">folder named &lt;code>_pages&lt;/code>&lt;/a>.&lt;/p>
&lt;p>&lt;strong>Step 2:&lt;/strong> Enable pagination and define what categories it should filter, in this case &lt;code>articles&lt;/code> by adding the following YAML Front Matter to &lt;code>articles.md&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">pagination&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">enabled&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">category&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">articles&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;aside class="notice">&lt;h3>Pro tip: &lt;a href="https://github.com/sverrirs/jekyll-paginate-v2/blob/master/README-GENERATOR.md#filtering-categories" rel="noopener">filter multiple categories&lt;/a>&lt;/h3>&lt;p>Want to combine several categories into one paginator object? Jekyll Paginate v2 has you covered. Just add additional categories as a comma separated list e.g., &lt;code>category: foo, bar&lt;/code>.&lt;/p>
&lt;/aside>
&lt;p>&lt;strong>Step 3:&lt;/strong> Output the posts by looping through the &lt;code>paginator.posts&lt;/code> array. A simple example would look something like this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">ul&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> {% for post in paginator.posts %}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!-- what you want to output. title, url, image, etc. --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">li&lt;/span>&lt;span class="p">&amp;gt;&amp;lt;&lt;/span>&lt;span class="nt">a&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ post.url }}&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>{{ post.title }}&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">a&lt;/span>&lt;span class="p">&amp;gt;&amp;lt;/&lt;/span>&lt;span class="nt">li&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> {% endfor %}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">ul&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>And for &amp;ldquo;next/previous&amp;rdquo; navigation links you can do something like this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">{% if paginator.total_pages &amp;gt; 1 %}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> {% if paginator.previous_page %}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">a&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ paginator.previous_page_path }}&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>Newer Posts&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">a&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> {% endif %}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> {% if paginator.next_page %}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">a&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ paginator.next_page_path }}&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>Older Posts&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">a&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> {% endif %}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{% endif %}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="lazyload-tag">&lt;a href="#lazyload-tag">Lazyload tag&lt;/a>&lt;/h3>
&lt;p>Another #WebPerf improvement was adding the ability to defer loading of images and video embeds.&lt;/p>
&lt;p>To do this I created a custom Jekyll plugin using &lt;a href="https://github.com/aFarkas/lazysizes" rel="noopener">&lt;strong>lazysizes&lt;/strong>&lt;/a> to do some JavaScript magic.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Attribute&lt;/th>
&lt;th>Required&lt;/th>
&lt;th>Description&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>data-src&lt;/code>&lt;/td>
&lt;td>&lt;strong>Required&lt;/strong>&lt;/td>
&lt;td>Full path to image eg: &lt;code>/assets/images/filename.jpg&lt;/code>. Use absolute URLS for those hosted externally.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>src&lt;/code>&lt;/td>
&lt;td>Optional&lt;/td>
&lt;td>Full path to low-quality image eg: &lt;code>/assets/images/filename.jpg&lt;/code>. Use absolute URLS for those hosted externally. Defaults to inline transparent &lt;code>.gif&lt;/code>.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>alt&lt;/code>&lt;/td>
&lt;td>Optional&lt;/td>
&lt;td>Image alternate text.&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>Example:&lt;/strong>&lt;/p>
&lt;pre tabindex="0">&lt;code class="language-liquid" data-lang="liquid">{% lazyload data-src=&amp;#34;/assets/images/my-image.jpg&amp;#34; src=&amp;#34;/assets/images/my-image-low-quality.jpg&amp;#34; alt=&amp;#34;my lazyloaded image&amp;#34; %}
&lt;/code>&lt;/pre>&lt;p>A Liquid version of this method is used in the &lt;a href="https://github.com/mmistakes/made-mistakes-jekyll/blob/11.0.0/src/_includes/page__hero.html" rel="noopener">hero image include&lt;/a> to apply a nice blur effect as those large images are loaded by the browser.&lt;/p>
&lt;h3 id="responsive-video-embed-tag">&lt;a href="#responsive-video-embed-tag">Responsive video embed tag&lt;/a>&lt;/h3>
&lt;p>Custom Jekyll plugin to embed a video from YouTube or Vimeo that responsively sizes to fit the width of its parent using &lt;a href="https://github.com/mmistakes/made-mistakes-jekyll/blob/master/src/_plugins/video_embed.rb" rel="noopener">&lt;code>/_plugins/video_embed.rb&lt;/code>&lt;/a>.&lt;/p>
&lt;p>Embeds are also &lt;a href="#lazyload-tag">lazyloaded&lt;/a> to improve page performance.&lt;/p>
&lt;h4 id="youtube-embed">&lt;a href="#youtube-embed">YouTube embed&lt;/a>&lt;/h4>
&lt;p>To embed the following YouTube video at url &lt;code>https://www.youtube.com/watch?v=fFX1CUO472U&lt;/code> (long version) or &lt;code>https://youtu.be/fFX1CUO472U&lt;/code> (short version) into a post or page&amp;rsquo;s main content you&amp;rsquo;d use:&lt;/p>
&lt;pre tabindex="0">&lt;code class="language-liquid" data-lang="liquid">{% youtube fFX1CUO472U %}
&lt;/code>&lt;/pre>&lt;h4 id="vimeo-embed">&lt;a href="#vimeo-embed">Vimeo embed&lt;/a>&lt;/h4>
&lt;p>To embed the following Vimeo video at url &lt;code>https://vimeo.com/97649261&lt;/code> into a post or page&amp;rsquo;s main content you&amp;rsquo;d use:&lt;/p>
&lt;pre tabindex="0">&lt;code class="language-liquid" data-lang="liquid">{% vimeo 34948855 %}
&lt;/code>&lt;/pre>&lt;h3 id="simplified-breadcrumbs">&lt;a href="#simplified-breadcrumbs">Simplified breadcrumbs&lt;/a>&lt;/h3>
&lt;p>Previously I was using some Liquid voodoo to &lt;a href="https://github.com/mmistakes/made-mistakes-jekyll/blob/10.3.0/src/_includes/breadcrumbs.html" rel="noopener">build a trail of breadcrumbs&lt;/a>. This sort of worked because my site structure isn&amp;rsquo;t too complex. On the other hand it wasn&amp;rsquo;t flexible and didn&amp;rsquo;t allow for overriding crumbs with more descriptive labels.&lt;/p>
&lt;p>Since my content hierarchy is shallow I decided to manually assign breadcrumbs to each post with YAML Front Matter.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">breadcrumbs&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">label&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Blog Articles&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">url&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">/articles/&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Then using this Liquid and HTML it&amp;rsquo;s outputted in my layout:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">{% if page.breadcrumbs %}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> {% assign crumb = page.breadcrumbs[0] %}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">a&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ crumb.url }}&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&amp;lt;&lt;/span>&lt;span class="nt">strong&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>{{ crumb.label }}&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">strong&lt;/span>&lt;span class="p">&amp;gt;&amp;lt;/&lt;/span>&lt;span class="nt">a&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{% endif %}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>For multiple levels of breadcrumbs the following YAML and sample &lt;code>for&lt;/code> loop should get the job started.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">breadcrumbs&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">label&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Level 1&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">url&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">/level-1/&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">label&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Level 2&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">url&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">/level-2/&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">{% if page.breadcrumbs %}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">ul&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;breadcrumbs&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> {% for crumb in page.breadcrumbs %}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">li&lt;/span>&lt;span class="p">&amp;gt;&amp;lt;&lt;/span>&lt;span class="nt">a&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ crumb.url }}&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>{{ crumb.label }}&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">a&lt;/span>&lt;span class="p">&amp;gt;&amp;lt;/&lt;/span>&lt;span class="nt">li&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> {% endfor %}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">ul&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{% endif %}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;aside class="notice">&lt;h3>Pro tip: add breadcrumbs using &lt;a href="http://jekyllrb.com/docs/configuration/#front-matter-defaults" rel="noopener">front matter defaults&lt;/a>&lt;/h3>&lt;p>Take a DRY approach and &lt;a href="https://github.com/mmistakes/made-mistakes-jekyll/blob/11.0.0/_config.yml#L126-L128" rel="noopener">add breadcrumbs&lt;/a> at the category level instead of on every post/page. &lt;code>defaults:&lt;/code> in &lt;code>_config.yml&lt;/code> is your friend.&lt;/p>
&lt;/aside>
&lt;h3 id="popular-tags">&lt;a href="#popular-tags">Popular tags&lt;/a>&lt;/h3>
&lt;p>Looking over my site&amp;rsquo;s analytics I came to the conclusion no one was clicking on the &amp;ldquo;this post was tagged&amp;rdquo; links, so I removed them. To make category pages more sticky and useful including tags seemed more useful. So with the following Liquid I was able to build a list of the most popular tags filtered on the current category.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">{% assign filterCategory = page.pagination.category | default: page.category %}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">ul&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> {% assign tagLimiter = 0 %}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> {% for tag in site.tags %}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> {% comment %}create an empty array{% endcomment %}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> {% assign postsInCategory = &amp;#34;&amp;#34; | split: &amp;#34;/&amp;#34; %}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> {% comment %}loop over site.tags{% endcomment %}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> {% for post in tag[1] %}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> {% if post.categories contains filterCategory %}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> {% comment %}if a post is in the filter category add it to postsInCategory array{% endcomment %}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> {% assign postsInCategory = postsInCategory | push: post %}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> {% endif %}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> {% endfor %}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> {% comment %}poor man&amp;#39;s tag limit on those that are frequently used{% endcomment %}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> {% if postsInCategory.size &amp;gt;= 5 and tagLimiter &lt;span class="err">&amp;lt;&lt;/span>= 5 %}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> {% assign tagLimiter = tagLimiter | plus: 1 %}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">li&lt;/span>&lt;span class="p">&amp;gt;&amp;lt;&lt;/span>&lt;span class="nt">a&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;/tag/{{ tag[0] | replace:&amp;#39; &amp;#39;,&amp;#39;-&amp;#39; | downcase }}/&amp;#34;&lt;/span> &lt;span class="p">&amp;gt;&lt;/span>{{ tag[0] }}&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">a&lt;/span>&lt;span class="p">&amp;gt;&amp;lt;/&lt;/span>&lt;span class="nt">li&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> {% endif %}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> {% endfor %}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">ul&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>On my &lt;strong>Mastering Paper&lt;/strong> category page about the iPad app &lt;a href="https://wetransfer.com/" rel="noopener">Paper by WeTransfer&lt;/a>, you&amp;rsquo;ll see related links to tags like &lt;em>Paper app&lt;/em>, &lt;em>iPad&lt;/em>, &lt;em>drawing&lt;/em>, etc.&lt;/p>
&lt;p>
&lt;img alt="popular topics screenshot" class="img--md" decoding="async" fetchpriority="auto" height="187" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/mm-popular-tags-screenshot_hu17103113767384058016.png" style="background: #e5e5e4;" width="300"
srcset="https://mademistakes.com/images/mm-popular-tags-screenshot_hu17103113767384058016.png 300w, https://mademistakes.com/images/mm-popular-tags-screenshot_hu548149241591527132.png 620w"
>&lt;/p>
&lt;hr>
&lt;p>Well there you have it, the complete tale of how I took something inherently simple and overly complicated it to suit my needs. If you have any questions after looking over the source ask away in the comments below.&lt;/p>
&lt;div class="footnotes" role="doc-endnotes">
&lt;hr>
&lt;ol>
&lt;li id="fn:1">
&lt;p>Each task was run 3 times and averaged as the values produced by &lt;code>jekyll build --profile&lt;/code> varied quite a bit.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:2">
&lt;p>I classify &amp;ldquo;features&amp;rdquo; as large, often full-width images commonly seen in landing pages built with &lt;strong>Bootstrap&lt;/strong> and other popular CSS frameworks.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:3">
&lt;p>Sharp is super fast, but only resizes JPEG, PNG, WebP, and TIFF images&amp;hellip; no GIF. It&amp;rsquo;s also a pain in the ass to install on Windows due to the &lt;a href="https://github.com/nodejs/node-gyp" rel="noopener">&lt;code>node-gyp&lt;/code>&lt;/a> dependency.&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:4">
&lt;p>In the last couple of years several &amp;ldquo;cloud&amp;rdquo; solutions have emerged to make serving responsively sized images easier. &lt;a href="https://cloudinary.com/" rel="noopener">&lt;strong>Cloudinary&lt;/strong>&lt;/a> (free plan), &lt;a href="https://www.imgix.com/" rel="noopener">&lt;strong>imgix&lt;/strong>&lt;/a> (paid plans), and &lt;a href="https://www.scientiamobile.com/page/imageengine" rel="noopener">&lt;strong>ImageEngine&lt;/strong>&lt;/a> (free plan) just to name a few.&amp;#160;&lt;a href="#fnref:4" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:5">
&lt;p>Continuous integration is a DevOps software development practice where developers regularly merge their code changes into a central repository, after which automated builds and tests are run.&amp;#160;&lt;a href="#fnref:5" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:6">
&lt;p>There are several CI platforms and services out there that can automate testing, building, and deploying a JAMstack site. &lt;a href="https://www.netlify.com/" rel="noopener">Netlify&lt;/a>, &lt;a href="https://circleci.com/" rel="noopener">Circle CI&lt;/a>, &lt;a href="https://www.codeship.io/" rel="noopener">Codeship&lt;/a>, &lt;a href="https://www.travis-ci.com/" rel="noopener">Travis CI&lt;/a>, and &lt;a href="https://about.gitlab.com/features/gitlab-ci-cd/" rel="noopener">GitLab CI&lt;/a> to name a few.&amp;#160;&lt;a href="#fnref:6" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:7">
&lt;p>Branch updates, pull requests, or both.&amp;#160;&lt;a href="#fnref:7" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;/div>&lt;p>&lt;a href="https://mademistakes.com/articles/using-jekyll-2017/?utm_source=atom_feed">Optimizing and automating my Jekyll site build&lt;/a> was originally published on Made Mistakes.&lt;/p></content><category scheme="https://mademistakes.com/tag/open-source" term="open-source" label="open source"/><category scheme="https://mademistakes.com/tag/static-sites" term="static-sites" label="static sites"/><category scheme="https://mademistakes.com/tag/tutorials" term="tutorials" label="tutorials"/><category scheme="https://mademistakes.com/tag/web-development" term="web-development" label="web development"/></entry><entry><title type="html">Styling language specific code blocks with Jekyll</title><link href="https://mademistakes.com/mastering-jekyll/styling-language-specific-code-blocks/?utm_source=atom_feed" rel="alternate" type="text/html"/><id>https://mademistakes.com/mastering-jekyll/styling-language-specific-code-blocks/</id><published>2017-05-16T00:00:00-04:00</published><updated>2021-08-03T11:55:33-04:00</updated><summary type="html">Interesting styling possibilities with Kramdown syntax highlighted code blocks in Jekyll.</summary><content type="html">
&lt;img alt="" decoding="async" fetchpriority="auto" height="169" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/styling-code-blocks_hu8110119900273887423.jpg" style="background: #212128;" width="300"
srcset="https://mademistakes.com/images/styling-code-blocks_hu8110119900273887423.jpg 300w,https://mademistakes.com/images/styling-code-blocks_hu15670185500933056283.jpg 800w, https://mademistakes.com/images/styling-code-blocks.jpg 2162w"
>&lt;p>While inspecting the markup of some syntax highlighted code blocks generated by Kramdown and &lt;strong>Jekyll&lt;/strong>, I noticed something I hadn&amp;rsquo;t before, &lt;code>language-lexer&lt;/code> classes:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">div&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;language-css highlighter-rouge&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">pre&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;highlight&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">code&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>.foo { color: red; }&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">code&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">pre&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">div&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This extra hook&lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup> on the &lt;code>&amp;lt;div&amp;gt;&lt;/code> element opens up some interesting styling possibilities. A fun use case for this is styling code blocks to look like terminal windows. Assign &lt;code>terminal&lt;/code>&lt;sup id="fnref:2">&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref">2&lt;/a>&lt;/sup> as the language/lexer like so:&lt;/p>
&lt;pre tabindex="0">&lt;code>```terminal
```
&lt;/code>&lt;/pre>&lt;p>Add the following Sass to your site&amp;rsquo;s stylesheet:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-scss" data-lang="scss">&lt;span class="line">&lt;span class="cl">&lt;span class="nv">$terminal-window-height&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mi">30&lt;/span>&lt;span class="kt">px&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nc">.language-terminal&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">position&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="ni">relative&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">margin-bottom&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="mf">.5&lt;/span>&lt;span class="kt">em&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">padding&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nf">calc&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="si">#{&lt;/span>&lt;span class="nv">$terminal-window-height&lt;/span>&lt;span class="si">}&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="kt">em&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="kt">em&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="kt">em&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">border&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="kt">px&lt;/span> &lt;span class="ni">solid&lt;/span> &lt;span class="nv">$border-color&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">border-radius&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nv">$border-radius&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">box-shadow&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mi">0&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="mf">.25&lt;/span>&lt;span class="kt">em&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="kt">em&lt;/span> &lt;span class="nf">rgba&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nv">$base-color&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="mf">.25&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">background-color&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nv">$base-color&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">&amp;amp;&lt;/span>&lt;span class="nd">::before&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">content&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;\2022 \2022 \2022&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">position&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="ni">absolute&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">top&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">left&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">margin&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">padding&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="mf">.5&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="nv">$terminal-window-height&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">background&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nf">mix&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nv">$base-color&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="mh">#fff&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="mi">25&lt;/span>&lt;span class="kt">%&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">color&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nf">mix&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nv">$base-color&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="mh">#fff&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="mi">50&lt;/span>&lt;span class="kt">%&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">font-size&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="mi">2&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="nv">$terminal-window-height&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">line-height&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">text-indent&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="mf">.5&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="nv">$terminal-window-height&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nc">.highlight&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">margin&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">padding&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">background-color&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="ni">initial&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">color&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mh">#fff&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>And you&amp;rsquo;ll get something that looks like this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="o">[&lt;/span>15:34:01&lt;span class="o">]&lt;/span> Finished &lt;span class="s1">&amp;#39;site&amp;#39;&lt;/span> after 2.18 min
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">[&lt;/span>15:34:01&lt;span class="o">]&lt;/span> Starting &lt;span class="s1">&amp;#39;copy:site&amp;#39;&lt;/span>...
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">[&lt;/span>15:34:06&lt;span class="o">]&lt;/span> Finished &lt;span class="s1">&amp;#39;copy:site&amp;#39;&lt;/span> after 4.8 s
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">[&lt;/span>15:34:06&lt;span class="o">]&lt;/span> Finished &lt;span class="s1">&amp;#39;build:site&amp;#39;&lt;/span> after 2.33 min
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">[&lt;/span>15:34:06&lt;span class="o">]&lt;/span> Starting &lt;span class="s1">&amp;#39;reload&amp;#39;&lt;/span>...
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">[&lt;/span>BS&lt;span class="o">]&lt;/span> Reloading Browsers...
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">[&lt;/span>15:34:06&lt;span class="o">]&lt;/span> Finished &lt;span class="s1">&amp;#39;reload&amp;#39;&lt;/span> after 5.22 ms
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This isn&amp;rsquo;t limited to &lt;code>language-terminal&lt;/code>, and can be applied to any of the other &lt;a href="https://github.com/jneen/rouge/wiki/List-of-supported-languages-and-lexers" rel="noopener">languages and lexers&lt;/a> supported by &lt;a href="http://rouge.jneen.net/" rel="noopener">&lt;strong>Rouge&lt;/strong>&lt;/a>.&lt;/p>
&lt;div class="footnotes" role="doc-endnotes">
&lt;hr>
&lt;ol>
&lt;li id="fn:1">
&lt;p>Apparently this was &lt;a href="https://github.com/gettalong/kramdown/issues/328" rel="noopener">added into &lt;strong>Kramdown&lt;/strong>&lt;/a> a year ago. 😐 Who knew?&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:2">
&lt;p>&lt;code>terminal&lt;/code> and &lt;code>console&lt;/code> are aliases for &lt;code>shell_session&lt;/code> and can be used interchangeably when assigning a language for syntax highlighting.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;/div>&lt;p>&lt;a href="https://mademistakes.com/mastering-jekyll/styling-language-specific-code-blocks/?utm_source=atom_feed">Styling language specific code blocks with Jekyll&lt;/a> was originally published on Made Mistakes.&lt;/p></content><category scheme="https://mademistakes.com/tag/static-sites" term="static-sites" label="static sites"/><category scheme="https://mademistakes.com/tag/til" term="til" label="TIL"/><category scheme="https://mademistakes.com/tag/tutorials" term="tutorials" label="tutorials"/><category scheme="https://mademistakes.com/tag/web-development" term="web-development" label="web development"/></entry><entry><title type="html">Relic Scout</title><link href="https://mademistakes.com/work/relic-scout/?utm_source=atom_feed" rel="alternate" type="text/html"/><id>https://mademistakes.com/work/relic-scout/</id><published>2017-04-19T00:00:00-04:00</published><updated>2019-12-10T16:51:32-05:00</updated><summary type="html">Site design and creative for Relic Scout — an app to make collecting simpler, while saving you money.</summary><content type="html">
&lt;img alt="" decoding="async" fetchpriority="auto" height="169" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/relic-scout-feature_hu900974008588135010.jpg" style="background: #be7034;" width="300"
srcset="https://mademistakes.com/images/relic-scout-feature_hu900974008588135010.jpg 300w,https://mademistakes.com/images/relic-scout-feature_hu7265773699442199818.jpg 800w, https://mademistakes.com/images/relic-scout-feature.jpg 1689w"
>&lt;p>Eko Wave, a company started by &lt;a href="https://twitter.com/endonend" rel="noopener">two&lt;/a> &lt;a href="https://twitter.com/bjpmba" rel="noopener">pals&lt;/a> and myself, just took the wraps off our first app &amp;mdash; &lt;strong>Relic Scout&lt;/strong>. Last week we launched a teaser site and couldn&amp;rsquo;t be prouder of the work that we&amp;rsquo;ve done so far.&lt;/p>
&lt;p>I had a lot of fun creating the 8bit pixel art and applying a parallax effect to it using the &lt;a href="https://github.com/cameronmcefee/plax" rel="noopener">jQuery plugin Plax&lt;/a>. If you collect sports cards, magic cards, coins, or comics, you should check it out.&lt;/p>
&lt;p>
&lt;img alt="Relic Scout homepage screenshot" class="img--md" decoding="async" fetchpriority="auto" height="332" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/relic-scout-browser-750_hu17679335679381107328.jpg" style="background: #3c312d;" width="300"
srcset="https://mademistakes.com/images/relic-scout-browser-750_hu17679335679381107328.jpg 300w, https://mademistakes.com/images/relic-scout-browser-750_hu15208151399461433644.jpg 620w"
>&lt;/p>
&lt;h2 id="style-tiles">&lt;a href="#style-tiles">Style tiles&lt;/a>&lt;/h2>
&lt;p>
&lt;img alt="Color palette" class="img--md" decoding="async" fetchpriority="auto" height="133" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/relic-scout-tile-color-palette_hu11349577460920255375.png" style="background: #f4ecd9;" width="300"
srcset="https://mademistakes.com/images/relic-scout-tile-color-palette_hu11349577460920255375.png 300w, https://mademistakes.com/images/relic-scout-tile-color-palette_hu14178558865757578375.png 620w"
>&lt;/p>
&lt;p>
&lt;img alt="Typography - headers" class="img--md" decoding="async" fetchpriority="auto" height="105" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/relic-scout-tile-headers_hu4569756165033275123.png" style="background: #f3ebd8;" width="300"
srcset="https://mademistakes.com/images/relic-scout-tile-headers_hu4569756165033275123.png 300w, https://mademistakes.com/images/relic-scout-tile-headers_hu13830406265079597529.png 620w"
>&lt;/p>
&lt;p>
&lt;img alt="Typography - body text" class="img--md" decoding="async" fetchpriority="auto" height="75" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/relic-scout-tile-body-text_hu4542176157972588146.png" style="background: #f2ead7;" width="300"
srcset="https://mademistakes.com/images/relic-scout-tile-body-text_hu4542176157972588146.png 300w, https://mademistakes.com/images/relic-scout-tile-body-text_hu14933009783934859913.png 620w"
>&lt;/p>
&lt;p>
&lt;img alt="Typography - in use" class="img--md" decoding="async" fetchpriority="auto" height="375" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/relic-scout-tile-typography-usage_hu17182027141099465805.png" style="background: #f2ead6;" width="300"
srcset="https://mademistakes.com/images/relic-scout-tile-typography-usage_hu17182027141099465805.png 300w, https://mademistakes.com/images/relic-scout-tile-typography-usage_hu12421271999281696852.png 620w"
>&lt;/p>
&lt;p>
&lt;img alt="Buttons" class="img--md" decoding="async" fetchpriority="auto" height="43" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/relic-scout-tile-buttons_hu3617469506411390983.png" style="background: #f4ecd8;" width="300"
srcset="https://mademistakes.com/images/relic-scout-tile-buttons_hu3617469506411390983.png 300w, https://mademistakes.com/images/relic-scout-tile-buttons_hu4833146691129132714.png 620w"
>&lt;/p>&lt;p>&lt;a href="https://mademistakes.com/work/relic-scout/?utm_source=atom_feed">Relic Scout&lt;/a> was originally published on Made Mistakes.&lt;/p></content><category scheme="https://mademistakes.com/tag/design" term="design" label="design"/><category scheme="https://mademistakes.com/tag/web-development" term="web-development" label="web development"/></entry><entry><title type="html">Eko Wave</title><link href="https://mademistakes.com/work/eko-wave/?utm_source=atom_feed" rel="alternate" type="text/html"/><id>https://mademistakes.com/work/eko-wave/</id><published>2017-04-01T00:00:00-04:00</published><updated>2018-03-05T13:54:54-05:00</updated><summary type="html">Site design and creative for Eko Wave — a team specializing in niche applications.</summary><content type="html">
&lt;img alt="" decoding="async" fetchpriority="auto" height="169" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/eko-wave-feature_hu8607032929211237410.jpg" style="background: #3e595c;" width="300"
srcset="https://mademistakes.com/images/eko-wave-feature_hu8607032929211237410.jpg 300w,https://mademistakes.com/images/eko-wave-feature_hu3454788725887437475.jpg 800w, https://mademistakes.com/images/eko-wave-feature.jpg 1024w"
>&lt;p>&lt;strong>Eko Wave LLC&lt;/strong> is a company started by &lt;a href="https://twitter.com/endonend" rel="noopener">two&lt;/a> &lt;a href="https://twitter.com/bjpmba" rel="noopener">pals&lt;/a> and myself to provide value to niche consumers through well-thought-out applications.&lt;/p>
&lt;p>When designing the logo I wanted something that played off of the word &lt;em>wave&lt;/em> found in our name, while keeping with a color palette we established in Eko&amp;rsquo;s previous life.&lt;/p>
&lt;p>
&lt;img alt="Eko Wave logo guide" class="img--md" decoding="async" fetchpriority="auto" height="410" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/eko-wave-logo-guide_hu4061445193727430468.jpg" style="background: #fcfcfc;" width="300"
srcset="https://mademistakes.com/images/eko-wave-logo-guide_hu4061445193727430468.jpg 300w, https://mademistakes.com/images/eko-wave-logo-guide_hu3869544983841525208.jpg 620w"
>&lt;/p>
&lt;p>Taking things a step further I created a basic style tile&lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup> document that helped me brainstorm how color, typography, and imagery fit and sell the concept to my partners. It would also serve as blueprint for me to design the site in browser &amp;mdash; making for a more enjoyable and loose experience instead of being tied to a .PSD mockup I needed to CSSify&lt;sup id="fnref:2">&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref">2&lt;/a>&lt;/sup> exactly.&lt;/p>
&lt;p>
&lt;img alt="Eko Wave style tile" class="img--md" decoding="async" fetchpriority="auto" height="660" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/eko-wave-style-tile_hu16832533014276752629.jpg" style="background: #404f53;" width="300"
srcset="https://mademistakes.com/images/eko-wave-style-tile_hu16832533014276752629.jpg 300w, https://mademistakes.com/images/eko-wave-style-tile_hu7220452787497605096.jpg 620w"
>&lt;/p>
&lt;p>The end result was a simple one page site that will ultimately grow as we build out our application and service portfolio.&lt;/p>
&lt;div class="footnotes" role="doc-endnotes">
&lt;hr>
&lt;ol>
&lt;li id="fn:1">
&lt;p>Style Tiles are a design deliverable consisting of fonts, colors and interface elements that communicate the essence of a visual brand for the web. (&lt;a href="http://styletil.es/" rel="noopener">Learn more&lt;/a>)&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:2">
&lt;p>Is CSSify even a word?&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;/div>&lt;p>&lt;a href="https://mademistakes.com/work/eko-wave/?utm_source=atom_feed">Eko Wave&lt;/a> was originally published on Made Mistakes.&lt;/p></content><category scheme="https://mademistakes.com/tag/design" term="design" label="design"/><category scheme="https://mademistakes.com/tag/web-development" term="web-development" label="web development"/></entry><entry><title type="html">Basically Basic Jekyll theme</title><link href="https://mademistakes.com/work/jekyll-themes/basically-basic/?utm_source=atom_feed" rel="alternate" type="text/html"/><id>https://mademistakes.com/work/jekyll-themes/basically-basic/</id><published>2017-03-29T00:00:00-04:00</published><updated>2021-09-05T15:01:37-04:00</updated><summary type="html">Your new Jekyll default theme.</summary><content type="html">
&lt;img alt="" decoding="async" fetchpriority="auto" height="169" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/jekyll-theme-basically-basic-feature_hu15099722207204065470.jpg" style="background: #233d39;" width="300"
srcset="https://mademistakes.com/images/jekyll-theme-basically-basic-feature_hu15099722207204065470.jpg 300w,https://mademistakes.com/images/jekyll-theme-basically-basic-feature_hu2787404913442946733.jpg 800w, https://mademistakes.com/images/jekyll-theme-basically-basic-feature.jpg 1234w"
>&lt;p>Basically Basic is a &lt;a href="https://jekyllrb.com/docs/themes/" rel="noopener">Jekyll theme&lt;/a> meant as a substitute for the default &amp;mdash; &lt;a href="https://github.com/jekyll/minima" rel="noopener">Minima&lt;/a>. Conventions and features found there are fully supported by &lt;strong>Basically Basic&lt;/strong>, with a few enhancements thrown in for good measure:&lt;/p>
&lt;ul>
&lt;li>Clean responsive design with six customizable skins&lt;/li>
&lt;li>Curriculum Vitæ/Resume layout powered by &lt;a href="https://jsonresume.org/" rel="noopener">JSON Resume&lt;/a>&lt;/li>
&lt;li>About page layout&lt;/li>
&lt;li>Disqus Comments and Google Analytics support&lt;/li>
&lt;li>SEO best practices via &lt;a href="https://github.com/jekyll/jekyll-seo-tag/" rel="noopener">Jekyll SEO Tag&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>
&lt;a href="https://mmistakes.github.io/jekyll-theme-basically-basic/" class="c-btn">Live preview&lt;/a>
&lt;/p>
&lt;div class="browser-frame">&lt;img alt="Screenshot of page with off-canvas menu." class="img--md" decoding="async" fetchpriority="auto" height="215" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/basically-basic-theme-menu_hu11938899852185244636.png" style="background: #fcfcfc;" width="300"
&lt;pre>&lt;code> srcset=&amp;quot;https://mademistakes.com/images/basically-basic-theme-menu_hu11938899852185244636.png 300w, https://mademistakes.com/images/basically-basic-theme-menu_hu8761160464118511264.png 620w&amp;quot;
&amp;gt;
&lt;/code>&lt;/pre>
&lt;/div>
&lt;div class="browser-frame">&lt;img alt="Screenshot of CV/Resume page." class="img--md" decoding="async" fetchpriority="auto" height="215" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/basically-basic-theme-cv_hu1206984904495914484.png" style="background: #fefffe;" width="300"
&lt;pre>&lt;code> srcset=&amp;quot;https://mademistakes.com/images/basically-basic-theme-cv_hu1206984904495914484.png 300w, https://mademistakes.com/images/basically-basic-theme-cv_hu10907596805391693609.png 620w&amp;quot;
&amp;gt;
&lt;/code>&lt;/pre>
&lt;/div>
&lt;div class="browser-frame">&lt;img alt="Screenshot of alternate theme skin." class="img--md" decoding="async" fetchpriority="auto" height="215" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/basically-basic-theme-alternate-skin_hu2210136705519057760.jpg" style="background: #f8f3e9;" width="300"
&lt;pre>&lt;code> srcset=&amp;quot;https://mademistakes.com/images/basically-basic-theme-alternate-skin_hu2210136705519057760.jpg 300w, https://mademistakes.com/images/basically-basic-theme-alternate-skin_hu3515784419130727665.jpg 620w&amp;quot;
&amp;gt;
&lt;/code>&lt;/pre>
&lt;/div>
&lt;h2 id="usage">&lt;a href="#usage">Usage&lt;/a>&lt;/h2>
&lt;p>To learn more about how to install, configure, customize the look and feel, create a curriculum vitæ/resume page, and some other junk, &lt;a href="https://github.com/mmistakes/jekyll-theme-basically-basic" rel="noopener">read up here&lt;/a>.&lt;/p>
&lt;h2 id="troubleshooting">&lt;a href="#troubleshooting">Troubleshooting&lt;/a>&lt;/h2>
&lt;p>Need help setting up the theme or getting it configured? Search the &lt;a href="https://github.com/mmistakes/jekyll-theme-basically-basic/discussions" rel="noopener">discussions&lt;/a> or &lt;a href="https://github.com/mmistakes/jekyll-theme-basically-basic/issues" rel="noopener">issues&lt;/a> on GitHub. Having trouble installing or using Jekyll, the &lt;a href="https://talk.jekyllrb.com/" rel="noopener">&lt;strong>Jekyll Talk&lt;/strong> forum&lt;/a> is a good place to ask for help.&lt;/p>
&lt;h2 id="license">&lt;a href="#license">License&lt;/a>&lt;/h2>
&lt;p>This theme is free and open source software, distributed under the &lt;a href="https://github.com/mmistakes/jekyll-theme-basically-basic/blob/master/LICENSE" rel="noopener">MIT License&lt;/a>.&lt;/p>&lt;p>&lt;a href="https://mademistakes.com/work/jekyll-themes/basically-basic/?utm_source=atom_feed">Basically Basic Jekyll theme&lt;/a> was originally published on Made Mistakes.&lt;/p></content><category scheme="https://mademistakes.com/tag/web-development" term="web-development" label="web development"/><category scheme="https://mademistakes.com/tag/static-sites" term="static-sites" label="static sites"/><category scheme="https://mademistakes.com/tag/design" term="design" label="design"/><category scheme="https://mademistakes.com/tag/open-source" term="open-source" label="open source"/></entry><entry><title type="html">Accessing static files in Jekyll</title><link href="https://mademistakes.com/mastering-jekyll/static-files/?utm_source=atom_feed" rel="alternate" type="text/html"/><id>https://mademistakes.com/mastering-jekyll/static-files/</id><published>2017-01-30T00:00:00-05:00</published><updated>2021-07-12T17:06:48-04:00</updated><summary type="html">Learn how to use static files with Jekyll to create a gallery of images automatically without a plugin.</summary><content type="html">
&lt;img alt="" decoding="async" fetchpriority="auto" height="169" itemprop="image" loading="lazy" sizes="(min-width: 620px) 620px, calc(100vw - 4rem)" src="https://mademistakes.com/images/static-files-illustration_hu7885832905749910873.jpg" style="background: #292824;" width="300"
srcset="https://mademistakes.com/images/static-files-illustration_hu7885832905749910873.jpg 300w,https://mademistakes.com/images/static-files-illustration_hu2869465202334805391.jpg 800w, https://mademistakes.com/images/static-files-illustration.jpg 2392w"
>&lt;p>While investigating ways of using static files with Jekyll for a site prototype I was building, I learned that &lt;code>site.static_files&lt;/code> was a thing. What is it and why you should care? Let me take a minute to share&amp;hellip;&lt;/p>
&lt;p>According to the official &lt;a href="https://jekyllrb.com/docs/static-files/" rel="noopener">Jekyll documentation&lt;/a>, &amp;ldquo;a static file is a file that does not contain any YAML front matter. These include images, PDFs, and other un-rendered content.&amp;rdquo;&lt;/p>
&lt;p>&lt;strong>The useful bit&lt;/strong> &amp;mdash; every one of these static files becomes accessible to Jekyll when looping through &lt;code>site.static_files&lt;/code>. There aren&amp;rsquo;t many reasons why you&amp;rsquo;d want to do this, but if building &amp;ldquo;plugin-free&amp;rdquo; image galleries is one of them &amp;mdash; here&amp;rsquo;s how.&lt;/p>
&lt;h2 id="organize-static-files">&lt;a href="#organize-static-files">Organize static files&lt;/a>&lt;/h2>
&lt;p>Let&amp;rsquo;s say you have a folder (named &lt;code>gallery-1&lt;/code>) of images you want to display together on a post or page.&lt;/p>
&lt;pre tabindex="0">&lt;code>├── assets/
│ └── images/
│ └── gallery-1/
│ ├── foo.jpg
│ ├── bar.gif
│ └── yaz.png
&lt;/code>&lt;/pre>&lt;h2 id="start-a-loop">&lt;a href="#start-a-loop">Start a loop&lt;/a>&lt;/h2>
&lt;p>Using &lt;a href="https://shopify.github.io/liquid/" rel="noopener">Liquid&lt;/a>&lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup> you&amp;rsquo;d start by looping through &lt;code>site.static_files&lt;/code> and then narrow down the files based on their paths. In this case we only want to show those images that live together in &lt;code>assets/images/gallery-1&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">{% raw%}{% for image in site.static_files %}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> {% if image.path contains &amp;#39;assets/images/gallery-1&amp;#39; %}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">img&lt;/span> &lt;span class="na">src&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ image.path }}&amp;#34;&lt;/span> &lt;span class="na">alt&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> {% endif %}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{% endfor %}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Which will generate into the following HTML:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">img&lt;/span> &lt;span class="na">src&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;assets/images/gallery-1/bar.gif&amp;#34;&lt;/span> &lt;span class="na">alt&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">img&lt;/span> &lt;span class="na">src&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;assets/images/gallery-1/foo.jpg&amp;#34;&lt;/span> &lt;span class="na">alt&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">img&lt;/span> &lt;span class="na">src&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;assets/images/gallery-1/yaz.png&amp;#34;&lt;/span> &lt;span class="na">alt&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="build-an-image-gallery-with-liquid">&lt;a href="#build-an-image-gallery-with-liquid">Build an image gallery with Liquid&lt;/a>&lt;/h2>
&lt;p>What if you&amp;rsquo;d rather spit out a group of thumbnail&lt;sup id="fnref:2">&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref">2&lt;/a>&lt;/sup> images that link to a full-size version? Completely achievable using the above method &amp;mdash; if you are careful to name these files in a predictable way.&lt;/p>
&lt;p>Start by creating thumbnail versions of each image however you&amp;rsquo;d like. The important part here is to be consistent with the naming of these files. For the sake of this example I&amp;rsquo;ll be adding the suffix &lt;code>-th&lt;/code> to each filename and storing them in the same location as the full-size assets.&lt;/p>
&lt;pre tabindex="0">&lt;code>├── assets/
│ └── images/
│ └── gallery-1/
│ ├── foo.jpg
│ ├── foo-th.jpg
│ ├── bar.gif
│ ├── bar-th.gif
│ ├── yaz.png
│ └── yaz-th.png
&lt;/code>&lt;/pre>&lt;p>Assuming a thumbnail and full-size image share the same base name, we can construct the gallery&amp;rsquo;s HTML with the following:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">{% for image in site.static_files %}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> {% if image.path contains &amp;#39;assets/images/gallery-1&amp;#39; %}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> {% unless image.path contains &amp;#39;-th.&amp;#39; %}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">a&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ image.path }}&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">img&lt;/span> &lt;span class="na">src&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ image.basename | append: &amp;#39;-th&amp;#39; | append: image.extname }}&amp;#34;&lt;/span> &lt;span class="na">alt&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">a&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> {% endunless %}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> {% endif %}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{% endfor %}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now you may be wondering where &lt;code>basename&lt;/code> and &lt;code>extname&lt;/code> came from&amp;hellip; simple. Each static file contains metadata accessible via Liquid with the following variables:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Variable&lt;/th>
&lt;th>Description&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>file.path&lt;/code>&lt;/td>
&lt;td>The relative path to the file, e.g., &lt;code>/assets/img/image.jpg&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>file.modified_time&lt;/code>&lt;/td>
&lt;td>The time the file was last modified, e.g., &lt;code>2016-04-01 16:35:26 +0200&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>file.name&lt;/code>&lt;/td>
&lt;td>The string name of the file e.g., &lt;code>image.jpg&lt;/code> for &lt;code>image.jpg&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>file.basename&lt;/code>&lt;/td>
&lt;td>The string basename of the file e.g., &lt;code>image&lt;/code> for &lt;code>image.jpg&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>file.extname&lt;/code>&lt;/td>
&lt;td>The extension name for the file, e.g., &lt;code>.jpg&lt;/code> for &lt;code>image.jpg&lt;/code>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;aside class="notice">&lt;h3>Note: filename consistency is paramount&lt;/h3>&lt;p>Using this method to build a gallery of thumbnail images is fragile. The &lt;code>image&lt;/code> objects inside of these loops are not validated by Jekyll at all. If you call in paths that don&amp;rsquo;t exist it will silently fail and leave you with missing images on the page.&lt;/p>
&lt;/aside>
&lt;h2 id="feedback-and-improvements">&lt;a href="#feedback-and-improvements">Feedback and improvements&lt;/a>&lt;/h2>
&lt;p>In the end looping through &lt;code>site.static_files&lt;/code> became an easy way spitting out a folder of files without having to hard code &lt;code>&amp;lt;img&amp;gt;&lt;/code> elements. Sure there are a million different ways to do this with Jekyll, but this served a need and was a quick hack.&lt;/p>
&lt;p>Curious if others are using &lt;strong>static files&lt;/strong> in cool and interesting ways. Feel free to let me know in the comment section below if you are.&lt;/p>
&lt;div class="footnotes" role="doc-endnotes">
&lt;hr>
&lt;ol>
&lt;li id="fn:1">
&lt;p>Liquid is an open source template language created by Shopify and written in Ruby.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:2">
&lt;p>Thumbnails are reduced-size versions of pictures, used to help in recognizing and organizing them, serving the same role for images as a normal text index does for words.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;/div>&lt;p>&lt;a href="https://mademistakes.com/mastering-jekyll/static-files/?utm_source=atom_feed">Accessing static files in Jekyll&lt;/a> was originally published on Made Mistakes.&lt;/p></content><category scheme="https://mademistakes.com/tag/static-sites" term="static-sites" label="static sites"/><category scheme="https://mademistakes.com/tag/til" term="til" label="TIL"/><category scheme="https://mademistakes.com/tag/tutorials" term="tutorials" label="tutorials"/><category scheme="https://mademistakes.com/tag/web-development" term="web-development" label="web development"/></entry></feed>