<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Web on Giovanni Bassi</title><link>https://giggio.net/en/blog/categories/web/</link><image><url>https://giggio.net/images/base/logo-small.png</url><title>Web on Giovanni Bassi</title><link>https://giggio.net/en/blog/categories/web/</link></image><description>Web no site do Giovanni Bassi</description><generator>Hugo</generator><language>en</language><managingEditor>giggio@giggio.net (Giovanni Bassi)</managingEditor><webMaster>giggio@giggio.net (Giovanni Bassi)</webMaster><copyright>© 2025 Giovanni Bassi</copyright><lastBuildDate>Wed, 02 Apr 2025 13:00:00 -0300</lastBuildDate><atom:link href="https://giggio.net/en/blog/categories/web/index.xml" rel="self" type="application/rss+xml"/><item><title>Modernizing the website using Bootstrap 5</title><link>https://giggio.net/en/blog/modernizando-o-site-a-partir-do-bootstrap-5/</link><pubDate>Wed, 02 Apr 2025 13:00:00 -0300</pubDate><author>giggio@giggio.net (Giovanni Bassi)</author><guid>https://giggio.net/en/blog/modernizando-o-site-a-partir-do-bootstrap-5/</guid><category>web</category><description>&lt;p&gt;In the last few days, I updated this site to &lt;a href="https://blog.getbootstrap.com/2021/05/05/bootstrap-5/"&gt;Bootstrap 5&lt;/a&gt;. I
took the opportunity to make several improvements and I want to share how that process went.&lt;/p&gt;
&lt;p&gt;More than just an update, I used this chance to enhance various parts of the site, including where it touches
&lt;a href="https://gohugo.io/"&gt;Hugo&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="bootstrap-5"&gt;
 &lt;a href="#bootstrap-5" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Bootstrap 5&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Migrating to Bootstrap 5 turned out to be much smoother than I expected. I thought it would be an extremely laborious
process, but in practice, it was pretty straightforward. There are countless articles and even the
&lt;a href="https://getbootstrap.com/docs/5.3/migration/"&gt;official documentation&lt;/a&gt; that explain every step, so I won&amp;rsquo;t dive too deep
into the technical details of the update itself. During the process, I realized I could improve other parts of the site,
so I decided to work on them as well.&lt;/p&gt;
&lt;p&gt;If you want to check out the update, the commit is
&lt;a href="https://github.com/giggio/giggionet/commit/484dd97a07340bfcda0d77bd5e7ccc899b3e1bad"&gt;on Github&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="ecmascript-modules"&gt;
 &lt;a href="#ecmascript-modules" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;EcmaScript modules&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Bootstrap 5, released in mid-2021 (three and a half years after version 4.0), brought full support for ES Modules,
