<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Bryce Wray</title>
    <description>The latest articles on DEV Community by Bryce Wray (@brycewray).</description>
    <link>https://dev.to/brycewray</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F372924%2F810c43e2-d9dc-44f2-b885-e5058a1b7321.png</url>
      <title>DEV Community: Bryce Wray</title>
      <link>https://dev.to/brycewray</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/brycewray"/>
    <language>en</language>
    <item>
      <title>Leaving Google Domains for Porkbun</title>
      <dc:creator>Bryce Wray</dc:creator>
      <pubDate>Fri, 16 Jun 2023 16:39:14 +0000</pubDate>
      <link>https://dev.to/brycewray/leaving-google-domains-for-porkbun-23j1</link>
      <guid>https://dev.to/brycewray/leaving-google-domains-for-porkbun-23j1</guid>
      <description>&lt;p&gt;Out of the clear blue yesterday came an &lt;a href="https://www.prnewswire.com/news-releases/squarespace-enters-definitive-agreement-to-acquire-google-domains-assets-301852507.html?tc=eml_cleartime"&gt;announcement&lt;/a&gt; that &lt;a href="https://squarespace.com"&gt;Squarespace&lt;/a&gt; had agreed to buy &lt;a href="https://domains.google.com"&gt;Google Domains&lt;/a&gt; from Google’s parent company.&lt;/p&gt;

&lt;p&gt;Having been a satisfied customer of Google Domains for several years, I was &lt;em&gt;not&lt;/em&gt; pleased — but, fortunately, I’d already suspected something like this might happen someday, so I had an exit strategy ready to go.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Incidentally, my discomfort with this surprising news was &lt;a href="https://news.ycombinator.com/item?id=36346454"&gt;by no means unusual&lt;/a&gt; among my fellow nerds.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I’d long admired Google Domains' technical excellence, transparent pricing, and (relatively) good GUI; but Google is &lt;a href="https://killedbygoogle.com"&gt;infamous for shutting down even popular projects&lt;/a&gt;, so I knew I needed to be ready in case Google Domains ever wound up on the death list.&lt;/p&gt;

&lt;p&gt;More on that shortly, after I clear up one item which may otherwise seem to blunt the impact of this event. That’s because, in this case, Google isn’t &lt;strong&gt;killing&lt;/strong&gt; Google Domains. It’s &lt;strong&gt;selling&lt;/strong&gt; it — to a company that typically charges quite a bit more.&lt;/p&gt;

&lt;p&gt;“Oh,” you might say, “but didn’t the announcement promise that, once the deal had gone through, pricing wouldn’t change right away for existing Google Domains customers?” Yep, it sure did:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Under the terms of the agreement, Squarespace will honor all existing Google Domains customers’ renewal prices for at least 12 months following the closing of the transaction . . .&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And that sounds fine, &lt;strong&gt;but&lt;/strong&gt; let’s finish that sentence:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;. . . as well as provide additional incentives to encourage Google Domains customers to build a website with Squarespace and adopt other Squarespace offerings.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Alert! Upsells incoming! Those with PTSD from hosting companies like GoDaddy and Bluehost — not to mention “We’re so cheap in the first year &lt;small&gt;(so we can screw you thereafter)&lt;/small&gt;” come-ons from cell service providers — should shield yourselves!&lt;/p&gt;

&lt;p&gt;Uh, no. Just no. Not now; not ever.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Besides: as a web dev nerd, I have a general objection to the Squarespaces of the world, as you might have detected in 2021’s “&lt;a href="https://www.brycewray.com/posts/2021/01/easy-peasy/"&gt;Easy-peasy&lt;/a&gt;.”)&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Anyway, back to that aforementioned exit strategy . . .&lt;/p&gt;

&lt;p&gt;I’d noticed that, whenever people on &lt;a href="https://news.ycombinator.com"&gt;Hacker News&lt;/a&gt; debate the merits and demerits of various domain registrars, one called &lt;a href="https://porkbun.com"&gt;Porkbun&lt;/a&gt; seemed to get a lot of love. After doing some research of my own and confirming that Porkbun looked like a good choice, I decided to give it a try.&lt;/p&gt;

&lt;p&gt;At the time, I had three different domains on Google Domains but I’d never actually used one of those domains for anything, so transferring it to Porkbun proved a pretty safe way to sample the experience. Porkbun’s marketing is perhaps a little too playful for my old-curmudgeon-like tastes, but the company does the real domains-handling stuff quite well, and at very fair pricing that I’ve found about as transparent as Google’s has been. Moreover, the people behind it seem to be genuinely nice, honest folks.&lt;/p&gt;

&lt;p&gt;So, after getting wind yesterday of the Google Domains sale, I promptly moved the other two domains to Porkbun.&lt;/p&gt;

&lt;p&gt;I’m not getting a dime to say this; but, if you, too, need to find a domain registrar (even if you couldn’t care less about what’s happening with Google Domains), I can highly recommend Porkbun.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Hugo and Tailwind: peace at last (maybe)</title>
      <dc:creator>Bryce Wray</dc:creator>
      <pubDate>Sat, 03 Jun 2023 17:45:04 +0000</pubDate>
      <link>https://dev.to/brycewray/hugo-and-tailwind-peace-at-last-maybe-4h25</link>
      <guid>https://dev.to/brycewray/hugo-and-tailwind-peace-at-last-maybe-4h25</guid>
      <description>&lt;p&gt;In the beginning, there were the &lt;a href="https://gohugo.io"&gt;Hugo&lt;/a&gt; &lt;a href="https://jamstack.org/generators"&gt;static site generator&lt;/a&gt; (SSG) and the &lt;a href="https://tailwindcss.com"&gt;Tailwind CSS&lt;/a&gt; styling framework. Separately, they were great; but, when you tried to put them together, things got complicated. Now, it appears, that’s been resolved. [Pinky-swear.]&lt;/p&gt;

&lt;p&gt;Things first got weird between Hugo and Tailwind early in 2021, when v.2 of the latter &lt;a href="https://blog.tailwindcss.com/just-in-time-the-next-generation-of-tailwind-css"&gt;included a just-in-time (JIT) compiler&lt;/a&gt;. At first, JIT was opt-in, then opt-out, and, since v.3, has been thoroughly baked into Tailwind. The problem has been that Tailwind/JIT just didn’t work all that smoothly with &lt;a href="https://gohugo.io/hugo-pipes"&gt;Hugo Pipes&lt;/a&gt;, Hugo’s built-in assets-handling pipeline, especially during development.&lt;/p&gt;

&lt;p&gt;In November, 2021, I &lt;a href="https://dev.to/posts/2021/11/making-tailwind-jit-work-hugo/"&gt;described&lt;/a&gt; a community-created, albeit somewhat convoluted, procedure that got Hugo to work with Tailwind v.2. However, only a few weeks later, the Tailwind folks &lt;a href="https://tailwindcss.com/blog/tailwindcss-v3"&gt;brought out v.3&lt;/a&gt;. Again, the Hugo community adapted and, before long, I was able to &lt;a href="https://dev.to/posts/2022/03/making-tailwind-jit-work-hugo-version-3-edition/"&gt;explain&lt;/a&gt; a smoother procedure that worked &lt;strong&gt;if&lt;/strong&gt; paired with Tailwind v.3.0.11 or newer (within &lt;strong&gt;v.3&lt;/strong&gt;, of course; more on that later). Still, these solutions admittedly were hacks, necessary only because the Hugo team hadn’t yet managed to deliver an official fix for the glitch — albeit not for lack of trying, to be fair.&lt;/p&gt;

&lt;p&gt;That changed with the &lt;a href="https://github.com/gohugoio/hugo/releases/tag/v0.112.0"&gt;recent release of Hugo v.0.112.0&lt;/a&gt;. Its major enhancement was a new &lt;code&gt;build.cachebusters&lt;/code&gt; option that was targeted at, finally, solving the whole Hugo/Tailwind/JIT problem. Here’s a summary of how each product is to be configured as a result, given the example in that link.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hugo (in development mode):

&lt;ul&gt;
&lt;li&gt;Constantly updates the &lt;code&gt;hugo_stats.json&lt;/code&gt; file, which automatically tracks the project's HTML tags, CSS classes, and IDs.&lt;/li&gt;
&lt;li&gt;Uses the &lt;code&gt;build.cachebusters&lt;/code&gt; settings, including for files “watched” by Tailwind, to tell Hugo when to update the dev server.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Tailwind CSS:

&lt;ul&gt;
&lt;li&gt;“Watches” the &lt;code&gt;hugo_stats.json&lt;/code&gt; file, as well as the other usual locations, to trigger JIT-based changes in the CSS output during Hugo’s development mode.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Update, 2023-06-05&lt;/strong&gt;: The example code provided in the Hugo v.0.112.0 release notes seems to assume the use of &lt;a href="https://gohugo.io/hugo-modules/"&gt;Hugo Modules&lt;/a&gt;. I initially thought that, if not using them, you wouldn’t need the &lt;code&gt;module&lt;/code&gt; settings to make this work. &lt;strong&gt;However&lt;/strong&gt;, I’ve since &lt;a href="https://discourse.gohugo.io/t/using-the-new-cachebusters-feature-with-a-theme/44700"&gt;learned otherwise&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And I can tell you that it works quite well. I’m currently working on a freelance project that pairs Tailwind v.3.x with suitably &lt;code&gt;cachebusters&lt;/code&gt;-ed Hugo v.0.112.x (Hugo/Tailwind is the customer’s preferred arrangement), and the interaction between the two is just as it should be between Tailwind and any site-building platform.&lt;sup id="fnref1"&gt;1&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;Outstanding news, right? So why did I put “(maybe)” in the title? And why did I use the “Pinky-swear” wink-wink in the intro?&lt;/p&gt;

&lt;p&gt;Well, it’s because Tailwind &lt;strong&gt;v.4&lt;/strong&gt; can’t be far off. Consider the cadence of the framework’s major version releases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/tailwindlabs/tailwindcss/releases/tag/v1.0.0"&gt;v.1.0.0&lt;/a&gt; — 2019-05-13.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/tailwindlabs/tailwindcss/releases/tag/v2.0.0"&gt;v.2.0.0&lt;/a&gt; — 2020-11-18, eighteen months after v.1.0.0.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/tailwindlabs/tailwindcss/releases/tag/v3.0.0"&gt;v.3.0.0&lt;/a&gt; — 2021-12-09, nearly thirteen months after v.2.0.0.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thus, as of the initial publication of this post, it’s been nearly eighteen months since the release of v.3.0.0. Note also that Tailwind creator Adam Wathan &lt;a href="https://twitter.com/adamwathan/status/1594874850178367488"&gt;said&lt;/a&gt; last November:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Goals for Tailwind CSS v4.0:&lt;/p&gt;

&lt;p&gt;🤏 Simplify mental model, fewer framework-specific concepts&lt;br&gt;
🪄 Less configuration, without less power&lt;br&gt;
✂️ Fewer dependencies&lt;br&gt;
⚡️ 10x faster&lt;/p&gt;

&lt;p&gt;Aggressively defend against complexity, double down on robustness and stability 🤝&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;All of these, along with certain references in replies to various issues reports on the &lt;a href="https://github.com/tailwindlabs/tailwindcss"&gt;Tailwind CSS GitHub repository&lt;/a&gt;, suggest one should expect a v.4 release sooner rather than later.&lt;/p&gt;

&lt;p&gt;The history of major Tailwind releases has been that each new one requires some juggling to make it work properly with Hugo. It’s been an “arms race” kind of thing. So, while Hugo v.0.112.x (and up) now gets along well with Tailwind v.3.x, we’ll have to wait to see what happens with future releases of Tailwind — and Hugo, for that matter.&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;I don’t know whether the aforementioned Hugo/Tailwind hacks still work but, in any event, you don’t need them any longer. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
    </item>
    <item>
      <title>Here, not there — or, making styling behave</title>
      <dc:creator>Bryce Wray</dc:creator>
      <pubDate>Sun, 19 Mar 2023 20:45:00 +0000</pubDate>
      <link>https://dev.to/brycewray/here-not-there-or-making-styling-behave-43ia</link>
      <guid>https://dev.to/brycewray/here-not-there-or-making-styling-behave-43ia</guid>
      <description>&lt;p&gt;All I wanted to do was make my bold and slanted text instances a little easier to see, as I’d seen done on others’ sites. But, man, did that ever end up involving some convoluted-looking styling. On the chance that you might need to do something similar, here are the details.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: Before I proceed, I must acknowledge &lt;a href="https://www.stefanjudis.com"&gt;Stefan Judis&lt;/a&gt;’s article, “&lt;a href="https://www.stefanjudis.com/snippets/how-to-select-elements-that-are-not-children-of-other-elements-in-css/"&gt;How to select elements that are not children of other elements in CSS&lt;/a&gt;,” without which I’d have been lost as a goose in what I’m about to describe.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Using sans-serif fonts, like &lt;a href="https://www.brycewray.com"&gt;my site&lt;/a&gt; is doing as of the initial publication of this post, can make some styling combinations hard to see under certain viewing conditions. While this is true for slanted text, it surprisingly can happen with boldfaced text, too, especially if one is viewing in dark mode.&lt;/p&gt;

&lt;p&gt;(Obviously, where slanted text is concerned, what we’re talking about with many sans-serif web fonts is &lt;em&gt;obliquing&lt;/em&gt; rather than the more commonly mentioned &lt;em&gt;italicization&lt;/em&gt;. However, since I suspect readers will be more familiar with calling slanted text “italics” than calling it “oblique,” I’ll use the former term from here on. Also: equally obviously, &lt;em&gt;truly&lt;/em&gt; italicized text, like an italicized serif font, is a completely separate consideration.)&lt;/p&gt;

