<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Bootstrap on Giovanni Bassi</title><link>https://giggio.net/en/blog/tags/bootstrap/</link><image><url>https://giggio.net/images/base/logo-small.png</url><title>Bootstrap on Giovanni Bassi</title><link>https://giggio.net/en/blog/tags/bootstrap/</link></image><description>Bootstrap 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/tags/bootstrap/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></channel></rss>