which are widely supported by 2025. Previously, my code loaded scripts and styles via CDN. That took advantage of users’
caches but ended up downloading the entire library, including unnecessary code and styles since not everything was used.
Plus, it didn’t use ESM.&lt;/p&gt;
&lt;p&gt;With ESM, I was able to apply tree shaking – the removal of unused code. Luckily, Hugo already offers an option to
perform this process, making everything extremely simple.&lt;/p&gt;
&lt;p&gt;I created a file called
&lt;a href="https://github.com/giggio/giggionet/blob/789839f6a6e14c4a14cd72c2ab5d2a1d48ad5d64/assets/js/vendor.mjs"&gt;vendor.mjs&lt;/a&gt; to
list only the dependencies I actually use. This is the file that gets compiled. It basically exports the libraries, like
this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;export&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Popover&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Tooltip&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;from&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;bootstrap&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Using Hugo’s &lt;a href="https://gohugo.io/functions/js/build/"&gt;js.Build&lt;/a&gt; method, the bundle is generated automatically. In short,
it looks like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go-html-template" data-lang="go-html-template"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;resources&lt;/span&gt;&lt;span class="na"&gt;.Get&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;/js/vendor.mjs&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;$opts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;dict&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;minify&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;hugo&lt;/span&gt;&lt;span class="na"&gt;.IsProduction&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;sourceMap&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;linked&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;format&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;esm&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;printf&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;`&amp;lt;!-- Vendor file: %q --&amp;gt;`&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;js&lt;/span&gt;&lt;span class="na"&gt;.Build&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;$opts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fingerprint&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;sha256&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="na"&gt;.RelPermalink&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;safeHTML&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This file is then imported into my main script, the
&lt;a href="https://github.com/giggio/giggionet/blob/789839f6a6e14c4a14cd72c2ab5d2a1d48ad5d64/assets/js/script.mjs"&gt;script.js&lt;/a&gt;,
with a simple &lt;code&gt;import&lt;/code&gt;. It starts off like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Popover&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Tooltip&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;from&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;./vendor.mjs&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I simply import this file, without building it, which means the vendor file isn’t inlined. The challenge was that the
file name changes (because of the fingerprint), but I solved that with an
&lt;a href="https://developer.mozilla.org/docs/Web/HTML/Element/script/type/importmap"&gt;importmap script&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I started using Bootstrap installed via Npm, which made the process a lot easier because Hugo’s &lt;code&gt;js.Build&lt;/code&gt; uses
&lt;a href="https://esbuild.github.io/"&gt;esbuild&lt;/a&gt; to automatically search within the &lt;code&gt;node_modules&lt;/code&gt; directory.&lt;/p&gt;
&lt;p&gt;Another advantage is that the vendor file remains static, which allows browsers to leverage their cache extensively. It
only changes when I update the dependencies. When that happens, the compiled file name changes, solving browser cache
issues. The same goes for &lt;code&gt;script.mjs&lt;/code&gt;, though that one tends to change more often as new features are added.&lt;/p&gt;
&lt;p&gt;Fun fact: rewriting this script was the only part that Github Copilot managed to automate during this migration – and it
generated a bug that I fixed right away.&lt;/p&gt;
&lt;h3 id="sass-compilation"&gt;
 &lt;a href="#sass-compilation" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Sass compilation&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;I had already been performing tree shaking and analyzing all of my Sass/CSS, but the Bootstrap CSS was loaded entirely
via CDN. Since Bootstrap’s Sass is already available in &lt;code&gt;node_modules&lt;/code&gt;, I started integrating it directly, eliminating
all of the &lt;strong&gt;unused Bootstrap CSS&lt;/strong&gt;. I lost the potential cache benefits from the CDN, but I believe the vendor file
will make up for that.&lt;/p&gt;
&lt;p&gt;The challenge was tweaking my unused CSS analysis function, as much of the CSS remaining in the final file (before tree
shaking) wasn’t written by me. This script cross-references the generated HTML with the CSS and points out unused
selectors, allowing me to remove them manually afterward. With a bit of Bash and open-source tools, I managed to sort it
out. If you&amp;rsquo;re interested, check out the
&lt;a href="https://github.com/giggio/giggionet/blob/789839f6a6e14c4a14cd72c2ab5d2a1d48ad5d64/package.json#L12"&gt;unused-css&lt;/a&gt;
script in the &lt;code&gt;package.json&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="goodbye-jquery"&gt;
 &lt;a href="#goodbye-jquery" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Goodbye jQuery&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;I’m incredibly grateful to jQuery – it paid many of my bills and was a great companion – but by 2025, it just doesn’t
make sense anymore. While Bootstrap 4 depended on jQuery, Bootstrap 5 does not. So, I removed it.&lt;/p&gt;
&lt;p&gt;The problem was that the lightbox library I had chosen depended on jQuery, and I didn’t want to keep it around just for
that reason. I decided to replace it with &lt;a href="https://photoswipe.com/"&gt;Photoswipe&lt;/a&gt;, which already supports ESM, making
implementation easier. To import it, I used another script with &lt;code&gt;importmap&lt;/code&gt;, pointing directly to the CDN. And with
that, I also eliminated the extra files from the previous library, which required me to also deliver its images. Now,
everything comes from the CDN.&lt;/p&gt;
&lt;h3 id="switching-fontawesome-to-js--svg"&gt;
 &lt;a href="#switching-fontawesome-to-js--svg" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Switching FontAwesome to JS + SVG&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;After optimizing Bootstrap’s JavaScript and CSS, I decided to tackle other areas of the site. I noticed that the web