&lt;p&gt;I’ve noticed some sites, such as &lt;a href="https://chriscoyier.net/"&gt;Chris Coyier’s&lt;/a&gt;, use some subtle styling to make bolds and italics stand out slightly from surrounding plain text, so I decided to try that on this site. I thought at first that it would be fairly simple: just set each variation to be a little darker in light mode and a little lighter in dark mode.&lt;/p&gt;

&lt;p&gt;. . . But, not so.&lt;/p&gt;

&lt;p&gt;First, I had to take into account the possible &lt;em&gt;pairing&lt;/em&gt; of boldfaced and italicized text, &lt;strong&gt;&lt;em&gt;like this&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Then, I remembered I’d also need to provide for all these variations to work within a blockquote, which starts out looking a little different than the surrounding plain text:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Here is a blockquote with &lt;strong&gt;bold&lt;/strong&gt;, &lt;em&gt;italic&lt;/em&gt;, and &lt;strong&gt;&lt;em&gt;bold italic&lt;/em&gt;&lt;/strong&gt; text.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And, finally, there was the matter of making sure the specified color differences didn't futz with colors that I didn’t &lt;em&gt;want&lt;/em&gt; to change, such as &lt;a href="https://www.brycewray.com"&gt;within &lt;em&gt;links&lt;/em&gt; &lt;strong&gt;like&lt;/strong&gt; &lt;strong&gt;&lt;em&gt;this&lt;/em&gt;&lt;/strong&gt; one to my home page&lt;/a&gt; and within the &lt;code&gt;red&lt;/code&gt; class I occasionally use for things like the &lt;strong&gt;Important&lt;/strong&gt;, below (although it won’t be red on this dev.to version of &lt;a href="https://www.brycewray.com/posts/2023/03/here-not-there-or-making-styling-behave/"&gt;my original post&lt;/a&gt;):&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Important&lt;/strong&gt;: This is a really big deal, so you should pay attention to it.&lt;/p&gt;

&lt;p&gt;But if you were to suspect this would require a mountain of gnarly CSS — or &lt;a href="https://sass-lang.com"&gt;Sass/SCSS&lt;/a&gt; — you’d be only half-correct. Gnarly? Oh, yeah. But a mountain? Nope.&lt;/p&gt;

&lt;p&gt;Before we get into the actual SCSS involved, note that the color variables to which it refers come from this (and, yes, I’m shamelessly cribbing those &lt;code&gt;$twcss&lt;/code&gt; colors from the &lt;a href="https://tailwindcss.com"&gt;Tailwind CSS&lt;/a&gt; &lt;a href="https://tailwindcss.com/docs/customizing-colors"&gt;color palette&lt;/a&gt; and then adding a couple more courtesy of &lt;a href="https://meyerweb.com/eric/tools/color-blend"&gt;Eric Meyer’s Color Blender tool&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="c1"&gt;// These are relevant excerpts from&lt;/span&gt;
&lt;span class="c1"&gt;// a partial called `_variables.scss`.&lt;/span&gt;

&lt;span class="nv"&gt;$twcss&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;gray-50&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#f9fafb&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;gray-100&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#f3f4f6&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;gray-200&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#e5e7eb&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;gray-300&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#d1d5db&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;gray-350&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#b7bcc5&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;gray-400&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#9ca3af&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;gray-500&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#6b7280&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;gray-600&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#4b5563&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;gray-650&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#414b5a&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;gray-700&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#374151&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;gray-800&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#1f2937&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;gray-900&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#111827&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With those variables set, here we go. (You may want to copy the code into a text editor for more comfortable, scroll-free viewing.)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="c1"&gt;// These are relevant excerpts from&lt;/span&gt;
&lt;span class="c1"&gt;// my `articles.scss` file, which&lt;/span&gt;
&lt;span class="c1"&gt;// comes into play only when a&lt;/span&gt;
&lt;span class="c1"&gt;// page has the `article` element.&lt;/span&gt;

&lt;span class="k"&gt;@charset&lt;/span&gt; &lt;span class="s1"&gt;'utf-8'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;@use&lt;/span&gt; &lt;span class="s1"&gt;'partials/variables'&lt;/span&gt; &lt;span class="nt"&gt;as&lt;/span&gt; &lt;span class="nt"&gt;var&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nt"&gt;article&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="c1"&gt;// First, we assign colors to&lt;/span&gt;
    &lt;span class="c1"&gt;// boldfaced and italicized text&lt;/span&gt;
    &lt;span class="c1"&gt;// that is **not** in the class&lt;/span&gt;
    &lt;span class="c1"&gt;// `red`, **not** inside a link,&lt;/span&gt;
    &lt;span class="c1"&gt;// and **not** within a blockquote&lt;/span&gt;
    &lt;span class="c1"&gt;// (we'll get to blockquotes below).&lt;/span&gt;
    &lt;span class="c1"&gt;//&lt;/span&gt;
    &lt;span class="c1"&gt;// Note that we cover not only&lt;/span&gt;
    &lt;span class="c1"&gt;// &amp;lt;strong&amp;gt; but also &amp;lt;b&amp;gt;, and&lt;/span&gt;
    &lt;span class="c1"&gt;// not only &amp;lt;em&amp;gt; but also &amp;lt;i&amp;gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// in addition, we take care of&lt;/span&gt;
    &lt;span class="c1"&gt;// groupings --- &amp;lt;strong&amp;gt;&amp;lt;em&amp;gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;// &amp;lt;em&amp;gt;&amp;lt;strong&amp;gt;, &amp;lt;b&amp;gt;&amp;lt;i&amp;gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;// and &amp;lt;i&amp;gt;&amp;lt;b&amp;gt;.&lt;/span&gt;

    &lt;span class="nt"&gt;strong&lt;/span&gt;&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;.red&lt;/span&gt; &lt;span class="o"&gt;*)&lt;/span&gt;&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="o"&gt;*)&lt;/span&gt;&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;blockquote&lt;/span&gt; &lt;span class="o"&gt;*),&lt;/span&gt;
    &lt;span class="nt"&gt;b&lt;/span&gt;&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;.red&lt;/span&gt; &lt;span class="o"&gt;*)&lt;/span&gt;&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="o"&gt;*)&lt;/span&gt;&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;blockquote&lt;/span&gt; &lt;span class="o"&gt;*),&lt;/span&gt;
    &lt;span class="nt"&gt;em&lt;/span&gt;&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;.red&lt;/span&gt; &lt;span class="o"&gt;*)&lt;/span&gt;&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="o"&gt;*)&lt;/span&gt;&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;blockquote&lt;/span&gt; &lt;span class="o"&gt;*),&lt;/span&gt;
    &lt;span class="nt"&gt;i&lt;/span&gt;&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;.red&lt;/span&gt; &lt;span class="o"&gt;*)&lt;/span&gt;&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="o"&gt;*)&lt;/span&gt;&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;blockquote&lt;/span&gt; &lt;span class="o"&gt;*),&lt;/span&gt;
    &lt;span class="nt"&gt;em&lt;/span&gt; &lt;span class="nt"&gt;strong&lt;/span&gt;&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;.red&lt;/span&gt; &lt;span class="o"&gt;*)&lt;/span&gt;&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="o"&gt;*)&lt;/span&gt;&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;blockquote&lt;/span&gt; &lt;span class="o"&gt;*),&lt;/span&gt;
    &lt;span class="nt"&gt;strong&lt;/span&gt; &lt;span class="nt"&gt;em&lt;/span&gt;&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;.red&lt;/span&gt; &lt;span class="o"&gt;*)&lt;/span&gt;&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="o"&gt;*)&lt;/span&gt;&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;blockquote&lt;/span&gt; &lt;span class="o"&gt;*),&lt;/span&gt;
    &lt;span class="nt"&gt;i&lt;/span&gt; &lt;span class="nt"&gt;b&lt;/span&gt;&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;.red&lt;/span&gt; &lt;span class="o"&gt;*)&lt;/span&gt;&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="o"&gt;*)&lt;/span&gt;&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;blockquote&lt;/span&gt; &lt;span class="o"&gt;*),&lt;/span&gt;
    &lt;span class="nt"&gt;b&lt;/span&gt; &lt;span class="nt"&gt;i&lt;/span&gt;&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;.red&lt;/span&gt; &lt;span class="o"&gt;*)&lt;/span&gt;&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="o"&gt;*)&lt;/span&gt;&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;blockquote&lt;/span&gt; &lt;span class="o"&gt;*)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;map-get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;$twcss&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gray-800&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// (vs. gray-700)&lt;/span&gt;
        &lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefers-color-scheme&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;map-get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;$twcss&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gray-200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// (vs. gray-300)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Now we deal with the blockquotes,&lt;/span&gt;
    &lt;span class="c1"&gt;// including links within them. Here,&lt;/span&gt;
    &lt;span class="c1"&gt;// we assign colors to boldfaced and&lt;/span&gt;
    &lt;span class="c1"&gt;// italicized text that **is** within&lt;/span&gt;
    &lt;span class="c1"&gt;// a blockquote but is **not** inside&lt;/span&gt;
    &lt;span class="c1"&gt;// a link. (We cover the same tags&lt;/span&gt;
    &lt;span class="c1"&gt;// and groupings as before.)&lt;/span&gt;
    &lt;span class="c1"&gt;//&lt;/span&gt;
    &lt;span class="c1"&gt;// In essence, we're giving this&lt;/span&gt;
    &lt;span class="c1"&gt;// normally grayed-out text some&lt;/span&gt;
    &lt;span class="c1"&gt;// color treatments to differ slightly&lt;/span&gt;
    &lt;span class="c1"&gt;// from usual blockquote styling.&lt;/span&gt;

    &lt;span class="nt"&gt;strong&lt;/span&gt;&lt;span class="nd"&gt;:is&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;blockquote&lt;/span&gt; &lt;span class="o"&gt;*)&lt;/span&gt;&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="o"&gt;*),&lt;/span&gt;
    &lt;span class="nt"&gt;b&lt;/span&gt;&lt;span class="nd"&gt;:is&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;blockquote&lt;/span&gt; &lt;span class="o"&gt;*)&lt;/span&gt;&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="o"&gt;*),&lt;/span&gt;
    &lt;span class="nt"&gt;em&lt;/span&gt;&lt;span class="nd"&gt;:is&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;blockquote&lt;/span&gt; &lt;span class="o"&gt;*)&lt;/span&gt;&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="o"&gt;*),&lt;/span&gt;
    &lt;span class="nt"&gt;i&lt;/span&gt;&lt;span class="nd"&gt;:is&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;blockquote&lt;/span&gt; &lt;span class="o"&gt;*)&lt;/span&gt;&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="o"&gt;*),&lt;/span&gt;
    &lt;span class="nt"&gt;em&lt;/span&gt; &lt;span class="nt"&gt;strong&lt;/span&gt;&lt;span class="nd"&gt;:is&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;blockquote&lt;/span&gt; &lt;span class="o"&gt;*)&lt;/span&gt;&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="o"&gt;*),&lt;/span&gt;
    &lt;span class="nt"&gt;strong&lt;/span&gt; &lt;span class="nt"&gt;em&lt;/span&gt;&lt;span class="nd"&gt;:is&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;blockquote&lt;/span&gt; &lt;span class="o"&gt;*)&lt;/span&gt;&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="o"&gt;*),&lt;/span&gt;
    &lt;span class="nt"&gt;i&lt;/span&gt; &lt;span class="nt"&gt;b&lt;/span&gt;&lt;span class="nd"&gt;:is&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;blockquote&lt;/span&gt; &lt;span class="o"&gt;*)&lt;/span&gt;&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="o"&gt;*),&lt;/span&gt;
    &lt;span class="nt"&gt;b&lt;/span&gt; &lt;span class="nt"&gt;i&lt;/span&gt;&lt;span class="nd"&gt;:is&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;blockquote&lt;/span&gt; &lt;span class="o"&gt;*)&lt;/span&gt;&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="o"&gt;*)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;map-get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;$twcss&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gray-650&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// (vs. gray-600)&lt;/span&gt;
        &lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefers-color-scheme&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;map-get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;$twcss&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gray-350&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// (vs. gray-400)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Would I want to fool with such spaghetti-ish stuff on a regular basis? Oh, my, no. But when you need it, you need it and, fortunately: (a.) &lt;a href="https://sass-lang.com/guide"&gt;any valid CSS works in Sass&lt;/a&gt;; and (b.) modern browsers’ CSS support &lt;a href="https://webkit.org/blog/3615/css-selectors-inside-selectors-discover-matches-not-and-nth-child/"&gt;has&lt;/a&gt; &lt;a href="https://hacks.mozilla.org/2020/12/and-now-for-firefox-84/"&gt;advanced&lt;/a&gt; &lt;a href="https://blog.chromium.org/2020/12/chrome-88-digital-goods-lighting.html"&gt;sufficiently&lt;/a&gt; over the years to handle situations like this one.&lt;/p&gt;

</description>
      <category>css</category>
      <category>scss</category>
      <category>styling</category>
      <category>fonts</category>
    </item>
    <item>
      <title>Hugo via npm?</title>
      <dc:creator>Bryce Wray</dc:creator>
      <pubDate>Mon, 20 Feb 2023 19:11:31 +0000</pubDate>
      <link>https://dev.to/brycewray/hugo-via-npm-5c4b</link>
      <guid>https://dev.to/brycewray/hugo-via-npm-5c4b</guid>
      <description>&lt;p&gt;&lt;strong&gt;Important&lt;/strong&gt;: Be sure to check the 2023-11-18 update at the end regarding a dependency issue with the Hugo Installer package described herein.&lt;/p&gt;

&lt;p&gt;During my years of using the &lt;a href="https://gohugo.io" rel="noopener noreferrer"&gt;Hugo&lt;/a&gt; &lt;a href="https://jamstack.org/generators" rel="noopener noreferrer"&gt;static site generator&lt;/a&gt; (SSG), I’ve occasionally seen mentions about how you could install, and even &lt;em&gt;run&lt;/em&gt;, Hugo’s &lt;a href="https://go.dev" rel="noopener noreferrer"&gt;Go&lt;/a&gt;-based binary by using one or more JavaScript packages sourced via &lt;a href="https://npmjs.com" rel="noopener noreferrer"&gt;npm&lt;/a&gt;. Having long ago understood the &lt;a href="https://gohugo.io/installation/" rel="noopener noreferrer"&gt;usual&lt;/a&gt;, very &lt;strong&gt;un&lt;/strong&gt;-npm-ish Hugo methods for installation — much less the un-npm-ish nature of Hugo use in general — I never bothered looking into these JS-based alternatives. Besides, I figured, how could they even work? And, if they did, in what use cases did they make more sense than  &lt;em&gt;normal&lt;/em&gt; Hugo use?&lt;/p&gt;

&lt;p&gt;But, yesterday, I began to grasp a bigger picture.&lt;/p&gt;

&lt;h2&gt;
  
  
  The point, and it does have one . . .
&lt;/h2&gt;

&lt;p&gt;While I was &lt;a href="https://www.brycewray.com/posts/2022/11/get-help-hugo-community-discord-server/" rel="noopener noreferrer"&gt;helping someone&lt;/a&gt; resolve an apparent problem with his own Hugo site, I saw that his chosen theme installed and employed Hugo through just such an approach. In this case, the npm package was &lt;a href="https://github.com/dominique-mueller/hugo-installer" rel="noopener noreferrer"&gt;Hugo Installer&lt;/a&gt;, created and maintained by &lt;a href="https://github.com/dominique-mueller" rel="noopener noreferrer"&gt;Dominique Müller&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;My usual reaction, bordering on disdain, soon gave way to my oft-cited nerdy curiosity, although I still had the same primary questions from those earlier encounters with references to an npm-style way to handle Hugo. Then, as I read up on Hugo Installer and how it was used in a variety of sites and themes, I started to get the point.&lt;/p&gt;

&lt;p&gt;Like the more venerable, &lt;a href="https://npmtrends.com/hugo-bin-vs-hugo-installer" rel="noopener noreferrer"&gt;more popular&lt;/a&gt;, but (in my opinion) less suitable &lt;a href="https://github.com/fenneclab/hugo-bin" rel="noopener noreferrer"&gt;hugo-bin&lt;/a&gt;, Hugo Installer makes it easier to manage Hugo use in projects already making thorough use of &lt;a href="https://nodejs.org" rel="noopener noreferrer"&gt;Node.js&lt;/a&gt; packages — which seems to describe a huge majority of the projects you’ll find on places like &lt;a href="https://github.com" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; and &lt;a href="https://gitlab.com" rel="noopener noreferrer"&gt;GitLab&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If cloning/forking a project, one usually does a simple &lt;code&gt;npm install&lt;/code&gt; to get all the goodies needed to make the project run either locally or on a remote host.&lt;/li&gt;
&lt;li&gt;In a multi-developer project, it’s important to keep everyone’s tools on the same versions through use of &lt;code&gt;package.json&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;On the other hand, it’s sometimes necessary for &lt;em&gt;separate&lt;/em&gt; projects on the same machine, or within a team’s machines, to use &lt;strong&gt;different&lt;/strong&gt; versions of one or more tools. For example, a dev or dev team might inherit a project that, for whatever reason, goes belly-up unless used with an older version of a specific tool.&lt;sup id="fnref1"&gt;1&lt;/sup&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, with Hugo Installer and appropriate scripting onboard, even a Hugo project can meet these requirements just as well as can a project anchored by a JS-based SSG.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;When run from a &lt;code&gt;package.json&lt;/code&gt; script, Hugo Installer checks for the presence of a Hugo binary — by default, in the project’s &lt;code&gt;bin/&lt;/code&gt; folder, although you can pick a different location — and, only if it doesn’t find the binary, downloads and installs a version, which you must specify. The check goes &lt;strong&gt;very&lt;/strong&gt; quickly and, thus, I suggest you make a &lt;code&gt;package.json&lt;/code&gt; script that does only the Hugo Installer part, and use it with your other Hugo-related scripts. Here are some examples, some of which use Müller’s &lt;a href="https://github.com/dominique-mueller/exec-bin" rel="noopener noreferrer"&gt;exec-bin&lt;/a&gt; package so the installed Hugo binary will run as you would expect:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"my-project"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"TBD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"config"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hugo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0.110.0"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"install:hugo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hugo-installer --version $npm_package_config_hugo --extended"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"dev:hugo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npm run install:hugo &amp;amp;&amp;amp; exec-bin bin/hugo/hugo server"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"prod:hugo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npm run install:hugo &amp;amp;&amp;amp; exec-bin bin/hugo/hugo --minify"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You’ll note my use of a &lt;code&gt;config&lt;/code&gt; object, in which I provide the desired Hugo version. This allows me a clear, easily identified place to change that spec at a moment’s notice whenever I wish. Down in the &lt;code&gt;install:hugo&lt;/code&gt; script, I call back to that specification by using &lt;code&gt;$npm_package_config_hugo&lt;/code&gt;.&lt;sup id="fnref2"&gt;2&lt;/sup&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Any sour points?
&lt;/h2&gt;

&lt;p&gt;Earlier, I cited this method’s advantages vs. “normal” Hugo. But are there comparative disadvantages?&lt;/p&gt;

&lt;p&gt;In my use so far, I’ve noticed only one: for anything you’d have normally done through a &lt;code&gt;hugo&lt;/code&gt; command from the terminal, you’ll have to change it to a script within the given project’s &lt;code&gt;package.json&lt;/code&gt;. Let’s say you want to recreate the &lt;code&gt;hugo new&lt;/code&gt; command for starting a new post, &lt;em&gt;e.g.&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;hugo new posts/2023/02/my-next-post.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In a project using Hugo Installer, you’d need a &lt;code&gt;package.json&lt;/code&gt; script like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"hugonew"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"exec-bin bin/hugo/hugo new"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you have that, the previous example would become:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run hugonew posts/2023/02/my-next-post.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;. . . which is hardly worth mentioning where one’s muscle memory is concerned. However, if you typically use a &lt;em&gt;lot&lt;/em&gt; of &lt;a href="https://gohugo.io/commands/" rel="noopener noreferrer"&gt;&lt;code&gt;hugo&lt;/code&gt; commands&lt;/a&gt; and thus would have to make a &lt;code&gt;package.json&lt;/code&gt; script for each, you’ll probably have a much different opinion about the whole thing.&lt;sup id="fnref3"&gt;3&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;Anyway, there you have it: a totally different way of installing and using Hugo.&lt;/p&gt;

&lt;p&gt;This month, I took &lt;a href="https://www.brycewray.com" rel="noopener noreferrer"&gt;my site&lt;/a&gt; squarely into npm-ville when I &lt;a href="https://www.brycewray.com/posts/2023/02/using-dart-sass-hugo-taking-it-easy/" rel="noopener noreferrer"&gt;brought in the npm version of Sass&lt;/a&gt; and &lt;a href="https://www.brycewray.com/posts/2023/02/some-future-now-css/" rel="noopener noreferrer"&gt;added PostCSS to make "future" CSS work with current browsers&lt;/a&gt;. As it turns out, those changes made my site an unexpectedly appropriate target for the use case that Hugo Installer presents. I’m sure I’ll find nits to pick over time but, for now, I’m impressed by what I’ve seen.&lt;/p&gt;




&lt;h2&gt;
  
  
  Update, 2023-11-18
&lt;/h2&gt;

&lt;p&gt;Due to a &lt;a href="https://github.com/dominique-mueller/hugo-installer/issues/57" rel="noopener noreferrer"&gt;vulnerability&lt;/a&gt; in one of the dependencies in the current version of Hugo Installer, be sure to add the following to your &lt;code&gt;package.json&lt;/code&gt; (and do an &lt;code&gt;npm install&lt;/code&gt; to install it, of course):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nl"&gt;"overrides"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"semver"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^7.5.4"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;Where Hugo is concerned, one possible scenario of this type would be if a project required the &lt;a href="https://github.com/russross/blackfriday" rel="noopener noreferrer"&gt;Blackfriday Markdown parser&lt;/a&gt;, Hugo’s support for which ended with the &lt;a href="https://github.com/gohugoio/hugo/releases/tag/v0.87.0" rel="noopener noreferrer"&gt;release of Hugo 0.87.0&lt;/a&gt; in August, 2021. For that matter, Hugo began transitioning away from Blackfriday with &lt;a href="https://github.com/gohugoio/hugo/releases/tag/v0.60.0" rel="noopener noreferrer"&gt;Hugo 0.60.0&lt;/a&gt; (November, 2019), when &lt;a href="https://github.com/yuin/goldmark/" rel="noopener noreferrer"&gt;Goldmark&lt;/a&gt; became Hugo’s default Markdown library. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;I encourage you to read &lt;a href="https://brianchildress.co/" rel="noopener noreferrer"&gt;Brian Childress&lt;/a&gt;’s 2018 article, “&lt;a href="https://brianchildress.co/variables-in-package-json/" rel="noopener noreferrer"&gt;Variables in package.json&lt;/a&gt;,” which was a great help in this aspect of the changeover. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;One other thing to keep in mind is that, if you already have a more conventional Hugo installation on your machine, especially a global installation, you'll probably want to uninstall it &lt;strong&gt;before&lt;/strong&gt; trying this method. Otherwise, you might not know whether Hugo Installer is working properly, because your machine may — and likely will — be using its previously installed Hugo version rather than the one you're trying to install and use. &lt;strong&gt;But&lt;/strong&gt;, if you have multiple Hugo projects on the machine, you’ll then have to use Hugo Installer with &lt;strong&gt;all&lt;/strong&gt; of them, and only you can decide is it’s worth it. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>alternative</category>
      <category>inclusion</category>
      <category>discuss</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Sweeter searches with Pagefind</title>
      <dc:creator>Bryce Wray</dc:creator>
      <pubDate>Thu, 08 Dec 2022 14:52:42 +0000</pubDate>
      <link>https://dev.to/brycewray/sweeter-searches-with-pagefind-194o</link>
      <guid>https://dev.to/brycewray/sweeter-searches-with-pagefind-194o</guid>
      <description>&lt;p&gt;If you lurk in some of the same tech forums that I do, you’ll see a lot of griping about the declining quality of online search. Who’d have ever thought that contorting search results, stuffing them with ads, inadequately curating them for quality, and otherwise caring only about their financial upside could have proved so counterproductive? (“Me,” you and I can yell in unison.)&lt;/p&gt;

&lt;p&gt;Fortunately, while there are limits to how much you’ll be able to improve your experience with online search in general, you &lt;em&gt;can&lt;/em&gt; optimize your own website’s search capabilities. That’s assuming, of course, that your website is built with a &lt;a href="https://jamstack.org/generators" rel="noopener noreferrer"&gt;static site generator&lt;/a&gt; (SSG), as I’ve recommended on &lt;a href="https://www.brycewray.com" rel="noopener noreferrer"&gt;my own website&lt;/a&gt; over the years, and &lt;em&gt;has&lt;/em&gt; search capabilities in the first place. If it lacks search, you can fix that readily enough with the free &lt;a href="https://pagefind.app" rel="noopener noreferrer"&gt;Pagefind&lt;/a&gt; tool about which I &lt;a href="https://www.brycewray.com/posts/2022/07/pagefind-quite-find-site-search/" rel="noopener noreferrer"&gt;wrote&lt;/a&gt; earlier this year.&lt;/p&gt;

&lt;p&gt;As newer versions of Pagefind appear, its powers grow; and one of those enhancements has enabled me to make &lt;a href="https://www.brycewray.com/search/" rel="noopener noreferrer"&gt;my site’s search results&lt;/a&gt; better — specifically, by &lt;em&gt;cutting out&lt;/em&gt; stuff which really didn’t belong. I’ll give a couple of examples herein, explaining the respective procedures for my two favorite SSGs, &lt;a href="https://11ty.dev" rel="noopener noreferrer"&gt;Eleventy&lt;/a&gt; and &lt;a href="https://gohugo.io" rel="noopener noreferrer"&gt;Hugo&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Taking out the trash
&lt;/h2&gt;

&lt;p&gt;Before I go there, however, let’s talk about exactly how you can make Pagefind exclude things you want excluded. Pagefind examines your site’s actual HTML, so you can use multiple methods to identify items for exclusion.&lt;/p&gt;