font files for &lt;a href="https://fontawesome.com/"&gt;FontAwesome&lt;/a&gt; were quite large – with three groups of free fonts (solid,
regular, and brands), totaling over 300KB. On the other hand, FontAwesome offers an option to use JavaScript + SVG
instead of fonts, without changing the HTML. This approach even allows for tree shaking, as described in the
&lt;a href="https://docs.fontawesome.com/apis/javascript/tree-shaking"&gt;documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The biggest challenge was the documentation, which ended up being confusing, especially with the constant promotion of
premium icons. I&amp;rsquo;m considering switching to &lt;a href="https://icons.getbootstrap.com/"&gt;Bootstrap’s own icons&lt;/a&gt; or at least mixing
some of them in, but I&amp;rsquo;m still evaluating. Either way, figuring out how to use FontAwesome’s JavaScript APIs wasn’t
simple since there isn’t a complete example available.&lt;/p&gt;
&lt;p&gt;I used the same technique with the &lt;code&gt;vendor.mjs&lt;/code&gt; file, including only the icons I need. Creating this file correctly was
challenging, but with some Bash-fu, I managed to generate a file whose sole purpose is to import the icons I’m using –
the
&lt;a href="https://github.com/giggio/giggionet/blob/789839f6a6e14c4a14cd72c2ab5d2a1d48ad5d64/assets/js/fontawesome.generated.mjs"&gt;fontawesome.generated.mjs&lt;/a&gt;.
If you want to see how it was done, all of the project’s Bash code is concentrated in one file (and I promise I’ll write
a detailed post about it soon):
&lt;a href="https://github.com/giggio/giggionet/blob/789839f6a6e14c4a14cd72c2ab5d2a1d48ad5d64/nix/h#L492-L556"&gt;find it here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The generated file, in simplified form, looks like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;dom&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;library&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;from&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;@fortawesome/fontawesome-svg-core&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;faMastodon&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;from&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;@fortawesome/free-brands-svg-icons&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;faCheck&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;from&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;@fortawesome/free-solid-svg-icons&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;library&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;faMastodon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;faCheck&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;export&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;dom&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;library&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In the actual file, lines 2 and 3 contain all the icons.&lt;/p&gt;
&lt;p&gt;Using it is super simple: I created another file called &lt;code&gt;fontawesome.mjs&lt;/code&gt;, which imports the generated file and contains
a single function responsible for initializing the icons:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;dom&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;from&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;./fontawesome.generated.mjs&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;initFontawesome&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// essa chamada inicializa os ícones da página inteira:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;dom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;i2svg&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// essa chamada inicializa apenas os ícones dos
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// elementos recebidos e é usada quando um elemento
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// com ícone é adicionado dinamicamente à página:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;dom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;i2svg&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Finally, this file is imported and exported in &lt;code&gt;vendor.mjs&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You might have noticed that the &lt;code&gt;initFontawesome&lt;/code&gt; function optionally accepts a list of nodes. To avoid having to call
it manually everywhere, I created a
&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver"&gt;MutationObserver&lt;/a&gt; that triggers it whenever an &lt;code&gt;&amp;lt;i&amp;gt;&lt;/code&gt;
element is added to the DOM. So when that happens, the &lt;code&gt;&amp;lt;i&amp;gt;&lt;/code&gt; is removed by FontAwesome’s code and replaced with an
&lt;code&gt;&amp;lt;svg&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="dark-mode"&gt;
 &lt;a href="#dark-mode" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Dark Mode&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;One of the reasons for updating to Bootstrap was its new implementation of light and dark themes, something essential
for me as a dark mode fan. My site’s all-white look was really bugging me, so this change was a top priority!&lt;/p&gt;
&lt;h4 id="moving-sass-variables-to-css"&gt;
 &lt;a href="#moving-sass-variables-to-css" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Moving Sass variables to CSS&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h4&gt;