&lt;p&gt;First, you can add &lt;strong&gt;&lt;code&gt;data-pagefind-ignore&lt;/code&gt;&lt;/strong&gt; to something’s opening HTML tag. You’d do this in your chosen SSG’s templating. For example, I exclude my site's &lt;a href="https://www.brycewray.com/sitemap/" rel="noopener noreferrer"&gt;HTML sitemap&lt;/a&gt; with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sitemap-Container"&lt;/span&gt; &lt;span class="na"&gt;data-pagefind-ignore&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While the &lt;code&gt;data-pagefind-ignore&lt;/code&gt; exclusion capability was in Pagefind from the beginning, a more recent enhancement has been the addition of the &lt;a href="https://pagefind.app/docs/config-options/#exclude-selectors" rel="noopener noreferrer"&gt;&lt;strong&gt;&lt;code&gt;exclude_selectors&lt;/code&gt;&lt;/strong&gt; instruction&lt;/a&gt; for use in the &lt;a href="https://pagefind.app/docs/config-sources/" rel="noopener noreferrer"&gt;site-wide Pagefind configuration file&lt;/a&gt; (in my repos’ case, that's &lt;code&gt;pagefind.yml&lt;/code&gt;). As I’ll show you below, it gives you the power to exclude items even when you can’t directly edit the HTML as you can in templates.&lt;/p&gt;

&lt;h3&gt;
  
  
  Footnote references
&lt;/h3&gt;

&lt;p&gt;It’s no secret to my regular readers that I like to use footnotes. The problem that presented for Pagefind search results was the appearance of numerical &lt;em&gt;footnote references&lt;/em&gt;. These are the superscripted numbers that go in the main text and link down to the actual footnotes. Without dealing with these footnote references, one might see things like this in search results:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This1 is where a footnote appears in the main text.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;. . . since there’d been a &lt;sup&gt;1&lt;/sup&gt; next to &lt;em&gt;This&lt;/em&gt; in the original sentence that Pagefind had located.&lt;/p&gt;

&lt;p&gt;Fixing this is where &lt;code&gt;exclude_selectors&lt;/code&gt; comes into play, since writing your main text in Markdown doesn’t allow you to edit footnotes' HTML directly.&lt;sup id="fnref1"&gt;1&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;In Eleventy, a footnote’s HTML begins like this if one is using the most typical footnotes-ready Markdown setup, &lt;em&gt;i.e.&lt;/em&gt;, the &lt;a href="https://github.com/markdown-it/markdown-it" rel="noopener noreferrer"&gt;&lt;code&gt;markdown-it&lt;/code&gt; parser&lt;/a&gt; combined with the &lt;a href="https://github.com/markdown-it/markdown-it-footnote" rel="noopener noreferrer"&gt;&lt;code&gt;markdown-it-footnote&lt;/code&gt; plugin&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;sup&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"footnote-ref"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;. . . so you’d put this in an Eleventy repo’s &lt;code&gt;pagefind.yml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;exclude_selectors&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[class='footnote-ref']"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As for Hugo with its built-in &lt;a href="https://github.com/yuin/goldmark" rel="noopener noreferrer"&gt;goldmark Markdown parser&lt;/a&gt; and included Footnote extension, a footnote’s HTML begins like this (here, it’s the first footnote in a page):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;sup&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"fnref:1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;. . . thus needing the following for a Hugo repo’s &lt;code&gt;pagefind.yml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;exclude_selectors&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[id^='fnref']"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Code blocks
&lt;/h3&gt;

&lt;p&gt;As this post shows, I also like to include &lt;em&gt;code blocks&lt;/em&gt;, and on occasion they can be, um, &lt;em&gt;large&lt;/em&gt;. You might think it would be a good idea to include those in site searches, but my experience has been that they get in the way of more plain-English results. After all, code blocks can have many ordinary words for which someone might be searching but expecting very &lt;em&gt;un&lt;/em&gt;-code-block-ish results. Since I annotate many of my code blocks with comments-as-documentation, this can further exacerbate the potential problem.&lt;/p&gt;

&lt;p&gt;Thus, we once again employ &lt;code&gt;exclude_selectors&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In Eleventy — assuming one is using the &lt;a href="https://www.11ty.dev/docs/plugins/syntaxhighlight/" rel="noopener noreferrer"&gt;official Eleventy syntax highlighting plugin&lt;/a&gt;, as I would recommend — a code block begins with something like this (here, the code itself is JavaScript, since the &lt;code&gt;class&lt;/code&gt; varies according to the language being highlighted):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;pre&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"language-js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;. . . so now our Eleventy &lt;code&gt;pagefind.yml&lt;/code&gt; will have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;exclude_selectors&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[class='footnote-ref']"&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[class^='language']"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In Hugo and its built-in &lt;a href="https://github.com/alecthomas/chroma" rel="noopener noreferrer"&gt;Chroma syntax highlighting&lt;/a&gt;, a code block begins with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;pre&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"highlight"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;. . . which means our Hugo &lt;code&gt;pagefind.yml&lt;/code&gt; now needs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;exclude_selectors&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[id^='fnref']"&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[class='highlight']"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;








&lt;p&gt;&lt;em&gt;For more information on managing how Pagefind provides search results for your visitors, consult the &lt;a href="https://pagefind.app/" rel="noopener noreferrer"&gt;Pagefind documentation&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;Well, technically, you could do so &lt;em&gt;if&lt;/em&gt; you did your footnotes completely manually — since you can mix HTML in your Markdown — but that would be a major pain &lt;em&gt;and&lt;/em&gt; completely waste the exemplary footnote-handling likely offered by your SSG. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>staticsitegenerator</category>
      <category>search</category>
      <category>pagefind</category>
    </item>
    <item>
      <title>Tailwind CSS v3.2: revisiting my “feature creep” warning</title>
      <dc:creator>Bryce Wray</dc:creator>
      <pubDate>Sat, 22 Oct 2022 13:43:00 +0000</pubDate>
      <link>https://dev.to/brycewray/tailwind-css-v32-revisiting-my-feature-creep-warning-3ohm</link>
      <guid>https://dev.to/brycewray/tailwind-css-v32-revisiting-my-feature-creep-warning-3ohm</guid>
      <description>&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: This also was the subject of a &lt;a href="https://news.ycombinator.com/item?id=33298806"&gt;Hacker News thread&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Earlier this week, a &lt;a href="https://tailwindcss.com/blog/tailwindcss-v3-2"&gt;blog post&lt;/a&gt; introduced &lt;a href="https://github.com/tailwindlabs/tailwindcss/releases/tag/v3.2.0"&gt;version 3.2&lt;/a&gt; of the popular &lt;a href="https://tailwindcss.com"&gt;Tailwind CSS&lt;/a&gt; styling framework.&lt;/p&gt;

&lt;p&gt;The “absolutely &lt;em&gt;massive&lt;/em&gt; amount of new stuff” the post trumpeted includes:&lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Multiple config files in one project using &lt;code&gt;@config&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Browser-support-based styling with &lt;code&gt;supports-*&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;ARIA attribute variants&lt;/li&gt;
&lt;li&gt;Data attribute variants&lt;/li&gt;
&lt;li&gt;Max-width and dynamic breakpoints&lt;/li&gt;
&lt;li&gt;Dynamic &lt;code&gt;group-*&lt;/code&gt; and &lt;code&gt;peer-*&lt;/code&gt; variants&lt;/li&gt;
&lt;li&gt;Dynamic variants with &lt;code&gt;matchVariant&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Nested &lt;code&gt;group&lt;/code&gt; and multiple &lt;code&gt;peer&lt;/code&gt; support using variant modifiers&lt;/li&gt;
&lt;li&gt;Container queries&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;. . . the details of which all sound impressive, to be sure. However, as I read about them, I was reminded of something I wrote near the end of the “&lt;a href="https://www.brycewray.com/posts/2022/01/should-you-adopt-tailwind-3"&gt;Should you adopt Tailwind 3?&lt;/a&gt;” piece I did back in January for the &lt;a href="https://www.stackbit.com/blog/"&gt;Stackbit blog&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Because of Tailwind’s need to stay not simply relevant but also popular, the project is particularly vulnerable to the dangers of &lt;em&gt;feature creep&lt;/em&gt;.  . . . [R]emember that the idea behind Tailwind, like every other utility-first CSS framework, is to &lt;em&gt;make styling easier&lt;/em&gt;, especially for front-end developers who dislike getting under CSS’s hood. The more capabilities that get added to Tailwind, the more complex Tailwind becomes. It may not yet be near a tipping point, but that’s a danger for which the Tailwind team will have to be on the lookout.&lt;sup id="fnref1"&gt;1&lt;/sup&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That was nine months ago. Although I surely wasn’t expecting to be proven right this soon, all the new features in this v3.2 release of Tailwind, especially on top of the many other additions to Tailwind over the last two years in particular, add up to a project that now does seem, uh, a bit much.&lt;/p&gt;

&lt;p&gt;I’ll cut to the chase for all the aforementioned “front-end developers who dislike getting under CSS’s hood”&lt;sup id="fnref2"&gt;2&lt;/sup&gt; . . .&lt;/p&gt;

&lt;p&gt;The more features that get added to Tailwind, the more you have to &lt;strong&gt;know&lt;/strong&gt; about CSS before you can &lt;strong&gt;use&lt;/strong&gt; those features. Right? So why not just bite the bullet and learn to use CSS &lt;strong&gt;without&lt;/strong&gt; the additional tooling (and weird, often lengthy additions to your HTML) that Tailwind and other utility-first styling frameworks require?&lt;sup id="fnref3"&gt;3&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;That said&lt;/strong&gt;: if you insist on using Tailwind because you utterly can’t bear to deal with vanilla CSS (although &lt;a href="https://web.dev/state-of-css-2022/"&gt;it’s not really all that vanilla anymore&lt;/a&gt;), Tailwind v3.2 provides quite a bit more to cram into your ditty bag of styling powers. You’ll have to decide when it reaches that inflection point where its use consumes more time and brainpower than it saves.&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;This includes a few edits for my site's style, as opposed to how it appeared both in the original form on the Stackbit blog and its contractually required re-publication on my site. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;And those who manage them, given that Tailwind is especially popular as a way of enforcing styles among corporate development teams — where CSS expertise can vary widely. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;For my own site, I’ve used Tailwind off-and-on over the years, but kept returning to &lt;a href="https://sass-lang.com"&gt;Sass/SCSS&lt;/a&gt; as a &lt;a href="https://www.brycewray.com/posts/2021/04/speaking-up-for-sass/"&gt;more comfortable, bullet-proof styling method&lt;/a&gt; &lt;em&gt;vs.&lt;/em&gt; what utility-first frameworks entail. (Then again, I’ve been working “under CSS’s hood” for 20 years, so there’s that.) ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>tailwindcss</category>
      <category>css</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Simplifying switcheroos</title>
      <dc:creator>Bryce Wray</dc:creator>
      <pubDate>Fri, 23 Sep 2022 14:25:43 +0000</pubDate>
      <link>https://dev.to/brycewray/simplifying-switcheroos-4m2f</link>
      <guid>https://dev.to/brycewray/simplifying-switcheroos-4m2f</guid>
      <description>&lt;p&gt;It’s a web meme that began as &lt;a href="https://www.imdb.com/title/tt0107290/quotes/qt1464414"&gt;a line from &lt;em&gt;Jurassic Park&lt;/em&gt;&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;. . . your scientists were so preoccupied with whether or not they &lt;em&gt;could&lt;/em&gt; that they didn't stop to think if they &lt;em&gt;should&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Well, the other day, my oft-mentioned enthusiasm for switching &lt;a href="https://www.brycewray.com/"&gt;my site&lt;/a&gt; among various static site generators (SSGs) manifested itself in an idea that I immediately thought was so cool that I had to make it happen.&lt;/p&gt;

&lt;p&gt;And, no, I didn’t stop to think if I should.&lt;/p&gt;

&lt;p&gt;But the thing is, I’ve heard from and read about others out there who, as do I, like to perform these switcheroos in which they move their sites from one SSG to another. It’s not just &lt;em&gt;moi&lt;/em&gt;. So I thought I’d write up the whole thing, just in case my fellow migratory nerds out there might find it useful.&lt;/p&gt;

&lt;h2&gt;
  
  
  The former way
&lt;/h2&gt;

&lt;p&gt;First, here’s a brief summary of how, &lt;em&gt;before&lt;/em&gt; now, I would perform such site moves. The term &lt;em&gt;project&lt;/em&gt; below refers to a website instance in my web host of choice, &lt;a href="https://pages.cloudflare.com"&gt;Cloudflare Pages&lt;/a&gt;. Terms vary from host to host (&lt;em&gt;e.g.&lt;/em&gt;, &lt;a href="https://vercel.com"&gt;Vercel&lt;/a&gt; also calls them &lt;em&gt;projects&lt;/em&gt;, while &lt;a href="https://netlify.com"&gt;Netlify&lt;/a&gt; calls them &lt;em&gt;sites&lt;/em&gt;), but you understand what I mean.&lt;/p&gt;

&lt;p&gt;For each SSG of interest, I’d have my site's content in a project. Of course, only one project at a time could be &lt;em&gt;the&lt;/em&gt; site — we’ll call it the &lt;em&gt;Site Project&lt;/em&gt; — and so only that one was set to receive traffic for my &lt;em&gt;brycewray.com&lt;/em&gt; domain, unlike however many &lt;em&gt;Non-site Projects&lt;/em&gt; I might be maintaining.&lt;/p&gt;

&lt;p&gt;When I chose to do a switcheroo, I’d have to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go into the Site Project and disable its settings for that custom domain, &lt;em&gt;thus taking my site offline&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Go into the Non-site Project to which I wanted to move the site and set &lt;em&gt;it&lt;/em&gt; up for that custom domain.&lt;/li&gt;
&lt;li&gt;Wait until the host “blessed” the change, at which time the switcheroo would become official and my site would be online again.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While experience born of sheer repetition allowed me to do the first two parts in well under a minute, and the subsequent “blessing” usually&lt;sup id="fnref1"&gt;1&lt;/sup&gt; occurred almost as quickly, sometimes Stuff Would Happen and my site would be offline for minutes.&lt;/p&gt;