&lt;p&gt;The &lt;a href="https://getbootstrap.com/docs/5.3/customize/color-modes/"&gt;documentation&lt;/a&gt; for Bootstrap explains the process well,
and the implementation was very straightforward. The challenge was that my code, inherited from a theme, made heavy use
of &lt;a href="https://sass-lang.com/documentation/variables/"&gt;Sass variables&lt;/a&gt;. For an effective dark mode implementation, I needed
to migrate to
&lt;a href="https://developer.mozilla.org/docs/Web/CSS/CSS_cascading_variables/Using_CSS_custom_properties"&gt;CSS variables&lt;/a&gt;
(or, more accurately, &lt;strong&gt;CSS custom properties&lt;/strong&gt;). I carried out this migration
&lt;a href="https://github.com/giggio/giggionet/commit/26ca930e5169eb9b6c457cbab67afbce966023e0"&gt;in this commit&lt;/a&gt; –
and it was super easy.&lt;/p&gt;
&lt;h4 id="implementing-dark-mode"&gt;
 &lt;a href="#implementing-dark-mode" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Implementing dark mode&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h4&gt;
&lt;p&gt;The implementation was super straightforward. Preferably, Bootstrap doesn’t rely solely on the
&lt;a href="https://developer.mozilla.org/docs/Web/CSS/@media/prefers-color-scheme"&gt;prefers-color-scheme&lt;/a&gt;
media query, since that would make it hard for
users to switch themes without altering their browser or OS settings. Instead, it uses the &lt;code&gt;data-bs-theme-value&lt;/code&gt;
attribute, which you can place anywhere (typically on the &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;With that done, you just need to create a section for default CSS variables (which in my case became the light theme),
and another for the alternative (dark) theme, something like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-scss" data-lang="scss"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="na"&gt;--body-color&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="ni"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="na"&gt;--text-color&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="no"&gt;black&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;@include&lt;/span&gt;&lt;span class="nd"&gt; bootstrap&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;color-mode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ni"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="na"&gt;--body-color&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="no"&gt;black&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="na"&gt;--text-color&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="ni"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The importance of migrating to CSS variables beforehand becomes clear here.&lt;/p&gt;
&lt;p&gt;The Sass &lt;code&gt;color-mode&lt;/code&gt; mixin basically inserts the content inside the mentioned attribute, like so:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-scss" data-lang="scss"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;@mixin&lt;/span&gt;&lt;span class="nf"&gt; color-mode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$mode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;data-bs-theme&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nv"&gt;$mode&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;@content&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And the above code compiles to:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-css" data-lang="css"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nd"&gt;root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;--body-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;--text-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;black&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;data-bs-theme&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;dark&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;--body-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;black&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;--text-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Since the &lt;code&gt;[data-bs-theme]&lt;/code&gt; attribute is defined on the &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt;, it has higher specificity than &lt;code&gt;:root&lt;/code&gt;, altering the
CSS variables and consequently, all the site’s colors.&lt;/p&gt;
&lt;p&gt;Bootstrap’s documentation provides a base code for this functionality, which I used and tweaked slightly. It only uses
the media query until the user opts to switch themes, ensuring a very flexible system.&lt;/p&gt;
&lt;p&gt;The biggest challenge wasn’t implementing dark mode, but finding a color scheme that I liked while still respecting the
visual identity of the light theme, especially for blog post readability. For that, the predominant background needed to
be black – for me, dark mode is truly black. On OLED screens, every turned-off pixel counts! No dark gray. In the end, I
liked the result, but I still need to live with it a bit longer to see if it really works for me.&lt;/p&gt;
&lt;p&gt;So, did you like dark mode? Any suggestions for improvement? Leave your comment down below!&lt;/p&gt;
&lt;h4 id="adapting-generated-themes-from-tools"&gt;
 &lt;a href="#adapting-generated-themes-from-tools" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Adapting generated themes from tools&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h4&gt;
&lt;p&gt;Two widgets didn’t automatically adapt: the &lt;a href="https://giscus.app/"&gt;Giscus&lt;/a&gt; comments and Hugo’s &lt;a href="https://gohugo.io/content-management/syntax-highlighting/"&gt;syntax
highlighting&lt;/a&gt;. Both used fixed themes, but luckily they offer
customization options with just a few lines of JavaScript. Giscus provides an API to adjust the theme, while Hugo’s
syntax highlighting allows using external CSS – you just need to change the &lt;code&gt;href&lt;/code&gt; of the &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; that loads the
highlight CSS.&lt;/p&gt;
&lt;p&gt;I’m even thinking about offering users the option to change just the syntax highlighting theme, with a subtle screen
element that allows for this change. It’s the kind of customization that’s only possible when you have complete control
over the HTML, something that would be quite labor-intensive in WordPress.&lt;/p&gt;
&lt;h3 id="conclusion"&gt;
 &lt;a href="#conclusion" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Conclusion&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;This journey took me about 30 hours, with a good portion of that time dedicated to research. If I had to do it again, it
would definitely be faster now that I’ve learned the parts of the work I didn’t know before. It was an extremely
enjoyable experience getting back into frontend development after a few years, without relying on SPA
libraries/frameworks or even TypeScript. Seeing how modern and accessible CSS and JavaScript have become was refreshing.
Perhaps soon, Sass won’t even be necessary. JavaScript handles a codebase of this size very well, especially with the
TypeScript compiler constantly checking the code, which ends up being a huge help.&lt;/p&gt;
&lt;p&gt;I still have other stories from this update to tell, but since they aren’t directly related to adopting Bootstrap 5,
I’ll save them for another post.&lt;/p&gt;
&lt;p&gt;So, did you know you could do all this with a static site generator? Were you aware of these new JavaScript and CSS
innovations? Drop a comment below and let me know what you think!&lt;/p&gt;
&lt;p&gt;It’s so much fun working with Hugo, Bootstrap, modern JavaScript, and CSS!&lt;/p&gt;</description></item><item><title>Under the hood: creating a site with Hugo</title><link>https://giggio.net/en/blog/under-the-hood-creating-a-site-with-hugo/</link><pubDate>Mon, 24 Mar 2025 15:00:00 -0300</pubDate><author>giggio@giggio.net (Giovanni Bassi)</author><guid>https://giggio.net/en/blog/under-the-hood-creating-a-site-with-hugo/</guid><category>web</category><description>&lt;h3 id="evaluating-the-options"&gt;
 &lt;a href="#evaluating-the-options" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Evaluating the Options&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;When I decided to create a new website, the first option that came to mind was WordPress. I’ve been using it for many
years and I like it. Since I’m also well-versed in infrastructure, setting up a service in the cloud and running it is
very simple. It’s tempting.&lt;/p&gt;
&lt;p&gt;But there are performance challenges. While I maintained the Lambda3 site, we faced several problems. Initially, we
solved them with cache plugins, but over time, even that wasn’t enough, and we started using Cloudflare’s cache, which
in fact solved the problem definitively.&lt;/p&gt;
&lt;p&gt;It was a bit much: PHP + MariaDB + Cloudflare, all just to serve a static site. And it still needed plugins to write in
Markdown, which was what I wanted from the start. Wasn’t there a better solution?&lt;/p&gt;
&lt;p&gt;My main argument in favor of WordPress is its ecosystem of plugins, which is unbeatable. But for a static site, that
seemed excessive. I just wanted a site with a blog and a few sections. Did I really need all of that?&lt;/p&gt;
&lt;p&gt;Giving up WordPress meant having to deliver everything it and its plugins do. My minimum requirements included static
pages, a blog, and RSS. Along the way, other needs emerged: posts categorization and tagging, blocking generative AI
robots, image optimization, among others. Not to mention the common frontend needs, like compiling styles written in
Sass.&lt;/p&gt;
&lt;h3 id="choosing-hugo"&gt;
 &lt;a href="#choosing-hugo" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Choosing Hugo&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Decided to use a static site generator, I went to evaluate options. I wanted fast software, under constant development,
mature and free. It needed to meet my needs and offer flexibility for the future. &lt;a href="https://gohugo.io/"&gt;Hugo&lt;/a&gt; won, but
other good options were &lt;a href="https://astro.build/"&gt;Astro&lt;/a&gt;, &lt;a href="https://jekyllrb.com/"&gt;Jekyll&lt;/a&gt;, and
&lt;a href="https://www.getzola.org/"&gt;Zola&lt;/a&gt;. I evaluated others, but these were the ones I considered the most. It’s been a while
since I made the decision for Hugo; I started the project months ago, and it was shelved for a while.&lt;/p&gt;
&lt;p&gt;Hugo met everything I needed. With almost &lt;a href="https://github.com/gohugoio/hugo"&gt;80 thousand stars on GitHub&lt;/a&gt;, it is a tool
in constant update, with almost 9 thousand commits. Made in Golang, it has good documentation and an active forum for
questions.&lt;/p&gt;
&lt;p&gt;Moreover, it features &lt;a href="https://themes.gohugo.io/"&gt;hundreds of themes&lt;/a&gt; distributed across the internet. Since I’m not a
designer, that was essential. I chose the &lt;a href="https://github.com/StaticMania/roxo-hugo"&gt;Roxo&lt;/a&gt; theme, which I ended up
completely customizing. The only thing that will still give me trouble is updating Bootstrap 4 to version 5, something
that isn’t so trivial.&lt;/p&gt;
&lt;h3 id="whats-good-about-hugo"&gt;
 &lt;a href="#whats-good-about-hugo" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;What’s Good About Hugo&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;h4 id="development-experience"&gt;
 &lt;a href="#development-experience" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Development Experience&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h4&gt;
&lt;p&gt;The development experience with Hugo is excellent. The built-in server, started with &lt;code&gt;hugo server&lt;/code&gt;, runs the site and
hot reloads whenever a file is changed. Changes in CSS don’t even reload the page; they are applied dynamically. On my
machine, a Markdown post takes 22ms to process, and the entire site half a second. On GitHub Actions, the build takes
2.5s, so YMMV. Template development is equally fast; you save and immediately see the result, without delay. The
experience is the same as developing a dynamic site with server-side rendering, and with immediate feedback, somewhat
better than working on an Angular or React SPA.&lt;/p&gt;
&lt;h4 id="flexibility"&gt;
 &lt;a href="#flexibility" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Flexibility&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h4&gt;
&lt;p&gt;With Hugo, you have full control over HTML, CSS, and JavaScript. You don’t depend on whoever wrote a plugin to adapt it
to get to the result you want. There are plugins for Hugo (called modules), but the most common is to
find code snippets to customize as needed. Since it is very extensible, that’s enough. I really liked this approach,
which allows for enormous freedom.&lt;/p&gt;
&lt;h4 id="template-system"&gt;
 &lt;a href="#template-system" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Template System&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h4&gt;
&lt;p&gt;Hugo uses HTML templates to structure the site and Markdown for content. This is useful because metadata remains in the
&lt;a href="https://gohugo.io/content-management/front-matter/"&gt;front matter&lt;/a&gt;, facilitating translations, RSS, and other features.&lt;/p&gt;
&lt;p&gt;Its template system is powerful. The templates are divided into &lt;a href="https://gohugo.io/templates/types/"&gt;types&lt;/a&gt;, such as
sections (lists of pages) and individual pages. There is support for partials, allowing for the componentization of
reusable elements like header and footer.&lt;/p&gt;
&lt;p&gt;The templates can generate any format, such as JSON, XML, or others. This is essential for features like
&lt;a href="https://giggio.net/pt-br/sitemap.xml"&gt;sitemap.xml&lt;/a&gt; and RSS.&lt;/p&gt;
&lt;h4 id="data-features"&gt;
 &lt;a href="#data-features" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Data Features&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h4&gt;
&lt;p&gt;At the footer there is a section of podcasts I listen to and blogs I read. Both were built using a Hugo feature to
reference &lt;a href="https://gohugo.io/content-management/data-sources/"&gt;data sources&lt;/a&gt;. You can create CSV, JSON, TOML, YAML, and
XML files (I used YAML) and use them as a source.&lt;/p&gt;
&lt;p&gt;For example, creating a YAML data file for podcasts, like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Medo e delírio em Brasília&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;https://medoedelirioembrasilia.com.br/&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Hipsters.tech&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;https://www.hipsters.tech/&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And then using it, like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go-html-template" data-lang="go-html-template"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;hugo&lt;/span&gt;&lt;span class="na"&gt;.Data.podcasts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;.url&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;.name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="shortcodes-and-code-fences"&gt;
 &lt;a href="#shortcodes-and-code-fences" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Shortcodes and Code Fences&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h4&gt;
&lt;p&gt;There are also &lt;a href="https://gohugo.io/content-management/shortcodes/"&gt;shortcodes&lt;/a&gt;, which are ways to easily create HTML,
like embedding a YouTube video, or creating code with &lt;em&gt;syntax highlighting&lt;/em&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{&amp;lt; youtube EFAe8W3n2ks &amp;gt;}}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The codes above were generated solely with &lt;a href="https://gohugo.io/content-management/syntax-highlighting/"&gt;&lt;em&gt;code fences&lt;/em&gt;&lt;/a&gt; —
those codes with three or four backticks in the markdown — and they use a shortcode under the hood. This is an example
of code fences with &lt;code&gt;yaml&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;```yaml
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;```&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="taxonomy"&gt;
 &lt;a href="#taxonomy" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Taxonomy&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h4&gt;