&lt;p&gt;It’s not as if my site is mission-critical to humanity or anything like that, but I still was annoyed by that (usually) brief period of offline-ness. I was sure there had to be a better way — other than the obvious choice of &lt;em&gt;never&lt;/em&gt; migrating, that is.&lt;/p&gt;

&lt;p&gt;This is where my idea comes into play.&lt;/p&gt;

&lt;h2&gt;
  
  
  The new way
&lt;/h2&gt;

&lt;p&gt;It was obvious that the failure point was in the domain-switching part. The just-as-obvious answer was that I would simply &lt;strong&gt;not&lt;/strong&gt; switch the domain. Instead, I’d set up just &lt;strong&gt;one&lt;/strong&gt; project, to which the domain would always be assigned, and point &lt;strong&gt;different&lt;/strong&gt; repos to that project.&lt;/p&gt;

&lt;p&gt;“But, wait,” you say, “even that repo switch can involve some period of down-time, right?”&lt;/p&gt;

&lt;p&gt;Yes, &lt;em&gt;if&lt;/em&gt; you do the switchover through the host’s GUI.&lt;/p&gt;

&lt;p&gt;But what if you &lt;strong&gt;don’t&lt;/strong&gt;?&lt;/p&gt;

&lt;p&gt;What if you do it completely through &lt;a href="https://www.infoworld.com/article/3271126/what-is-cicd-continuous-integration-and-continuous-delivery-explained.html"&gt;CI/CD&lt;/a&gt;?&lt;/p&gt;

&lt;p&gt;Bingo.&lt;/p&gt;

&lt;p&gt;So here’s what I did.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I created a new project, called &lt;code&gt;static-site&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;For &lt;code&gt;static-site&lt;/code&gt;’s &lt;em&gt;default&lt;/em&gt; content — the repo that the host's GUI assigns to it via the usual Git connection — I assigned a for-testing-only repo which, on publication, has just a couple of minimal pages and nothing more.&lt;sup id="fnref2"&gt;2&lt;/sup&gt;
&lt;/li&gt;
&lt;li&gt;In &lt;em&gt;each&lt;/em&gt; SSG-specific repo (&lt;em&gt;i.e.&lt;/em&gt;, each repo with my site’s real content and using a specific SSG of interest), I created a GitHub Action (GHA) that would publish to &lt;code&gt;static-site&lt;/code&gt;, and thus the site.&lt;/li&gt;
&lt;li&gt;Then — and this is how the whole trick works --- I make sure that &lt;em&gt;only&lt;/em&gt; the SSG-specific repo I &lt;em&gt;want&lt;/em&gt; to use has its GHA in the right location (&lt;code&gt;.github/workflows/&lt;/code&gt;) so it’ll work, while each other SSG-specific repo has its GHA in a standby location which GitHub won't “see” as a trigger point.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This way, I can work with and push commits to each remote repo as much as I want, but &lt;em&gt;only&lt;/em&gt; the one I want to be the site repo at any given time will actually have its content published to the website on each push.&lt;/p&gt;

&lt;p&gt;Thus, I can effortlessly, &lt;em&gt;instantly&lt;/em&gt; switch my site between &lt;a href="https://11ty.dev"&gt;Eleventy&lt;/a&gt; and &lt;a href="https://gohugo.io"&gt;Hugo&lt;/a&gt;, or between Hugo and &lt;a href="https://astro.build"&gt;Astro&lt;/a&gt;, or between Astro and . . . well, you see.&lt;sup id="fnref3"&gt;3&lt;/sup&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The proverbial “devil’s workshop”
&lt;/h2&gt;

&lt;p&gt;Whether anyone else might ever have a use for such a method, I obviously can’t say, but it works for me. If you can find value in it, there you are.&lt;/p&gt;

&lt;p&gt;And, if not: well, try not to pity me &lt;em&gt;too&lt;/em&gt; awfully, okay?&lt;/p&gt;

&lt;p&gt;Besides, it could’ve been worse: I had a fleeting thought of trying to come up with a &lt;a href="https://en.wikipedia.org/wiki/Monorepo"&gt;&lt;strong&gt;monorepo&lt;/strong&gt;&lt;/a&gt; with &lt;em&gt;multiple&lt;/em&gt; SSGs and their respective configurations, file/folder “lookup orders,” &lt;em&gt;etc.&lt;/em&gt; — so I could share just one set of content among all the SSGs, making switcheroos that much easier. At least I didn’t go &lt;em&gt;there&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Well . . . not yet, anyway. &lt;em&gt;[Evil laugh.]&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Later note&lt;/strong&gt;: For those who may be morbidly curious about that last item, be advised that, my curiosity too much to bear, I &lt;em&gt;did&lt;/em&gt; attempt just such a monorepo-based approach. While I got it to work with very bare-bones-ish content, I couldn’t determine how to make different SSGs respect — or, more accurately, ignore — each others’ components, shortcodes, &lt;em&gt;etc.&lt;/em&gt; (at least without show-stopping errors). That’s where the mad-scientist-level experiment ended.&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;More often than not, it’s a function of how quickly the host can auto-issue a new SSL certificate so the site will have that &lt;code&gt;https:&lt;/code&gt; goodness. Under normal circumstances, the cert is issued within a minute. In my case, I get it for both &lt;em&gt;brycewray.com&lt;/em&gt; and &lt;em&gt;&lt;a href="http://www.brycewray.com"&gt;www.brycewray.com&lt;/a&gt;&lt;/em&gt;, but these happen sufficiently simultaneously that the doubling-up usually isn’t a factor. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;Why did I even bother with that? Because it’s the easiest, quickest, least glitchy way of getting the &lt;code&gt;static-site&lt;/code&gt; project online at all. I’ve not yet found a way to &lt;em&gt;initiate&lt;/em&gt; such a project strictly via CI/CD without first setting up at least a bare-bones presence via the host’s GUI — mainly because there are certain parameters that I can’t even put in CI/CD until the project exists. It’s a chicken/egg thing. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;One drawback to such switches — one which will probably keep me from executing them in &lt;em&gt;too&lt;/em&gt; willy-nilly a fashion — is that they tend to re-send recent entries in the site’s &lt;a href="https://en.wikipedia.org/wiki/RSS"&gt;RSS&lt;/a&gt; and &lt;a href="https://jsonfeed.org/"&gt;JSON&lt;/a&gt; feeds. I’m sure there’s a way to avoid that, perhaps through working to make each SSG’s feed output as identical as possible, but I haven’t yet found it. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>static</category>
      <category>webdev</category>
      <category>ssg</category>
      <category>cicd</category>
    </item>
    <item>
      <title>Cache-busting in Eleventy: a simpler way with Sass</title>
      <dc:creator>Bryce Wray</dc:creator>
      <pubDate>Tue, 20 Sep 2022 10:00:37 +0000</pubDate>
      <link>https://dev.to/brycewray/cache-busting-in-eleventy-a-simpler-way-with-sass-10h3</link>
      <guid>https://dev.to/brycewray/cache-busting-in-eleventy-a-simpler-way-with-sass-10h3</guid>
      <description>&lt;p&gt;If you use the &lt;a href="https://11ty.dev"&gt;Eleventy&lt;/a&gt; static site generator (SSG) and &lt;a href="https://sass-lang.com"&gt;Sass&lt;/a&gt; to build your website, read on for some cool stuff.&lt;/p&gt;

&lt;p&gt;Back in late 2020, I &lt;a href="https://www.brycewray.com/posts/2020/11/using-postcss-cache-busting-eleventy/"&gt;wrote&lt;/a&gt; &lt;a href="https://www.brycewray.com/posts/2020/12/cache-busting-eleventy-take-two/"&gt;three&lt;/a&gt; &lt;a href="https://www.brycewray.com/posts/2020/12/hashing-out-cache-busting-fix-eleventy/"&gt;posts&lt;/a&gt; about how to perform “cache-busting” on an Eleventy site’s CSS, which ensures the vast majority of web browsers will reliably show your most up-to-date styling. In the end, I suggested a method that accomplished this through a combination of &lt;code&gt;package.json&lt;/code&gt; scripting and build-time JavaScript runs. It worked, but it was somewhat of a time drag during dev mode. Besides, it’s a lot clunkier than how one can do it in &lt;a href="https://gohugo.io"&gt;Hugo&lt;/a&gt; via that SSG’s built-in asset pipeline, &lt;a href="https://gohugo.io/hugo-pipes/scss-sass/"&gt;Hugo Pipes&lt;/a&gt;. Thus, I’ve continued to look for a better way.&lt;/p&gt;

&lt;p&gt;Now, I’ve found it.&lt;/p&gt;

&lt;p&gt;It comes in the form of two recently introduced Eleventy plugins by &lt;a href="https://github.com/kentaroi"&gt;Kentaro Imai&lt;/a&gt;: &lt;a href="https://github.com/kentaroi/eleventy-sass"&gt;&lt;code&gt;eleventy-sass&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://github.com/kentaroi/eleventy-plugin-rev"&gt;&lt;code&gt;eleventy-plugin-rev&lt;/code&gt;&lt;/a&gt;. The former provides Sass-to-CSS compilation, and the latter adds filters you can access to provide &lt;em&gt;versioning&lt;/em&gt;-based filename changes for cache-busting.&lt;/p&gt;

&lt;p&gt;It’s as simple as this. First, go to your Eleventy project directory and install the two plugins from npm:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm i -D eleventy-sass eleventy-plugin-rev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then register them in the project’s config file, usually a top-level &lt;code&gt;.eleventy.js&lt;/code&gt; file, as in this simplified example (see the &lt;a href="https://github.com/kentaroi/eleventy-sass"&gt;&lt;code&gt;eleventy-sass&lt;/code&gt; documentation&lt;/a&gt; for more details about available options):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pluginRev&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;eleventy-plugin-rev&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;eleventySass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;eleventy-sass&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;eleventyConfig&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="nx"&gt;eleventyConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addPlugin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pluginRev&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;eleventyConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addPlugin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;eleventySass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;rev&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="cm"&gt;/*
    other Eleventy config as usual...
  */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, add the following to your template for the site-wide &lt;code&gt;head&lt;/code&gt; tag (the following assumes you have an &lt;code&gt;index.scss&lt;/code&gt; file within &lt;code&gt;src/styles/&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"{{ "&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;styles&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;index.css&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt; &lt;span class="err"&gt;|&lt;/span&gt; &lt;span class="na"&gt;rev&lt;/span&gt; &lt;span class="err"&gt;}}"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text/css"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s all!&lt;/p&gt;

&lt;p&gt;Compared to my method from 2020, this requires &lt;strong&gt;no&lt;/strong&gt; weird gyrations in &lt;code&gt;package.json&lt;/code&gt; and &lt;strong&gt;no&lt;/strong&gt; build-time machinations in JavaScript. It’s so much cleaner and simpler.&lt;/p&gt;

&lt;p&gt;Now, on each run of Eleventy, the combo of &lt;code&gt;eleventy-sass&lt;/code&gt; and &lt;code&gt;eleventy-plugin-rev&lt;/code&gt; will create a CSS file with a hashed filename that changes every time you make a change to &lt;code&gt;index.scss&lt;/code&gt; &lt;strong&gt;or&lt;/strong&gt; any &lt;a href="https://sass-lang.com/guide#topic-4"&gt;partials&lt;/a&gt; that &lt;code&gt;index.scss&lt;/code&gt; might &lt;code&gt;@use&lt;/code&gt;. (And you’ll want to &lt;code&gt;@use&lt;/code&gt;, &lt;strong&gt;not&lt;/strong&gt; &lt;code&gt;@import&lt;/code&gt;, because the Sass-to-CSS compilation in &lt;code&gt;eleventy-sass&lt;/code&gt; is done with &lt;a href="https://sass-lang.com/dart-sass"&gt;Dart Sass&lt;/a&gt;, rather than the &lt;a href="https://sass-lang.com/blog/libsass-is-deprecated"&gt;deprecated&lt;/a&gt; LibSass.)&lt;/p&gt;

&lt;p&gt;Incidentally: in my own testing of this setup in dev mode, Eleventy’s &lt;code&gt;--incremental&lt;/code&gt; flag keeps the dev server’s refresh functionality from calling the renamed CSS file when you alter the SCSS (and therefore cause that renaming to occur). Also, I found this to be true with both Eleventy 1.x and the still-in-development Eleventy 2.x; so I’d suggest not using &lt;code&gt;--incremental&lt;/code&gt; if you don’t want to have to do a dev server refresh every time you make an SCSS change.&lt;/p&gt;

&lt;p&gt;That little gripe aside, I encourage all Eleventy/Sass users to consider adopting this quick config improvement, courtesy of some neat coding by Kentaro Imai. It’s ’waaay cleaner than any other Sass-using, cache-busting solution for Eleventy that I’ve yet encountered, and you can have it up and running in just a few minutes.&lt;/p&gt;

</description>
      <category>eleventy</category>
      <category>sass</category>
      <category>cache</category>
      <category>css</category>
    </item>
    <item>
      <title>Get good Git info from Eleventy, too</title>
      <dc:creator>Bryce Wray</dc:creator>
      <pubDate>Mon, 05 Sep 2022 15:54:55 +0000</pubDate>
      <link>https://dev.to/brycewray/get-good-git-info-from-eleventy-too-30in</link>
      <guid>https://dev.to/brycewray/get-good-git-info-from-eleventy-too-30in</guid>
      <description>&lt;p&gt;In “&lt;a href="https://www.brycewray.com/posts/2022/06/get-good-git-info-hugo/"&gt;Get good Git info from Hugo&lt;/a&gt;,” I explained how to use the &lt;a href="https://gohugo.io"&gt;Hugo&lt;/a&gt; static site generator (SSG)’s built-in &lt;a href="https://gohugo.io/variables/git"&gt;Git info variables&lt;/a&gt; to display page-by-page &lt;a href="https://git-scm.com/docs/git-commit"&gt;Git commit&lt;/a&gt; data for one’s static website. Well, lo and behold, you can get that kind of information in the &lt;a href="https://11ty.dev"&gt;Eleventy&lt;/a&gt; SSG, too. You just need to add a little code, in the form of a &lt;a href="https://www.11ty.dev/docs/shortcodes/"&gt;shortcode&lt;/a&gt; that takes advantage of &lt;a href="https://www.11ty.dev/docs/data-eleventy-supplied/"&gt;Eleventy-supplied data&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  It requires CI/CD
&lt;/h2&gt;

&lt;p&gt;The one potential kink to this for you is that, in order for this to work best, you’ll have to use a &lt;a href="https://www.infoworld.com/article/3271126/what-is-cicd-continuous-integration-and-continuous-delivery-explained.html"&gt;CI/CD&lt;/a&gt; method of deploying your site. For anybody likely to find this article, you’ll typically be using either &lt;a href="https://github.com/features/actions"&gt;GitHub Actions&lt;/a&gt; or &lt;a href="https://docs.gitlab.com/ee/ci/"&gt;GitLab CI/CD&lt;/a&gt;. The reason this is necessary is because this method uses &lt;a href="https://git-scm.com/docs/git-log"&gt;&lt;code&gt;git log&lt;/code&gt;&lt;/a&gt;, and — at least as far as I know — there's no way to use any of the Jamstack-savvy web hosts’ UIs to specify &lt;code&gt;fetch-depth: 0&lt;/code&gt;, &lt;a href="https://discourse.gohugo.io/t/problems-with-gitinfo-in-ci/22480"&gt;which is necessary for this to work&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It’s outside the scope of this article to explain the CI/CD part, but I’ve written other posts about it (see my &lt;a href="https://www.brycewray.com/search/"&gt;search page&lt;/a&gt;, especially regarding GitHub Actions) which you may find useful in pressing ahead with it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The shortcode: go easy
&lt;/h2&gt;

&lt;p&gt;In putting together this shortcode, we’ll follow the advice I gave in “&lt;a href="https://www.brycewray.com/posts/2022/09/take-load-off/"&gt;Take a load off&lt;/a&gt;” about how &lt;strong&gt;not&lt;/strong&gt; to overtax your development environment. While that article was about limiting accesses of remote APIs, it also applies to getting Git data. This is because &lt;code&gt;git log&lt;/code&gt; is notorious for its potentially high impact on local device performance; it was built to be comprehensive, not to save CPU cycles. Thus, we’ll set the shortcode, which I call &lt;code&gt;gitinfo.js&lt;/code&gt;, so that it does the heavy lifting only in production.&lt;/p&gt;