&lt;p&gt;Hugo has a taxonomy system, which is a way to categorize your pages. The most common, and what I’m using, are categories
and tags for blog posts, but you could use it to organize anything. I also used it to build pages for types of
contributions (for example, &lt;a href="http://giggio.net/participations/kinds/videos/"&gt;videos&lt;/a&gt; – check its code on
&lt;a href="https://github.com/giggio/giggionet/tree/main/content/participationkinds"&gt;Github&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;The example they use in the &lt;a href="https://gohugo.io/content-management/taxonomies/"&gt;documentation&lt;/a&gt; is with movies and actors.
Thus, a movie page can list its actors. Actor is the &lt;code&gt;taxonomy&lt;/code&gt;, the actor’s name becomes the &lt;code&gt;term&lt;/code&gt;. The cool thing is
that you can create pages for these classifications, both for the taxonomy and for the term. That’s how I have here the
&lt;a href="https://giggio.net/blog/categories/"&gt;categories&lt;/a&gt; page, &lt;a href="https://giggio.net/blog/tags/"&gt;tags&lt;/a&gt;, and each of their terms,
such as the tag &lt;a href="https://giggio.net/blog/tags/blog/"&gt;blog&lt;/a&gt;. The pages, the taxonomies, and the terms are data that you
can manipulate.&lt;/p&gt;
&lt;h4 id="build"&gt;
 &lt;a href="#build" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Build&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h4&gt;
&lt;p&gt;During the build, all pages are generated, the entire site. With the dev server running you can choose whether to
rebuild everything or just the page that changed. It builds the lists of pages, the taxonomies, everything. It also
caches everything it did, such as image manipulations and files it may have downloaded.&lt;/p&gt;
&lt;p&gt;Hugo supports environments. In dev, it doesn’t optimize anything (and this is configurable). The prod build, on the
other hand, can do a series of things. Just look at what happens on my site:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Sass compilation, concatenation of all CSS, tree shaking (with PostCSS), minification, and fingerprinting.&lt;/li&gt;
&lt;li&gt;Optimization of all images, which are converted to webp and in some cases resized.&lt;/li&gt;
&lt;li&gt;HTML minification&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I also perform critical CSS analysis with the &lt;a href="https://github.com/addyosmani/critical"&gt;critical&lt;/a&gt; tool, which is run for
each individual page. I also generate search with &lt;a href="http://pagefind.app/"&gt;pagefind&lt;/a&gt; (you can try it
&lt;a href="https://giggio.net/en/search/"&gt;at /search&lt;/a&gt; – and I admit I spent little time on it, but it’s working).&lt;/p&gt;
&lt;p&gt;This happens locally, if I want to test, and also using the
&lt;a href="https://github.com/giggio/giggionet/blob/main/.github/workflows/build-hugo.yaml"&gt;workflow&lt;/a&gt; from GitHub Actions, which
also performs linting and spell checking (with &lt;a href="https://cspell.org/"&gt;cspell&lt;/a&gt;).&lt;/p&gt;
&lt;h4 id="other-features"&gt;
 &lt;a href="#other-features" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Other Features&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Hugo supports multiple languages, and I’m using that on this site (click the little flag at the top in the header).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The index, at the beginning of this post, was also built using a page metadata, the
&lt;a href="https://gohugo.io/methods/page/tableofcontents/"&gt;.TableOfContents&lt;/a&gt; (check out
&lt;a href="https://github.com/giggio/giggionet/blob/b7be46d67eeb539459c4d51d5a16f15468f086d9/layouts/blog/single.html#L37"&gt;the source code&lt;/a&gt;).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You can use external resources that can be downloaded, such as an image, or a CSS file, or JSON, and then process them
and generate content from them. That’s how I’m generating my &lt;a href="http://giggio.net/robots.txt"&gt;robots.txt&lt;/a&gt; file (see the
source &lt;a href="https://github.com/giggio/giggionet/blob/b7be46d67eeb539459c4d51d5a16f15468f086d9/layouts/robots.txt"&gt;on Github&lt;/a&gt;),
like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go-html-template" data-lang="go-html-template"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;{{-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resources&lt;/span&gt;&lt;span class="na"&gt;.GetRemote&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;https://link/para/robots.json&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;-}}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;(notice the &lt;code&gt;try&lt;/code&gt;?)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The integration with Node.js resources is excellent, thanks to the ability to mount directories and files. You can
specify that any directory should be available for the site, and when accessing the resource, it copies the necessary
files. That’s how I’m doing it with the files from &lt;a href="https://fontawesome.com/search"&gt;Font Awesome&lt;/a&gt;, check out how
the
&lt;a href="https://github.com/giggio/giggionet/blob/b7be46d67eeb539459c4d51d5a16f15468f086d9/config/_default/hugo.yaml#L147C3-L155"&gt;configuration file&lt;/a&gt;
looks:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;module&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;mounts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;node_modules/@fortawesome/fontawesome-free&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;assets/css/fontawesome&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="what-could-improve"&gt;
 &lt;a href="#what-could-improve" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;What Could Improve&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Hugo’s main problem is the lack of debugging. When you’re deep into template developing and something doesn’t work
as you’d like, ideally you would be able to debug the problem while the site is being generated, but the tool does not
offer that.&lt;/p&gt;
&lt;p&gt;We’re stuck with good and old writing to HTML approach, using the &lt;code&gt;debug.Dump&lt;/code&gt; instruction, when the result appears
directly in the HTML, or the &lt;code&gt;warnidf&lt;/code&gt; function, which generates a log in the terminal. It helps, but it’s an experience
that could be improved. I struggled a bit until I discovered that these existed, so here’s a tip for those who are
starting.&lt;/p&gt;
&lt;h3 id="conclusion"&gt;
 &lt;a href="#conclusion" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Conclusion&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;I still have more to say about Hugo, but this post is already long. The main point is that Hugo is an excellent tool for
static sites. It perfectly solved my problem, it’s a pleasure to use, and from what I see, it will continue to serve me
for a long time.&lt;/p&gt;
&lt;p&gt;I still have more to talk about regarding the entire structure around it, such as how I used GitHub Pages, the use of
Nix to structure the tools, and much more, and that will also be saved for future posts.&lt;/p&gt;
&lt;p&gt;What do you think of Hugo? Comment below!&lt;/p&gt;</description></item></channel></rss>