&lt;p&gt;Of course, be sure to enable &lt;code&gt;gitinfo.js&lt;/code&gt; (or whatever you choose to call it) in your Eleventy config file through the &lt;a href="https://www.11ty.dev/docs/shortcodes/"&gt;usual procedure&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Note that &lt;code&gt;gitinfo.js&lt;/code&gt; assumes you have installed the &lt;a href="https://github.com/moment/luxon"&gt;&lt;code&gt;luxon&lt;/code&gt;&lt;/a&gt; package, to be used here for formatting dates from Git commit data. (On the other hand, &lt;code&gt;child_process&lt;/code&gt; is included in &lt;a href="https://nodejs.org"&gt;Node.js&lt;/a&gt;.) Notice also that you’ll have to supply, in the &lt;code&gt;repoLink&lt;/code&gt; variable, the URL for your online repo’s commits.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;DateTime&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;luxon&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;childProcess&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;child_process&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NODE_ENV&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pubdate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;stringToRet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;``&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;production&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;repoLink&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;``&lt;/span&gt;
        &lt;span class="cm"&gt;/* ================
            For `repoLink`, fill in the starting URL
            for commits to your project's online repo!
            If you use GitHub, it'll usually be 
            in the format of:
            https://github.com/your-github-name/your-repo-name/commit/
        ================ */&lt;/span&gt;
        &lt;span class="nx"&gt;pubdate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fromJSDate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pubdate&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toFormat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;yyyy-MM-dd&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lastUpdatedFromGit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
            &lt;span class="nx"&gt;childProcess&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;execSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`git log -1 --format=%cd --date=short &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;abbrevHash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
            &lt;span class="nx"&gt;childProcess&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;execSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`git log -1 --pretty=format:"%h" &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;longHash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="nx"&gt;childProcess&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;execSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`git log -1 --pretty=format:"%H" &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="nx"&gt;repoLink&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;longHash&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;longHash&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;stringToRet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`Latest commit: &amp;lt;a class="mono" href="&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;repoLink&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" rel="noopener"&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;abbrevHash&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/a&amp;gt;`&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pubdate&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;lastUpdatedFromGit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;stringToRet&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s2"&gt;`, &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lastUpdatedFromGit&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;stringToRet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&amp;amp;nbsp;`&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;stringToRet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`[Git info will appear here in production.]`&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;stringToRet&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You may have noticed that the shortcode took two parameters: &lt;code&gt;pubdate&lt;/code&gt; and &lt;code&gt;filename&lt;/code&gt;. Here’s where the Eleventy-supplied data comes in. Wherever you want the Git data to occur, use the &lt;code&gt;gitinfo&lt;/code&gt; shortcode as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;{% gitinfo page.date, page.inputPath %}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will automatically feed to &lt;code&gt;gitinfo&lt;/code&gt; a Markdown file’s &lt;code&gt;date&lt;/code&gt; (derived by default from the file’s front matter but, if it’s not there, &lt;a href="https://www.11ty.dev/docs/dates/"&gt;Eleventy has fallbacks&lt;/a&gt;) and &lt;code&gt;inputPath&lt;/code&gt;. In a typical Eleventy repo, &lt;code&gt;inputPath&lt;/code&gt; takes this form:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./src/path/to/my-markdown-file.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As for the final message conveyed in your site by &lt;code&gt;stringToRet&lt;/code&gt;, you’re obviously free to make &lt;code&gt;stringToRet&lt;/code&gt; say what works best for your site. The main thing is that the code here shows you how to get the data that it needs in the first place. And, to be specific about that data, in production the &lt;code&gt;gitinfo&lt;/code&gt; shortcode invocation brings back a string containing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;lastUpdatedFromGit&lt;/code&gt; — The date of the file’s last Git commit in &lt;code&gt;yyyy-mm-dd&lt;/code&gt; format. (If this is on the same day as the initial date of publication, &lt;code&gt;lastUpdatedFromGit&lt;/code&gt; doesn’t appear.)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;abbrevHash&lt;/code&gt; and &lt;code&gt;longHash&lt;/code&gt; — The short and long versions, respectively, of the commit’s &lt;a href="https://www.mikestreety.co.uk/blog/the-git-commit-hash/"&gt;hash&lt;/a&gt;. The returned string combines &lt;code&gt;repoLink&lt;/code&gt; with &lt;code&gt;longHash&lt;/code&gt; to produce the URL to which it then links &lt;code&gt;abbrevHash&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you typically show each page’s initial publication date and last-modified date, you &lt;em&gt;can&lt;/em&gt; choose to avoid worrying about that latter part from here on by just using &lt;code&gt;gitinfo&lt;/code&gt;’s output, instead. (Some prefer to provide the last-modified info manually, to indicate only true content changes; by contrast, &lt;code&gt;gitinfo&lt;/code&gt; can report only the last time the file was changed/committed at all, even if just to eliminate a totally invisible empty line someplace.)&lt;/p&gt;

&lt;p&gt;So, while it’s indeed cool that Hugo wields Git info-handling prowess out of the box, this shortcode gives Eleventy the same powers. (I’ll avoid the “great responsibility” trope.) Perhaps you can make good use of them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Acknowledgements and related material
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Zachary Betz, “&lt;a href="https://zwbetz.com/create-react-app-show-current-git-branch-and-commit-hash-from-any-os/"&gt;Create React App: Show Current Git Branch and Commit Hash From Any OS&lt;/a&gt;” (2021-06-22).&lt;/li&gt;
&lt;li&gt;Aleksandr Hovhannisyan, “&lt;a href="https://www.aleksandrhovhannisyan.com/blog/eleventy-build-info/"&gt;Add Build Info to an 11ty Site&lt;/a&gt;” (2022-05-31).&lt;/li&gt;
&lt;li&gt;Hector Molina, “&lt;a href="https://hmolina.dev/p/moving-away-from-github-pages-to-cloudflare-pages/"&gt;Moving away from GitHub Pages to Cloudflare Pages&lt;/a&gt;” (2022-07-03).&lt;/li&gt;
&lt;li&gt;Michael Schnerring, “&lt;a href="https://schnerring.net/blog/create-a-hugo-website-with-github-pages-github-actions-and-cloudflare/"&gt;Create a Hugo Website with GitHub Pages, GitHub Actions, and Cloudflare&lt;/a&gt;” (2021-03-19).&lt;/li&gt;
&lt;li&gt;Mike Street, "&lt;a href="https://www.mikestreety.co.uk/blog/the-git-commit-hash/"&gt;The Git Commit Hash&lt;/a&gt;" (2020-05-28).&lt;/li&gt;
&lt;li&gt;Hugo Discourse forum

&lt;ul&gt;
&lt;li&gt;“&lt;a href="https://discourse.gohugo.io/t/adding-last-modified-git-status-to-pages/25402"&gt;Adding Last Modified Git Status to Pages?&lt;/a&gt;” (initially posted 2020-05-10).&lt;/li&gt;
&lt;li&gt;“&lt;a href="https://discourse.gohugo.io/t/problems-with-gitinfo-in-ci/22480"&gt;Problems with GitInfo in CI&lt;/a&gt;” (initially posted 2019-12-25).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://iles.pages.dev/"&gt;îles&lt;/a&gt; GitHub Discussions, “&lt;a href="https://github.com/ElMassimo/iles/discussions/132"&gt;Any way to expose Git data?&lt;/a&gt;” (initially posted 2022-06-10).&lt;/li&gt;
&lt;li&gt;Node.js v18.8.0 documentation, “&lt;a href="https://nodejs.org/api/child_process.html"&gt;Child process&lt;/a&gt;” (accessed 2022-09-04 for this article).&lt;/li&gt;
&lt;li&gt;Pragmatic Pineapple, “&lt;a href="https://pragmaticpineapple.com/add-updated-at-to-your-gatsby-blog/"&gt;Add Updated At To Your Gatsby Blog&lt;/a&gt;” (2021-02-08).&lt;/li&gt;
&lt;li&gt;Stack Overflow

&lt;ul&gt;
&lt;li&gt;“&lt;a href="https://stackoverflow.com/questions/34518389/get-hash-of-most-recent-git-commit-in-node/"&gt;Get hash of most recent git commit in Node&lt;/a&gt;” (initially posted 2015-12-29).&lt;/li&gt;
&lt;li&gt;“&lt;a href="https://stackoverflow.com/questions/63673227/why-the-output-of-git-log-is-different-in-terminal-and-execsync-in-node-js"&gt;Why The Output Of ‘git log’ Is Different In Terminal and execSync In node.js&lt;/a&gt;” (initially posted 2020-08-31).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>git</category>
      <category>eleventy</category>
      <category>staticwebsites</category>
      <category>cicd</category>
    </item>
    <item>
      <title>Astro and the move to MDX</title>
      <dc:creator>Bryce Wray</dc:creator>
      <pubDate>Sun, 31 Jul 2022 17:47:01 +0000</pubDate>
      <link>https://dev.to/brycewray/astro-and-the-move-to-mdx-4bd0</link>
      <guid>https://dev.to/brycewray/astro-and-the-move-to-mdx-4bd0</guid>
      <description>&lt;p&gt;Grizzled old guy that I am, I saw &lt;a href="https://en.wikipedia.org/wiki/Hail_Mary_pass"&gt;the original “Hail Mary” play&lt;/a&gt; when it happened&lt;sup id="fnref1"&gt;1&lt;/sup&gt; back in 1975, so I like to think I know a good “Hail Mary” when I see one. In fact, I may have done just that.&lt;/p&gt;

&lt;p&gt;In April, I wrote “&lt;a href="https://dev.to/posts/2022/04/astro-ready-your-blog/"&gt;Is Astro ready for your blog?&lt;/a&gt;” In it, I noted that, although the &lt;a href="https://astro.build"&gt;Astro&lt;/a&gt; &lt;a href="https://jamstack.org/generators"&gt;static site generator&lt;/a&gt; (SSG) was (as it still is) justly receiving a lot of love for its many cool features and the leanness of Astro-built sites, it did suffer somewhat regarding the developer experience on a site containing a large number of Markdown files:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;. . . larger sites don’t refresh all that quickly when you edit either Markdown or an &lt;code&gt;.astro&lt;/code&gt; file . . . . where refresh-in-dev-mode speed is concerned, all the other competitors currently have an edge on Astro for this item, with the stunningly fast &lt;a href="https://gohugo.io"&gt;Hugo&lt;/a&gt; obviously leading the pack.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;(Link added to original text.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Here's what I meant. When I’m running Hugo in dev mode and save a change to any of my site’s “watched” files (content, style, layout, &lt;em&gt;etc.&lt;/em&gt;), I see the result almost instantly on-screen; but, with Astro back then, such actions caused Astro to “think about” the change for several seconds before it would display the revision.&lt;/p&gt;

&lt;p&gt;In the &lt;a href="https://dev.to/posts/2022/05/mulling-over-migration/"&gt;subsequent “Mulling over migration?” post&lt;/a&gt;, I added more details:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If your current site has only a few pages, you’ll find Astro's dev server refreshes the browser quickly enough to suit you. However, after your content gets up to and beyond about the 100-page level, even a fairly simple content edit will cause the refresh to take several seconds when you save the file you’re changing, and the lag will get longer as the site gets bigger.&lt;/p&gt;

&lt;p&gt;The Astro devs are aware of the issue — perhaps related to &lt;a href="https://astro.build/blog/astro-021-preview/#hello-vite"&gt;Astro's interaction with Vite&lt;/a&gt; — but it’s unknown when it’ll be fixed. Thus, with a larger site, you may want to get used to writing your content &lt;em&gt;outside of&lt;/em&gt; development mode and, only then, activating the Astro dev server to check the new page’s appearance.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This dev-mode sluggishness wasn’t the main reason why, as I wrote near the end of that post, I had moved my site off Astro and back to Hugo in May after only a few weeks with the newer SSG, but it didn’t help.&lt;sup id="fnref2"&gt;2&lt;/sup&gt; Still, I retained interest in and curiosity about Astro, so I continued occasionally to look in on its &lt;a href="https://astro.build/chat"&gt;Discord community&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Along the way, I saw that the launch of Astro 1.0, originally planned for early June, had slipped multiple times. As of now, it’s set for next week, assuming the current Release Candidate (RC) suffices. This post is about one key decision that came &lt;em&gt;very&lt;/em&gt; late in the dev process: Astro is shifting content management support from Markdown to &lt;a href="https://mdxjs.com"&gt;MDX&lt;/a&gt;. (While Markdown will still work in Astro at least for the time being, it’ll do so under a “legacy” flag.)&lt;/p&gt;

&lt;p&gt;As for why the Astro team made such a decision, especially so close to the desired date for “shipping” v.1.0, one recent comment on the Astro Discord by Astro co-creator Fred K. Schott may be especially telling. When a user wondered about problems with using components in Astro-flavored Markdown, Schott replied:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Yeah, you’ve kind of hit [on] the thing it’s taken us a year to learn: it’s really hard to build your own MD [plus] components system and syntax[.] 😅 We’re experimenting now with MDX as a better solution for components in [Markdown;] instead of us having to develop our own system we could leverage MDX instead. We already have experimental support . . . but if all goes well this would become standard in time for v1.0 with us deprioritizing our own[.]&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is somewhat reminiscent of the Astro devs’ decision late last year to transition Astro from their own &lt;a href="https://snowpack.dev"&gt;Snowpack&lt;/a&gt; platform to Vite. It wasn’t easy, but the performance win was significant.&lt;/p&gt;

&lt;p&gt;In similar fashion, making the move from Markdown to MDX this late in the ballgame is a big gamble. However, with the hassles the Astro devs have encountered while trying to make Markdown and Astro play nicely together, it makes sense — particularly considering how much of Astro’s &lt;em&gt;raison d'être&lt;/em&gt; involves not only a pleasant DX but also smooth interaction between markup and components.&lt;/p&gt;

&lt;p&gt;By this weekend, the Astro team appeared to have MDX working smoothly with the latest RC version, so I put it through some tests. Specifically, I wanted to see the DX for an Astro site with about as many MDX files as my current Hugo site has Markdown files. In fact, I even stacked the deck a bit against Astro by giving the test project &lt;em&gt;more&lt;/em&gt; such files. (Each MDX file had numerous paragraphs of “lorem ipsum” boilerplate, enough to constitute plenty of text for Astro to manage.)&lt;/p&gt;

&lt;p&gt;Based on my tests, the move to MDX looks like a winner. In dev mode, edits to “watched” files show up virtually immediately (I’d say Astro's MDX DX, so to speak, is very similar to &lt;a href="https://www.zachleat.com/web/build-benchmark/#benchmark-results"&gt;that&lt;/a&gt; of &lt;a href="https://11ty.dev"&gt;Eleventy&lt;/a&gt;’s with plain Markdown). This could simplify a move to Astro for some folks with big, Markdown-heavy sites, folks who simply couldn’t stomach the molasses-like DX that they’d encountered before Astro/MDX interactivity became a thing.&lt;/p&gt;

&lt;p&gt;So what could be wrong with this picture? Well, anyone who had &lt;em&gt;already&lt;/em&gt; transitioned existing Markdown content from another SSG to Astro — especially given the &lt;a href="https://dev.to/posts/2022/05/mulling-over-migration/#modifying-your-markdown"&gt;changes such a move required&lt;/a&gt; — now must do so &lt;em&gt;again&lt;/em&gt;. &lt;strong&gt;However&lt;/strong&gt;, for those who &lt;strong&gt;hadn’t&lt;/strong&gt; yet made that wrenching change, it’s a lot easier to move regular Markdown files to an MDX-equipped Astro site: you just change their extensions from &lt;code&gt;.md&lt;/code&gt; to &lt;code&gt;.mdx&lt;/code&gt;. Of course, you’ll have to address any “special” stuff you may have put in that Markdown on the other SSG, just as you would in moving between &lt;em&gt;any&lt;/em&gt; two SSGs; but, now, &lt;em&gt;generic&lt;/em&gt; Markdown as &lt;code&gt;.mdx&lt;/code&gt; will work just fine in Astro.&lt;/p&gt;

&lt;p&gt;The stability of Astro, like any other SSG (especially a JavaScript-based one), is only as good as the stability of its many third-party dependencies; and some of those are still a little shaky at this writing. That said, where the RC’s readiness for becoming the &lt;em&gt;real&lt;/em&gt; v.1.0 is concerned, the team is pretty near the finish line on this count — and, in my view, the new MDX compatibility is a surprisingly big reason why. The switch was a gutsy move to make at all, much less at this point in the run-up to v.1.0, but also one that needed to be made.&lt;/p&gt;

&lt;p&gt;To the good folks at The Astro Technology Company: I think you just threw one whale of a “Hail Mary” as the fourth-quarter clock was ticking down to zero. Here’s hoping v.1.0 is a winning touchdown.&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;Sorry, Minnesota Vikings fans, but I’m &lt;em&gt;not&lt;/em&gt; interested in your complaints about offensive pass interference on the play. It was a long, long time ago. Get over it. (Also: in the famous film sequence of Drew Pearson as he crossed the goal line with the winning score, that was an &lt;em&gt;orange&lt;/em&gt; thrown onto the field by a fan, not a penalty flag thrown by a referee. Truth hurts; I know.) ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;In time, though, I found other reasons to prefer the Hugo DX, especially as I delved more deeply into coding projects requiring access to remote APIs. Hugo makes that work considerably easier than does any &lt;a href="https://nodejs.org"&gt;Node.js&lt;/a&gt;-based SSG. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
    </item>
    <item>
      <title>Using iCloud Mail with a custom domain</title>
      <dc:creator>Bryce Wray</dc:creator>
      <pubDate>Mon, 13 Jun 2022 20:20:52 +0000</pubDate>
      <link>https://dev.to/brycewray/using-icloud-mail-with-a-custom-domain-24pc</link>
      <guid>https://dev.to/brycewray/using-icloud-mail-with-a-custom-domain-24pc</guid>
      <description>&lt;p&gt;I was excited a year ago when, at its 2021 Worldwide Developers Conference (WWDC), Apple &lt;a href="https://9to5mac.com/2021/06/07/custom-domain-names-are-coming-to-icloud-mail-with-icloud/"&gt;announced&lt;/a&gt; that one feature of its &lt;a href="https://www.macrumors.com/2021/06/07/apple-announces-icloud-with-private-relay-more/"&gt;new extra-cost iCloud+ package&lt;/a&gt; was support for &lt;em&gt;custom domains&lt;/em&gt; in iCloud Mail.&lt;/p&gt;

&lt;p&gt;Up to that point, only an &lt;code&gt;@icloud.com&lt;/code&gt; address would work with this service&lt;sup id="fnref1"&gt;1&lt;/sup&gt; but, now, people like me who got their email on one or more custom domains would be able to use those domains in connection with iCloud Mail. I knew I’d be an iCloud+ customer, anyway — the bundled Apple services to which I’d already subscribed are cheaper via iCloud+ rather than as separate offerings — so this additional benefit sounded like the feature which I’d long hoped Apple would provide.&lt;/p&gt;

&lt;p&gt;As I’d mentioned in 2019's “&lt;a href="https://www.brycewray.com/posts/2019/05/the-holy-mail/"&gt;The Holy Mail&lt;/a&gt;,” one advantage I gained in my 2017 move from a generic &lt;a href="https://en.wikipedia.org/wiki/Gmail"&gt;Gmail&lt;/a&gt; address to a custom-domain address (hosted by &lt;a href="https://fastmail.com"&gt;FastMail&lt;/a&gt;) was that, if I ever wanted to change providers in the future, this never again would involve telling all my various contacts to use a new address. Instead, I simply would point the appropriate online records for the domain to the new provider, and everything would continue to work just fine.&lt;/p&gt;

&lt;p&gt;Don’t get me wrong; I’ve been mostly very satisfied with FastMail — but it &lt;em&gt;is&lt;/em&gt; about fifty bucks a year. That price includes many services that I rarely or never use and which, I think, seem more useful for businesses than individuals; so I took this Apple announcement as a way I might get out of that.&lt;/p&gt;

&lt;h2&gt;
  
  
  As always, Reality ain’t pretty
&lt;/h2&gt;

&lt;p&gt;Unfortunately, the new custom-domains feature got a lot of mixed reviews&lt;sup id="fnref2"&gt;2&lt;/sup&gt; when it went live later in 2021. Then, when I first tried it last October, certain resulting oddities gave me some qualms, so I quickly reverted the domain to my still-active FastMail account. In essence, there seemed to be some technical issues that Apple still hadn’t resolved sufficiently to make the switch as clean, or as compatible with other email providers, as I’d hoped following the WWDC reveal.&lt;/p&gt;

&lt;p&gt;In the ensuing months, I continued to monitor developments and, after reading various reports that iCloud Mail’s custom-domains support seems now to be working more smoothly&lt;sup id="fnref3"&gt;3&lt;/sup&gt;, I once again decided to give it a try. Last night, I pointed one of my two custom-domain email addresses to iCloud Mail. In fact, it’s the address to which you can send by clicking that “Reply via email” link at the bottom of each post on my website.&lt;/p&gt;

&lt;p&gt;Before you think, “Holy crap, he’s spending $100 a year for personal email,” please understand that this first domain I’m trying with iCloud Mail previously was just an &lt;a href="https://www.fastmail.help/hc/en-us/articles/360060591073-How-to-set-up-aliases"&gt;&lt;em&gt;alias&lt;/em&gt; on FastMail&lt;/a&gt;. That means it already came with my existing fifty-dollar-a-year subscription. Speaking of email aliases: any custom-domain email you put on iCloud Mail is just an alias within &lt;em&gt;that&lt;/em&gt; service, too&lt;sup id="fnref4"&gt;4&lt;/sup&gt;. At one time, that would’ve been a deal-breaker for me but, now, I don’t care.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Important note&lt;/strong&gt;: if you’re doing the same — bringing a FastMail alias over to iCloud Mail — you’ll want to kill &lt;strong&gt;both&lt;/strong&gt; the alias &lt;strong&gt;and&lt;/strong&gt; the alias’s domain entry on FastMail. I found out the hard way that doing only the former but not also the latter, even if only to play it safe, can cause SNAFUs during the switchover process. If that sounds like too much of a jump off the bridge for you, remember that you can always reverse the process if necessary. It’s tedious, but you can do it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Progress report
&lt;/h2&gt;

&lt;p&gt;It’s early, so the proverbial jury is still out; but I’ve been pleased so far. With a domain like this one, whose various &lt;a href="https://en.wikipedia.org/wiki/MX_record"&gt;MX&lt;/a&gt; and other records are hosted by &lt;a href="https://cloudflare.com"&gt;Cloudflare&lt;/a&gt;, the Apple switchover process seems much less manual than what I encountered last October, so perhaps Apple listened to the community complaints and shored up things in the meantime. And, as for the nitty-gritty of actually &lt;em&gt;using&lt;/em&gt; the thing: when I send emails to the address from various other-domain addresses to which I have access (long story), everything goes back and forth without any trouble.&lt;/p&gt;

&lt;p&gt;It remains to be seen whether I’ll also switch my &lt;em&gt;other&lt;/em&gt; address, the Big Kahuna that serves me in all aspects of life &lt;em&gt;other than&lt;/em&gt; interactions with you folks who kindly visit my website. As you can well imagine, it will depend in great measure on how things go with this first one. I have a few months before my FastMail subscription would auto-renew, so I’ll have plenty of time to make a reasoned evaluation and act accordingly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.brycewray.com/posts/2022/06/using-icloud-mail-custom-domain-following-up/"&gt;I’ll let you know how it goes&lt;/a&gt;.&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;Well, of course, that’s not counting those &lt;a href="https://support.apple.com/en-us/HT201771"&gt;legacy &lt;code&gt;@me.com&lt;/code&gt; and &lt;code&gt;@mac.com&lt;/code&gt; addresses&lt;/a&gt; Apple issued many years ago, but I’m referring to &lt;em&gt;current&lt;/em&gt; stuff. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;I’ll leave it to you to find those reports. You won’t have much difficulty, especially if you confine your searches to the second half of 2021. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;For example, see the endnote addenda to the otherwise “meh” review in &lt;a href="https://domlaut.com/"&gt;Dominic Lauter&lt;/a&gt;’s “&lt;a href="https://domlaut.com/icloud-custom-email-domains-should-be-better/"&gt;iCloud+ Custom Email Domains should be better&lt;/a&gt;.” ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn4"&gt;
&lt;p&gt;See “One Mailbox” under “The SPAM and the Ugly” in &lt;a href="https://mike.lapidak.is/"&gt;Mike Lapidakis&lt;/a&gt;’s article from last November, “&lt;a href="https://empty.coffee/thoughts-on-custom-domains-in-apple-icloud-mail/"&gt;Thoughts on Custom Domains in Apple’s iCloud Mail&lt;/a&gt;.” ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>email</category>
      <category>icloud</category>
      <category>domain</category>
      <category>apple</category>
    </item>
    <item>
      <title>Get good Git info from Hugo</title>
      <dc:creator>Bryce Wray</dc:creator>
      <pubDate>Wed, 01 Jun 2022 13:14:00 +0000</pubDate>
      <link>https://dev.to/brycewray/get-good-git-info-from-hugo-45f8</link>
      <guid>https://dev.to/brycewray/get-good-git-info-from-hugo-45f8</guid>
      <description>&lt;p&gt;While reading blog posts from other static site generator (SSG) users, I sometimes see that a post includes a link to the specific &lt;a href="https://git-scm.com/docs/git-commit"&gt;Git commit&lt;/a&gt; for that post’s most recent update. In this post, I’ll show you how to do it in a &lt;a href="https://gohugo.io"&gt;Hugo&lt;/a&gt; site, in case you’re interested in doing the same. As an additional benefit, it’ll automate something you might have been doing manually up to now.&lt;/p&gt;

&lt;p&gt;I got the idea yesterday, when I saw a &lt;a href="https://www.aleksandrhovhannisyan.com/blog/eleventy-build-info/"&gt;post&lt;/a&gt; from &lt;a href="https://www.aleksandrhovhannisyan.com/"&gt;Aleksandr Hovhannisyan&lt;/a&gt;. In it, he gave a fine tutorial about displaying this data in pages built with the &lt;a href="https://11ty.dev/"&gt;Eleventy&lt;/a&gt; SSG. Hovhannisyan’s method employed JavaScript to fetch the necessary Git data for use by his Eleventy templates.&lt;/p&gt;

&lt;p&gt;On the other hand: with a Hugo site, things are much easier, thanks to the built-in availability of &lt;strong&gt;&lt;a href="https://gohugo.io/variables/git"&gt;Git info variables&lt;/a&gt;&lt;/strong&gt;. Once you set Hugo to fetch these variables, they’re available from within a &lt;code&gt;.GitInfo&lt;/code&gt; object.&lt;/p&gt;

&lt;p&gt;In your &lt;a href="https://gohugo.io/getting-started/configuration/"&gt;project config file&lt;/a&gt;, set &lt;code&gt;enableGitInfo&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt; (here, I’m showing the Hugo default of &lt;a href="https://github.com/toml-lang/toml"&gt;TOML&lt;/a&gt;, although my own config file is actually &lt;a href="https://yaml.org/spec/"&gt;YAML&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="py"&gt;enableGitInfo&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As the setting’s name implies, this activates the presence of the Git info variables.&lt;/p&gt;

&lt;p&gt;I’ll get to the part about displaying commit info shortly but, first, let’s note that making this setting may have just liberated you from a nit-picking duty involved in how you display your posts’ dates. If you’ve been using &lt;em&gt;manual&lt;/em&gt; entries in your posts’ front matter to indicate when they were last modified, you no longer have to do that. The Git info will, by default, provide this data as &lt;code&gt;.Lastmod&lt;/code&gt;.&lt;sup id="fnref1"&gt;1&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;However&lt;/strong&gt;, in production, you will need to deploy your site using a &lt;a href="https://github.com/features/actions/"&gt;GitHub Action&lt;/a&gt; (or other CI/CD), as &lt;a href="https://www.brycewray.com/posts/2022/05/using-dart-sass-hugo-github-actions-edition/"&gt;I've been doing lately&lt;/a&gt;. The problem is that, although these automated &lt;code&gt;.Lastmod&lt;/code&gt; indications will be correct when you’re developing &lt;em&gt;locally&lt;/em&gt; with &lt;code&gt;hugo server&lt;/code&gt;, they’ll &lt;em&gt;all&lt;/em&gt; take on the &lt;em&gt;current&lt;/em&gt; date when you deploy. Fortunately, there’s an explanation and solution, from a &lt;a href="https://discourse.gohugo.io/t/problems-with-gitinfo-in-ci/22480"&gt;thread&lt;/a&gt;&lt;sup id="fnref2"&gt;2&lt;/sup&gt; on the Hugo Discourse forum:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;By default, the &lt;a href="https://github.com/actions/checkout"&gt;GitHub “checkout” action&lt;/a&gt; only fetches a single commit (for the &lt;a href="https://git-scm.com/book/en/v2/Git-Internals-Git-References"&gt;ref/SHA&lt;/a&gt; that triggered the workflow). This results in the behavior you describe — &lt;em&gt;i.e.&lt;/em&gt;[,] the current date/time is used for &lt;code&gt;.Lastmod&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you modify the checkout action to fetch the entire history (by specifying &lt;code&gt;fetch-depth: 0&lt;/code&gt;), then &lt;code&gt;.GitInfo&lt;/code&gt; and &lt;code&gt;.Lastmod&lt;/code&gt; [work] as expected[.]&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;[Stylistic edits and one link applied.]&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is because (a.) hosts’ Git configurations for builds typically are set to so-called &lt;em&gt;shallow-clone&lt;/em&gt; behavior; and, apparently, (b.) no host allows altering this in either its built-in UI or any optional config files (&lt;em&gt;e.g.&lt;/em&gt;, &lt;code&gt;netlify.toml&lt;/code&gt; with Netlify or &lt;code&gt;vercel.json&lt;/code&gt; with Vercel). Shallow-clone behavior causes problems with using &lt;code&gt;.GitInfo&lt;/code&gt; data as described in this post, so keep this in mind if you typically deploy via your host’s built-in user interface rather than with CI/CD.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note from the future&lt;/strong&gt;: In testing for a &lt;a href="https://www.brycewray.com/posts/2023/02/get-good-git-info-even-hosts-gui/"&gt;later article&lt;/a&gt;, I found that, in fact, Netlify and &lt;a href="https://www.digitalocean.com/products/app-platform"&gt;DigitalOcean App Platform&lt;/a&gt; exhibit &lt;em&gt;deep-clone&lt;/em&gt; behavior where Git is concerned, so you likely can use the information herein with their native UIs rather than having to deploy to them via CI/CD.&lt;/p&gt;

&lt;p&gt;To make use of this answer in a GitHub Action, you could just add a &lt;code&gt;with&lt;/code&gt; section to the GitHub Action’s &lt;code&gt;Checkout default branch&lt;/code&gt; step:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout default branch&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;fetch-depth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;. . . and, indeed, that fixes the glitch.&lt;/p&gt;

&lt;p&gt;You might also want to test for whether a post’s day of original publication and its “last-modified” day are the same --- &lt;em&gt;e.g.&lt;/em&gt;, when you fix a typo or otherwise edit something while it’s still the same day as when you first issued the post --- and, if so, show only the “original-pub” listing, to avoid duplication. However, this requires comparing the &lt;em&gt;formatted&lt;/em&gt; dates, since full &lt;em&gt;timestamps&lt;/em&gt; clearly can &lt;em&gt;never&lt;/em&gt; be the same &lt;a href="https://pkg.go.dev/time#ANSIC"&gt;down to the nanosecond&lt;/a&gt;; so here’s how you could accomplish this in an applicable template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;strong&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PublishDate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Format&lt;/span&gt; &lt;span class="s"&gt;"January 2, 2006"&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;strong&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;br&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ne&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PublishDate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Format&lt;/span&gt; &lt;span class="s"&gt;"January 2, 2006"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lastmod&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Format&lt;/span&gt; &lt;span class="s"&gt;"January 2, 2006"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;
    &lt;span class="n"&gt;Last&lt;/span&gt; &lt;span class="n"&gt;modified&lt;/span&gt; &lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lastmod&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Format&lt;/span&gt; &lt;span class="s"&gt;"January 2, 2006"&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;
  &lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;nbsp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Within the paragraph, if the two are &lt;em&gt;not&lt;/em&gt; equal (&lt;code&gt;ne&lt;/code&gt;), this shows the “Last modified” statement; otherwise, it just inserts a non-breaking space so the height of the line will be the same.&lt;/p&gt;

&lt;p&gt;That takes care of Git info for dates; but what about the original subject of this post, namely how you can link to a page’s most recent Git commit?&lt;/p&gt;

&lt;p&gt;Well, the &lt;code&gt;.GitInfo&lt;/code&gt; object also provides two variables for each commit’s &lt;a href="https://www.mikestreety.co.uk/blog/the-git-commit-hash/"&gt;hash&lt;/a&gt;: the &lt;em&gt;full&lt;/em&gt; hash (&lt;code&gt;.Hash&lt;/code&gt;) and the more familiar &lt;em&gt;abbreviated&lt;/em&gt; hash (&lt;code&gt;.AbbreviatedHash&lt;/code&gt;). Adding this within the proper templates is pretty matter-of-fact. The following example &lt;em&gt;displays&lt;/em&gt; &lt;code&gt;.AbbreviatedHash&lt;/code&gt; while the &lt;em&gt;link&lt;/em&gt; is an &lt;code&gt;exampleuser&lt;/code&gt; repo link plus &lt;code&gt;/commit/&lt;/code&gt; plus &lt;code&gt;.Hash&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GitInfo&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;strong&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Latest&lt;/span&gt; &lt;span class="n"&gt;commit&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;strong&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"https://github.com/exampleuser/hugo_site/commit/{{ .GitInfo.Hash }}"&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"_blank"&lt;/span&gt; &lt;span class="n"&gt;rel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"noopener"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GitInfo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AbbreviatedHash&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;nbsp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(The &lt;code&gt;if $.GitInfo&lt;/code&gt; conditional prevents &lt;code&gt;hugo server&lt;/code&gt; errors during local development while you’re working on content you haven’t yet committed.&lt;sup id="fnref3"&gt;3&lt;/sup&gt; You can thank &lt;a href="https://discourse.gohugo.io/t/adding-last-modified-git-status-to-pages/25402/5"&gt;another Hugo Discourse forum answer&lt;/a&gt; for that one.)&lt;/p&gt;

&lt;p&gt;And, yes, the previous advisory still applies: &lt;em&gt;i.e.&lt;/em&gt;, you have to deploy to your host via CI/CD in order to see the correct Git commit data for each post.&lt;/p&gt;

&lt;p&gt; &lt;br&gt;&lt;/p&gt;

&lt;p&gt;So, now, you’ve automated both (a.) displaying that &lt;code&gt;Lastmod&lt;/code&gt; stuff and (b.) linking to the commit from which each page’s latest version originates. Yet, you needed no additional tools, and very little extra code, to do it. Not bad for a few minutes’ worth of work in Hugo, eh?&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Update, 2022-10-07&lt;/strong&gt;: If you’re interested in displaying &lt;strong&gt;both&lt;/strong&gt; per-page Git info &lt;strong&gt;and&lt;/strong&gt; whole-site Git info in your Hugo site, check the &lt;a href="https://github.com/gohugoio/hugo/issues/9738#issuecomment-1086669372"&gt;solution&lt;/a&gt; suggested by Hugo expert/contributor &lt;a href="https://github.com/jmooring"&gt;Joe Mooring&lt;/a&gt;. Thanks to &lt;a href="https://github.com/rodrigoalcarazdelaosa"&gt;Rodrigo Alcaraz de la Osa&lt;/a&gt; for the &lt;a href="https://github.com/brycewray/comments/discussions/25"&gt;Q&amp;amp;A session&lt;/a&gt; that led me toward this additional information!&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;&lt;a href="https://gohugo.io/getting-started/configuration/#configure-dates"&gt;By default&lt;/a&gt;, Hugo will give higher priority to the Git info variable &lt;code&gt;.Lastmod&lt;/code&gt; &lt;em&gt;vs.&lt;/em&gt; other possibilities—including any manual &lt;code&gt;Lastmod&lt;/code&gt; entries you may have already provided in your content’s front matter. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;The original question dates from December 25, 2019, but it took another 21 months before an answer, much less &lt;em&gt;the&lt;/em&gt; answer, appeared. Jeeeez. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;The reason you don’t have to do this with the dates-display mentioned earlier is because, in that case, we’re simply using &lt;code&gt;Lastmod&lt;/code&gt; (a variable &lt;a href="https://gohugo.io/variables/page/"&gt;Hugo already knows&lt;/a&gt;) rather than, specifically, &lt;code&gt;.GitInfo.Lastmod&lt;/code&gt;, the absence of which for a given page gives Hugo fits. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>webdev</category>
      <category>git</category>
      <category>tutorial</category>
      <category>hugo</category>
    </item>
  </channel>
</rss>
