<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:georss="http://www.georss.org/georss"
	xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#"
>
  <channel>
    <atom:link href="https://fantinel.dev/rss.xml" rel="self" type="application/rss+xml" />
    <title>Matt Fantinel</title>
    <link>https://fantinel.dev</link>
    <description>I’m a web developer trying to figure out this weird thing called the internet. I write about development, the web, games, music, and whatever else I feel like writing about!
</description>
    <image>
      <url>https://fantinel.dev/favicons/favicon-96x96.png</url>
      <title>Matt Fantinel</title>
      <link>https://fantinel.dev</link>
      <width>96</width>
      <height>96</height>
    </image>
    
    <item>
      <guid>https://fantinel.dev/photography/misty-lake</guid>
      <title>Photography: Misty Lake</title>
      <link>https://fantinel.dev/photography/misty-lake</link>
      <pubDate>Sun, 01 Mar 2026 18:39:23 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>Misty day in Colico, Italy.</p>

        <p>Photo taken on Sunday, 01 Mar 2026</p>

        <p><img src="https://fantinel.dev/cms/media/photographies/colico-foggy-lake.png" alt="Wooden posts float atop the grey waters of a lake, reflecting the fog above it." /></p>
                
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="/cms/media/photographies/colico-foggy-lake.png"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="/cms/media/photographies/colico-foggy-lake.png"/>
      
    </item>
  
    <item>
      <guid>https://fantinel.dev/photography/sunken-mountain</guid>
      <title>Photography: Sunken Mountain</title>
      <link>https://fantinel.dev/photography/sunken-mountain</link>
      <pubDate>Sun, 01 Mar 2026 18:36:19 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>The lake at night is just a soothing as it is eerie.</p>
<p>Taken in Lecco, Italy.</p>

        <p>Photo taken on Saturday, 28 Feb 2026</p>

        <p><img src="https://fantinel.dev/cms/media/photographies/lecco-lake-at-night.png" alt="The lights of a city at night and the shadow of the mountain behind it are perfectly reflected in the dark waters of the lake." /></p>
                
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="/cms/media/photographies/lecco-lake-at-night.png"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="/cms/media/photographies/lecco-lake-at-night.png"/>
      
    </item>
  
    <item>
      <guid>https://myretrotvs.com/</guid>
      <title>Cool Link: MyRetroTVs</title>
      <link>https://myretrotvs.com/</link>
      <pubDate>Sat, 28 Feb 2026 11:45:25 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>by Joey Cato</p>
        
        <p>Miss the feeling if watching TV as a kid? This site allows you to surf channels on a retro TV, and you can even choose the decade you want!</p>
<p>Unfortunately it seems it only has US TV, so I can’t really relate to anything there. Would love a Brazilian version of this!</p>
      ]]></content:encoded>
    </item>
  
    <item>
      <guid>https://fantinel.dev/blog/cool-links-february-2026</guid>
      <title>Cool Links Vol. 20: February, 2026</title>
      <description>Links to the best stuff I&apos;ve read or watched during the month of February, 2026</description>
      <link>https://fantinel.dev/blog/cool-links-february-2026</link>
      <pubDate>Sat, 28 Feb 2026 00:00:00 +0000</pubDate>
      <category>Cool Links</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/cool-links-february-2026">
              read on the site!
            </a>
          </strong>
        </div>

        <p>Hey there! February is over, which means I survived my first winter in the Northern Hemisphere, and my second winter of the year! I moved from Brazil to Italy in September, which means I survived the winter there and came straight to autumn again…</p>
<p>I gotta say, it was way milder than I expected. I know Italy is not really known for harsh winters (even if I am near the Alps), but I was surprised to find out that winters in (southern) Brazil are way tougher.</p>
<p>Anyway, February is gone but it has brought over 7 Cool Links which have been posted here throughout the month. But for convenience, here’s a neat <em>monthly digest</em> for you!</p>
<h2 id="fun">Fun</h2>
<p><strong><a href="https://myretrotvs.com/">MyRetroTVs</a>, by Joey Cato</strong></p>
<p>Miss the feeling if watching TV as a kid? This site allows you to surf channels on a retro TV, and you can even choose the decade you want!</p>
<p>Unfortunately it seems it only has US TV, so I can’t really relate to anything there. Would love a Brazilian version of this!</p>
<p><strong><a href="https://neal.fun/sandboxels">Sandboxels - Experiment with Pixels</a>, by Neal Agarwhal / R74n</strong></p>
<p>This is amazing! This is a pixelated sandbox that allows you to experiment with all kinds of materials and elements, and see how they interact with each other. Each material interacts with others differently, as they would in real life. For example, oil won’t mix with water, but ink will.</p>
<p>A lot of time-consuming potential here, so don’t open it if you have something else to do 😅</p>
<h2 id="dev">Dev</h2>
<p><strong><a href="https://pawelgrzybek.com/more-invoker-commands-and-more-reasons-not-to-use-javascript-please/">More invoker commands, and more reasons not to use JavaScript please</a>, by Paweł Grzybek</strong></p>
<p>HTML is getting more powerful! Now you can add some predefined <em>commands</em> to HTML elements that can do things like open (or close) modals, for example, without a single line of JS.
This article explains really succinctly how that works. The custom commands thing is neat as well.</p>
<h2 id="ai">AI</h2>
<p><strong><a href="https://localghost.dev/blog/stop-generating-start-thinking/">Stop generating, start thinking</a>, by Sophie Koonin</strong></p>
<p>Fantastic piece wielding the power of common sense and highlighting all the struggles that software engineers have with using generative AI on our jobs.</p>
<p>I also use LLMs as a spicy autocomplete (or even a spicy search) and they can be very useful at times. But I can’t replace my thinking with machines, because machines don’t think.</p>
<p><strong><a href="https://theshamblog.com/an-ai-agent-published-a-hit-piece-on-me/">An AI Agent Published a Hit Piece on Me</a>, by Scott Shambaugh</strong></p>
<p>This is both funny and incredibly infuriating. A PR was declined on GitHub for an open-source project because it was made by an AI agent and… the AI agent (or the anonymous person behind it) wrote up a defamatory blog post targeted specifically at the project’s maintainer.</p>
<p>If being an open-source maintainer was already a thankless job, now there’s one more hell to endure.</p>
<h2 id="app">App</h2>
<p><strong><a href="https://www.terrygodier.com/current">Current</a>, by Terry Godier</strong></p>
<p>This is pretty cool! Terry built a RSS reader that rethinks how to approach a continuous feed — or rather, a <em>current</em> — of articles and links. I particularly love how it aims to solve the noisy feed problem, where a source that posts 20 news items a day might drown a really cool article from someone who doesn’t post often, which is a problem I’ve had on every single RSS reader I’ve used (and that I “solved” by unsubscribing from noisy feeds).</p>
<p>It’s a one-time-purchase and on iOS/iPadOS/macOS only, and I haven’t tried it out yet because I just renewed the annual plan for another RSS reader 😅</p>
<h2 id="deep-read">Deep Read</h2>
<p><strong><a href="https://www.youtube.com/watch?v=F0K2DnI1TDk">The Future is an Empty Room</a> (video), by Jacob Geller</strong></p>
<p>Fantastic video essay about loneliness, technology, and the loss of our ability to <em>do</em>.</p>
<p>Beware that there’s some (I assume mild?) spoilers of Death Stranding 1 &#x26; 2 in there.</p>
<h2 id="wrapping-up">Wrapping up</h2>
<p>Thanks for reading and clicking on the links! I get really happy whenever I see people finding out about cool stuff because of me. See you next month!</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/opengraph/cool-links/Q29vbCUyMExpbmtzJTIwVm9sLiUyMDIw/MjAyNi0wMi0yOFQwMCUzQTAwJTNBMDAuMDAwWg==/image.png"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/opengraph/cool-links/Q29vbCUyMExpbmtzJTIwVm9sLiUyMDIw/MjAyNi0wMi0yOFQwMCUzQTAwJTNBMDAuMDAwWg==/image.png"/>          
    </item>
  
    <item>
      <guid>https://www.youtube.com/watch?v=F0K2DnI1TDk</guid>
      <title>Cool Link: The Future is an Empty Room</title>
      <link>https://www.youtube.com/watch?v=F0K2DnI1TDk</link>
      <pubDate>Fri, 27 Feb 2026 18:19:44 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>by Jacob Geller</p>
        
        <p>Fantastic video essay about loneliness, technology, and the loss of our ability to <em>do</em>.</p>
<p>Beware that there’s some (I assume mild?) spoilers of Death Stranding 1 &#x26; 2 in there.</p>
      ]]></content:encoded>
    </item>
  
    <item>
      <guid>https://fantinel.dev/quick-reviews/frankenstein</guid>
      <title>Quick Review: Frankenstein</title>
      <link>https://fantinel.dev/quick-reviews/frankenstein</link>
      <pubDate>Sun, 22 Feb 2026 12:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>Frankenstein <br> 2025, Guillermo del Toro</p>
        <p>My rating: Decent</p>
        <p><img src="https://m.media-amazon.com/images/M/MV5BYzYzNDYxMTQtMTU4OS00MTdlLThhMTQtZjI4NGJmMTZmNmRiXkEyXkFqcGc@._V1_SX300.jpg" /></p>
        
        <p>There’s a great movie in here but I feel the pacing is really off. Each act takes waaaay too long to get going, and by the time they got there I was tired of it already, and ended up not caring as much. Good ending though.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://m.media-amazon.com/images/M/MV5BYzYzNDYxMTQtMTU4OS00MTdlLThhMTQtZjI4NGJmMTZmNmRiXkEyXkFqcGc@._V1_SX300.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://m.media-amazon.com/images/M/MV5BYzYzNDYxMTQtMTU4OS00MTdlLThhMTQtZjI4NGJmMTZmNmRiXkEyXkFqcGc@._V1_SX300.jpg"/>          
    </item>
  
    <item>
      <guid>https://neal.fun/sandboxels</guid>
      <title>Cool Link: Sandboxels - Experiment with Pixels</title>
      <link>https://neal.fun/sandboxels</link>
      <pubDate>Thu, 19 Feb 2026 12:54:04 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>by Neal Agarwhal / R74n</p>
        
        <p>This is amazing! This is a pixelated sandbox that allows you to experiment with all kinds of materials and elements, and see how they interact with each other. Each material interacts with others differently, as they would in real life. For example, oil won’t mix with water, but ink will.</p>
<p>A lot of time-consuming potential here, so don’t open it if you have something else to do 😅</p>
      ]]></content:encoded>
    </item>
  
    <item>
      <guid>https://www.terrygodier.com/current</guid>
      <title>Cool Link: Current</title>
      <link>https://www.terrygodier.com/current</link>
      <pubDate>Thu, 19 Feb 2026 10:16:56 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>by Terry Godier</p>
        
        <p>This is pretty cool! Terry built a RSS reader that rethinks how to approach a continuous feed — or rather, a <em>current</em> — of articles and links. I particularly love how it aims to solve the noisy feed problem, where a source that posts 20 news items a day might drown a really cool article from someone who doesn’t post often, which is a problem I’ve had on every single RSS reader I’ve used (and that I “solved” by unsubscribing from noisy feeds).</p>
<p>It’s a one-time-purchase and on iOS/iPadOS/macOS only, and I haven’t tried it out yet because I just renewed the annual plan for another RSS reader 😅</p>
      ]]></content:encoded>
    </item>
  
    <item>
      <guid>https://theshamblog.com/an-ai-agent-published-a-hit-piece-on-me/</guid>
      <title>Cool Link: An AI Agent Published a Hit Piece on Me</title>
      <link>https://theshamblog.com/an-ai-agent-published-a-hit-piece-on-me/</link>
      <pubDate>Tue, 17 Feb 2026 18:24:31 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>by Scott Shambaugh</p>
        
        <p>This is both funny and incredibly infuriating. A PR was declined on GitHub for an open-source project because it was made by an AI agent and… the AI agent (or the anonymous person behind it) wrote up a defamatory blog post targeted specifically at the project’s maintainer.</p>
<p>If being an open-source maintainer was already a thankless job, now there’s one more hell to endure.</p>
      ]]></content:encoded>
    </item>
  
    <item>
      <guid>https://fantinel.dev/quick-reviews/if-i-had-legs-id-kick-you</guid>
      <title>Quick Review: If I Had Legs I’d Kick You</title>
      <link>https://fantinel.dev/quick-reviews/if-i-had-legs-id-kick-you</link>
      <pubDate>Sat, 14 Feb 2026 12:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>If I Had Legs I’d Kick You <br> 2025, Mary Bronstein</p>
        <p>My rating: Decent</p>
        <p><img src="https://m.media-amazon.com/images/M/MV5BMGRlMjIzMDctNzNkMS00MzEwLTlhZWEtM2FjYmE1ZmFjMzZlXkEyXkFqcGc@._V1_SX300.jpg" /></p>
        
        <p>One more for the “it’s good but I’ll never watch it again” group. The movie is great at portraying negative emotions and Rose’s acting is top notch. What it chooses not to show is just as important as what it does.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://m.media-amazon.com/images/M/MV5BMGRlMjIzMDctNzNkMS00MzEwLTlhZWEtM2FjYmE1ZmFjMzZlXkEyXkFqcGc@._V1_SX300.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://m.media-amazon.com/images/M/MV5BMGRlMjIzMDctNzNkMS00MzEwLTlhZWEtM2FjYmE1ZmFjMzZlXkEyXkFqcGc@._V1_SX300.jpg"/>          
    </item>
  
    <item>
      <guid>https://localghost.dev/blog/stop-generating-start-thinking/</guid>
      <title>Cool Link: Stop generating, start thinking</title>
      <link>https://localghost.dev/blog/stop-generating-start-thinking/</link>
      <pubDate>Tue, 10 Feb 2026 10:21:50 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>by Sophie Koonin</p>
        
        <p>Fantastic piece wielding the power of common sense and highlighting all the struggles that software engineers have with using generative AI on our jobs.</p>
<p>I also use LLMs as a spicy autocomplete (or even a spicy search) and they can be very useful at times. But I can’t replace my thinking with machines, because machines don’t think.</p>
      ]]></content:encoded>
    </item>
  
    <item>
      <guid>https://fantinel.dev/blog/maple-mono-font</guid>
      <title>I have a new favorite coding font</title>
      <description>Maple Mono is free, easy to read, has ligatures, and most importantly is incredibly cute.</description>
      <link>https://fantinel.dev/blog/maple-mono-font</link>
      <pubDate>Tue, 10 Feb 2026 00:00:00 +0000</pubDate>
      <category>Apps</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/maple-mono-font">
              read on the site!
            </a>
          </strong>
        </div>

        <p>Last week I came across a post on Mastodon mentioning <a href="https://font.subf.dev/en/">Maple Mono</a>, a monospaced font. We don’t really have a shortage of fonts, there’s a lot out there (<a href="https://www.monolisa.dev">MonoLisa</a> and <a href="https://monaspace.githubnext.com">Monaspace</a> being my other favorites), but I’d never found one that manages to be so easy to read while also being <em>incredibly cute</em>.</p>
<p>Its main appeal is how rounded the characters are — monospaced fonts are usually blocky due to the constraint of all characters having to have the same width. It also has amazing <em>italics-specific styles</em> and some custom glyphs for commonly-used characters in programming. Seriously, just look at those:</p>
<p><img src="https://fantinel.dev/cms/media/articles/maple-mono-glyphs.png" alt="Screenshot showing the highly stylized and cute characters for characters like @, $, &#x26; and letters like l or k."></p>
<p>I already swapped my code editor and terminal fonts to Maple Mono, and even changed the monospace font on this website to it already! It pairs greatly with the <a href="https://catppuccin.com">Catppuccin colors</a>.</p>
<h2 id="live-example">Live example</h2>
<p>Unless you’re reading on an RSS reader, here’s two examples of the font being used. On the heading above ☝️ and on the code block below 👇</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="html"><code><span class="line"><span style="color:#E1E4E8">&#x3C;</span><span style="color:#85E89D">script</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#B392F0">	$</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">'#example'</span><span style="color:#E1E4E8">).</span><span style="color:#B392F0">on</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">'click'</span><span style="color:#E1E4E8">, (</span><span style="color:#FFAB70">e</span><span style="color:#E1E4E8">) </span><span style="color:#F97583">=></span><span style="color:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#B392F0">		alert</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">'clicked!'</span><span style="color:#E1E4E8">);</span></span>
<span class="line"><span style="color:#E1E4E8">	});</span></span>
<span class="line"><span style="color:#E1E4E8">&#x3C;/</span><span style="color:#85E89D">script</span><span style="color:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#E1E4E8">&#x3C;</span><span style="color:#85E89D">div</span><span style="color:#B392F0"> class</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"pixel-flux"</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#E1E4E8">	&#x3C;</span><span style="color:#85E89D">button</span><span style="color:#B392F0"> id</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"example"</span><span style="color:#E1E4E8">>Click @ me&#x3C;/</span><span style="color:#85E89D">button</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#E1E4E8">&#x3C;/</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#E1E4E8">&#x3C;</span><span style="color:#85E89D">style</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#6A737D">/* Look at those sweet, sweet italics */</span></span>
<span class="line"><span style="color:#B392F0">.pixel-flux</span><span style="color:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#79B8FF">	background</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">peachpuff</span><span style="color:#E1E4E8">;</span></span>
<span class="line"><span style="color:#6A737D">	/* And this gorgeous ampersand */</span></span>
<span class="line"><span style="color:#E1E4E8">	&#x26; #</span><span style="color:#79B8FF">example</span><span style="color:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#79B8FF">		color</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">goldenrod</span><span style="color:#E1E4E8">;</span></span>
<span class="line"><span style="color:#E1E4E8">	}</span></span>
<span class="line"><span style="color:#E1E4E8">}</span></span>
<span class="line"><span style="color:#E1E4E8">&#x3C;/</span><span style="color:#85E89D">style</span><span style="color:#E1E4E8">></span></span></code></pre>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/cms/media/articles/maple-mono.png"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/cms/media/articles/maple-mono.png"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/quick-reviews/sinners</guid>
      <title>Quick Review: Sinners</title>
      <link>https://fantinel.dev/quick-reviews/sinners</link>
      <pubDate>Sun, 08 Feb 2026 12:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>Sinners <br> 2025, Ryan Coogler</p>
        <p>My rating: Loved it!</p>
        <p><img src="https://m.media-amazon.com/images/M/MV5BNjIwZWY4ZDEtMmIxZS00NDA4LTg4ZGMtMzUwZTYyNzgxMzk5XkEyXkFqcGc@._V1_SX300.jpg" /></p>
        
        <p>While the movie takes its time to get going, it is so well worth it. It keeps reinventing itself every few minutes and just keeps getting better and better.</p>
<p>The soundtrack, though, is just on another level. It is <em>so good</em>. One particular scene is gonna stick in my mind for a while, it was freaking amazing. If you watched it, you know which one it is.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://m.media-amazon.com/images/M/MV5BNjIwZWY4ZDEtMmIxZS00NDA4LTg4ZGMtMzUwZTYyNzgxMzk5XkEyXkFqcGc@._V1_SX300.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://m.media-amazon.com/images/M/MV5BNjIwZWY4ZDEtMmIxZS00NDA4LTg4ZGMtMzUwZTYyNzgxMzk5XkEyXkFqcGc@._V1_SX300.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/photography/snow-day</guid>
      <title>Photography: Snow Day</title>
      <link>https://fantinel.dev/photography/snow-day</link>
      <pubDate>Sat, 07 Feb 2026 16:31:35 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>A very cool way to spend the afternoon, just 30min away from home. Lots of kids and dogs playing in the snow!</p>
<p>Taken in Piani Resinelli, Lecco, Italy.</p>

        <p>Photos taken on Saturday, 07 Feb 2026</p>

        <p><img src="https://fantinel.dev/cms/media/photographies/piani-resinelli.jpg" alt="An open area full of snow and people playing on it. There's an alpine village a bit further ahead, and in the background there's the snow-capped Alps. The sky is partly cloudy." /></p>
        <p><img src="/cms/media/photographies/piani-resinelli-2.jpg" alt="A fallen tree in the middle of the snowy forest." /></p>
        <p><img src="/cms/media/photographies/piani-resinelli-3.jpg" alt="Dry trees amidst knee-high snow. The sky is very foggy." /></p>        
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="/cms/media/photographies/piani-resinelli.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="/cms/media/photographies/piani-resinelli.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="/cms/media/photographies/piani-resinelli-2.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="/cms/media/photographies/piani-resinelli-3.jpg"/>
    </item>
  
    <item>
      <guid>https://pawelgrzybek.com/more-invoker-commands-and-more-reasons-not-to-use-javascript-please/</guid>
      <title>Cool Link: More invoker commands, and more reasons not to use JavaScript please</title>
      <link>https://pawelgrzybek.com/more-invoker-commands-and-more-reasons-not-to-use-javascript-please/</link>
      <pubDate>Tue, 03 Feb 2026 15:53:06 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>by Paweł Grzybek</p>
        
        <p>HTML is getting more powerful! Now you can add some predefined <em>commands</em> to HTML elements that can do things like open (or close) modals, for example, without a single line of JS.
This article explains really succinctly how that works. The custom commands thing is neat as well.</p>
      ]]></content:encoded>
    </item>
  
    <item>
      <guid>https://fantinel.dev/quick-reviews/bugonia</guid>
      <title>Quick Review: Bugonia</title>
      <link>https://fantinel.dev/quick-reviews/bugonia</link>
      <pubDate>Mon, 02 Feb 2026 12:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>Bugonia <br> 2025, Yorgos Lanthimos</p>
        <p>My rating: Loved it!</p>
        <p><img src="https://m.media-amazon.com/images/M/MV5BNzIzNWQxMjEtZmQ3MS00OTk2LWFlZjktZDUyYWRkM2M3NWVlXkEyXkFqcGc@._V1_SX300.jpg" /></p>
        
        <p>Gosh I loved this movie. A thriller that managed to make me both really tense and laugh out loud in the same scene without any of these reactions contradicting each other.
It’s absolutely ridiculous in the best way.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://m.media-amazon.com/images/M/MV5BNzIzNWQxMjEtZmQ3MS00OTk2LWFlZjktZDUyYWRkM2M3NWVlXkEyXkFqcGc@._V1_SX300.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://m.media-amazon.com/images/M/MV5BNzIzNWQxMjEtZmQ3MS00OTk2LWFlZjktZDUyYWRkM2M3NWVlXkEyXkFqcGc@._V1_SX300.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/quick-reviews/the-secret-agent</guid>
      <title>Quick Review: The Secret Agent</title>
      <link>https://fantinel.dev/quick-reviews/the-secret-agent</link>
      <pubDate>Sun, 01 Feb 2026 12:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>The Secret Agent <br> 2025, Kleber Mendonça Filho</p>
        <p>My rating: I like it</p>
        <p><img src="https://m.media-amazon.com/images/M/MV5BODliMTdhOTYtYThjMy00OWVhLWFlMjEtYzZlMDhkMjczYjc5XkEyXkFqcGc@._V1_SX300.jpg" /></p>
        
        <p>As a Brazilian I’m extremely proud of this movie, and it’s extremely good in pretty much all regards. Trying to avoid spoilers here, but… it feels like it’s missing a huge part though? With the historical context I have I can kind of imagine why it’s missing, but it felt frustrating, unlike the rest of the movie that’s actually there.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://m.media-amazon.com/images/M/MV5BODliMTdhOTYtYThjMy00OWVhLWFlMjEtYzZlMDhkMjczYjc5XkEyXkFqcGc@._V1_SX300.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://m.media-amazon.com/images/M/MV5BODliMTdhOTYtYThjMy00OWVhLWFlMjEtYzZlMDhkMjczYjc5XkEyXkFqcGc@._V1_SX300.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/blog/cool-links-january-2026</guid>
      <title>Cool Links Vol. 19: January, 2026</title>
      <description>Links to the best stuff I&apos;ve read or watched during the month of January, 2026</description>
      <link>https://fantinel.dev/blog/cool-links-january-2026</link>
      <pubDate>Sat, 31 Jan 2026 00:00:00 +0000</pubDate>
      <category>Cool Links</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/cool-links-january-2026">
              read on the site!
            </a>
          </strong>
        </div>

        <p>Hey there, welcome to the first Cool Links <em>digest</em> of 2026!</p>
<p>Before I start with the links, I want to just mention that I just launched my first app on the App Store! It’s not something I built from scratch, but I took my favorite Mastodon client and wrapped it around nicely into an iOS app that plays along better with the system. I wrote about it <a href="https://fantinel.dev/blog/iphanpy-mastodon-client">on this other blog post</a> from today!</p>
<h2 id="fun">Fun</h2>
<p><strong><a href="https://notneelpatel.xyz/WallpaperThemeConverter/">Wallpaper Theme Converter</a></strong></p>
<p>You might have read before that I love the <a href="https://catppuccin.com">Catppuccin</a> color theme. It’s the same one I use on this website! I also love using that theme on the apps that allow me to, like Obsidian, VS Code and Vivaldi.</p>
<p>To match all of that, I need some wallpapers that fit the palette too. And I just came across this tool that automatically adapts the color palette of any image you upload to a theme of your choice! I’ve had good results with it so far. Definitely makes my desktop look way nicer :)</p>
<p><strong><a href="https://www.fieggen.com/shoelace/index.htm">Ian’s Shoelace Site</a></strong></p>
<p>This is awesome: an entire site dedicated to shoelaces, how to lace, tie or simply learn about them. It even includes the “world’s fastest shoelace knot”, created by the website’s author himself! I gotta try it out.</p>
<p><strong><a href="https://www.youtube.com/watch?v=vWuioJSZiaU">The Truth About Lying (and why we do it)</a> (video), by Miss Chalice</strong></p>
<p>Excuse me, I’m in a Lies of P obsession right now. This video (which contains some light spoilers) talks about the Truth/Lie choices in the game, which are an incredible narrative device, and how that relates to what makes us human.</p>
<p>It’s not surprising that a game based on the story of Pinocchio would have Lies and “becoming human” as parts of its theme, but I really like how they made it all make sense organically and not just like something they tacked in there because they had to.</p>
<h2 id="dev">Dev</h2>
<p><strong><a href="https://blog.jquery.com/2026/01/17/jquery-4-0-0/">jQuery 4.0.0</a></strong></p>
<p>I definitely didn’t expect seeing a new major jQuery release in 2026, but here it is! This is the first major release in 10 years and it doesn’t bring a lot of new things on the surface, but seems to have been a major overhaul behind the scenes. Looks like a future v5 will bring in bigger changes.</p>
<p>jQuery might be old by JS framework standards, but it’s still very useful, and I actually still use it almost daily at my job.</p>
<p><strong><a href="https://lynnandtonic.com/thoughts/entries/case-study-2025-refresh/">Case Study: lynnandtonic.com 2025 refresh</a>, by Lynn Fisher</strong></p>
<p>Lynn talks through a really neat effect added to the latest refresh of their website: a “squishy” animation on the content whenever the window gets resized!</p>
<p>This means you won’t get to see the effect live on your phone, but there’s videos of the effect on the article just in case.</p>
<p>Love the paper-like aesthetic of the website, too.</p>
<h2 id="deep-read">Deep Read</h2>
<p><strong><a href="https://henry.codes/writing/a-website-to-destroy-all-websites/">A Website To End All Websites</a>, by Henry Desroches</strong></p>
<p>This is a very interesting read that compares the internet’s development to that of the <em>automobile</em>, but I also want to highlight the design of the article itself. So good 🤌</p>
<h2 id="wrapping-up">Wrapping up</h2>
<p>Thanks for following along, and see you next month!</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/opengraph/cool-links/Q29vbCUyMExpbmtzJTIwVm9sLiUyMDE5/MjAyNi0wMS0zMVQwMCUzQTAwJTNBMDAuMDAwWg==/image.png"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/opengraph/cool-links/Q29vbCUyMExpbmtzJTIwVm9sLiUyMDE5/MjAyNi0wMS0zMVQwMCUzQTAwJTNBMDAuMDAwWg==/image.png"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/blog/iphanpy-mastodon-client</guid>
      <title>The best Mastodon client now has an iOS version!</title>
      <description>Meet iPhanpy, the best way to use Phanpy on iOS.</description>
      <link>https://fantinel.dev/blog/iphanpy-mastodon-client</link>
      <pubDate>Sat, 31 Jan 2026 00:00:00 +0000</pubDate>
      <category>Apps</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/iphanpy-mastodon-client">
              read on the site!
            </a>
          </strong>
        </div>

        <h2 id="download">Download</h2>
<p>TL;DR: I’ve just released an iOS version of <a href="https://phanpy.social">Phanpy</a> on the App Store and AltStore. It provides better performance and a Liquid Glass icon compared to the installed web app.</p>
<div class="md-center">
<p><a href="https://apps.apple.com/app/iphanpy-for-mastodon/id6755365082" title="button || color=blue">Download (App Store)</a>    <a href="altstore-pal://source?url=https://raw.githubusercontent.com/matfantinel/iphanpy/refs/heads/main/altstore/source.json" title="button || color=teal">Download (AltStore PAL)</a></p>
</div>
<p>Now, if you want some more context…</p>
<h2 id="context">Context</h2>
<p>If you’re reading this, chances are you already know what Mastodon (or the Fediverse) is. If you don’t, the gist of it is that they’re people-owned social media; a bunch of different websites that talk to each other as if they were all the same thing. It’s really nice, and has garnered a lot of interesting people over the years.</p>
<p>One of the best perks of Mastodon is that there’s a bazillion ways to use it. Even though there are official apps (and they’re pretty good), there’s also a ton of 3rd party ones that do an excellent job of satisfying many usage niches. I’ve used a few, but one of them has always stood out: <a href="https://phanpy.social">Phanpy</a>.</p>
<p>Phanpy is a web client for Mastodon developed by <a href="https://mastodon.social/@cheeaun">Chee Aun</a>, and it’s easily become my favorite way to use it. It’s not only pretty and kinda soothing, but it packs a bunch of UX touches that actually make a difference.</p>
<p>It is a web client though, and while on desktop it provides a top-notch experience (and probably on Android too), on iOS the experience was less than ideal, because <a href="https://webventures.rejh.nl/blog/2024/history-of-safari-show-stoppers/">Apple hates web apps</a> (and the web in general). While you can install Phanpy as a full-screen web app via Safari, it has significant performance limitations, keeps reloading when in the background, and now with iOS 26, can’t have adaptive icons to fit in with other apps.</p>
<p>Which is why I decided to take advantage of Phanpy’s open source code, wrap it in a native package for iOS, and distribute it as an iOS app!</p>
<blockquote>
<p>[!info]
Launching iOS apps costs money, but my developer license was fully covered by the good folks at <a href="https://altstore.io">AltStore</a>!
AltStore has been around for a while as a way to sideload apps into an iPhone, but now it’s a proper alternative marketplace for iOS in the EU and Japan! Meaning that it’s the only way to install apps that Apple won’t allow you to.
iPhanpy is available on both the AltStore and the Apple App Store, but I highly recommend checking out the AltStore version if you’re able, so you can also check out the other apps in there 😊</p>
</blockquote>
<h2 id="about-phanpy">About Phanpy</h2>
<p>Phanpy is a web client for Mastodon developed by <a href="https://mastodon.social/@cheeaun">Chee Aun</a>, and it’s easily become my favorite way to use it. It’s not only pretty and kinda soothing, but it packs a bunch of UX touches that actually make a difference.</p>
<p>These touches can be small, but make all the difference. For example, whenever your timeline includes a reply to another post, Phanpy will show a preview of the post being replied to (something that is challenging to do on chronological feeds):</p>
<p><img src="https://fantinel.dev/cms/media/articles/phanpy-replies-in-timeline.png" alt="Screenshot of Phanpy, showing a post replying to another. The post being replied to is show non top of the reply, but on a very compact, one-line form."></p>
<p>Some can be also small, but very opinionated, like the fact that Alt text is automatically displayed below an image (if it’s short enough to fit in there). This highlights how alt text is not just for vision-impaired users; it’s also a way to provide context to the images, or draw attention to the thing you specifically wanted to highlight.</p>
<p><img src="https://fantinel.dev/cms/media/articles/phanpy-alt-text.png" alt="Screenshot of Phanpy, showing a post with an image, and the image has its alt text displayed right below it, almost as if it were a caption."></p>
<p>But the UX touches can also be big: one of my favorites is “Catch-up”, perfect for when you haven’t opened the app in a while and want to see if you’ve missed anything important or want to quickly check posts by the people you care about the most. You choose a period of time and Phanpy will grab all the posts from that period and display them in a highly filterable view.</p>
<p>In the screenshot below, I’m seeing the posts from my timeline from the past 8 hours:</p>
<p><img src="https://fantinel.dev/cms/media/articles/phanpy-catch-up.png" alt="Screenshot of Phanpy showing a list of posts on the bottom, with a bunch of filters on top, like type of post, author, and sorting by date, likes, boosts."></p>
<p>These are definitely not all the nice touches in there, just my favorites. Definitely check out the main <a href="https://phanpy.social">Phanpy website</a> to learn about the rest.</p>
<p>Since Phanpy is <a href="https://github.com/cheeaun/phanpy">open source</a> and under the MIT license, this means I could create a fork of it and add whatever changes are necessary to get it to work as an installable iOS app (I did get Chee Aun’s blessing for doing this, though, as I felt it was <em>polite</em> to ask). Why is what I did: this is <a href="https://github.com/matfantinel/iphanpy">iPhanpy’s source code</a>, which I try to keep as up-to-date as possible with the main one.</p>
<p>The repo explains the technical side of the implementation in case you’re curious.</p>
<h2 id="download-links">Download links</h2>
<div class="md-center">
<p><a href="https://apps.apple.com/app/iphanpy-for-mastodon/id6755365082" title="button || color=blue">Download (App Store)</a>    <a href="altstore-pal://source?url=https://raw.githubusercontent.com/matfantinel/iphanpy/refs/heads/main/altstore/source.json" title="button || color=teal">Download (AltStore PAL)</a></p>
</div>
<h2 id="faqs">FAQs</h2>
<h3 id="why-not-just-use-the-web-app">Why not just use the web app?</h3>
<p>While the installed web app (PWA) on iOS is perfectly usable, in my experience it has a few annoyances:</p>
<ul>
<li>It almost always fully reloaded when I opened it, meaning opening the web app would usually result in a blank screen for a couple seconds. This happens because iOS doesn’t keep websites in memory as long as it keeps apps;</li>
<li>Scrolling would sometimes get stuttery, even on a recent iPhone. iOS limits how much juice web apps get, so Phanpy wasn’t as smooth as it could be;</li>
<li>Visual glitches: I use my phone on an auto light/dark mode depending on time of day, and the status bar (the bar behind the clock/battery icon) would often get stuck in the wrong color;</li>
<li>The home screen icon doesn’t adapt to the light/dark/clear modes, meaning Phanpy always stood out in a bad way.</li>
</ul>
<p>As I said, these were mostly <em>annoyances</em>, not dealbreakers. Still, iPhanpy fixes all of those.</p>
<h3 id="android-version">Android version?</h3>
<p>Technically, it’s possible to make one, yes. It wasn’t a priority for me because I don’t use Android (and don’t have a device to test with), but I’m not saying it won’t happen. I just need time and volition to work on it. Maybe once I get a bit more comfortable with keeping the iOS one up-to-date.</p>
<p>If you know a little about Capacitor and how to build/test Android apps, feel free to try it and open a PR with your findings!</p>
<h3 id="i-cant-log-in-to-my-instance">I can’t log in to my instance!</h3>
<p><del>I’m aware that logging in to some specific instances is not working on the iOS app, even though it works on the web app. I’m looking into it. What I know so far is that it seems to be related to either an older Mastodon version (&#x3C; 4.3.0) or to some specific security settings being enabled/disabled. It will be fixed in a future update.</del></p>
<p>Update: this issue has been resolved in version 1.5.2. Make sure you’re running the latest version!</p>
<h3 id="who-do-i-report-issues-to">Who do I report issues to?</h3>
<p>If you’re using the installed version, iPhanpy, then probably to me. You can do it either on the <a href="https://github.com/matfantinel/iphanpy">GitHub repo</a> if you have an account there, or just shoot me a message on <a href="https://hachyderm.io/@fantinel">my Mastodon account</a>.</p>
<p>For feature requests, it’s better to do it on <a href="https://github.com/cheeaun/phanpy">the original project’s repo</a>.</p>
<p>Keep in mind that Phanpy and iPhanpy are hobby projects to both of us and we have no obligation to work on your issue. Be sensible.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/cms/media/articles/iphanpy-mastodon-client.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/cms/media/articles/iphanpy-mastodon-client.jpg"/>          
    </item>
  
    <item>
      <guid>https://lynnandtonic.com/thoughts/entries/case-study-2025-refresh/</guid>
      <title>Cool Link: Case Study: lynnandtonic.com 2025 refresh</title>
      <link>https://lynnandtonic.com/thoughts/entries/case-study-2025-refresh/</link>
      <pubDate>Sun, 25 Jan 2026 11:32:53 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>by Lynn Fisher</p>
        
        <p>Lynn talks through a really neat effect added to the latest refresh of their website: a “squishy” animation on the content whenever the window gets resized!</p>
<p>This means you won’t get to see the effect live on your phone, but there’s videos of the effect on the article just in case.</p>
<p>Love the paper-like aesthetic of the website, too.</p>
      ]]></content:encoded>
    </item>
  
    <item>
      <guid>https://fantinel.dev/blog/one-more-feed-to-scroll</guid>
      <title>One Feed to Rule Them All</title>
      <description>And in my website bind them</description>
      <link>https://fantinel.dev/blog/one-more-feed-to-scroll</link>
      <pubDate>Wed, 21 Jan 2026 00:00:00 +0000</pubDate>
      <category>Meta</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/one-more-feed-to-scroll">
              read on the site!
            </a>
          </strong>
        </div>

        <p>Last weekend I quietly launched another section in this website: a <a href="https://fantinel.dev/timeline">chronological feed</a> that aggregates everything I post here, e.g. blog posts, quick reviews, photographies and cool links, all in one place.</p>
<p>I’ve struggled to think of a good name for it. I felt like “Timeline” could mean something else, as people usually use “Timeline” on their websites to talk about their life timeline or something like that. So, for now, I just went with “Feed”. I like it because it’s very creative.</p>
<p>The reasoning behind it is that I found it a bit annoying that you had to open 4 pages to see if I had posted anything new (unless you use RSS, which makes you awesome 🫶). Now, you only need to open one! I am sure everyone will make good use of these precious seconds that are being saved!</p>
<p>This is something I had designed quite a while ago, but always postponed building for some unknown reason. I took huge inspiration from the way <a href="https://phanpy.social">Phanpy</a> groups notifications by date, and did the same here. The initial idea was to have some fancy filtering, but I chose to skip it just so that I could get it out there. Maybe someday.</p>
<p>Dealing with the data fetching was also quite easy: even though Astro handles each post type as a separate collection, the fact that my website is static means I don’t have to worry much about performance: I just query for <em>everything</em>, sort by date, then only get the posts I need (12-ish per page). It’s only gonna run once anyway, so I don’t have to do anything fancy for that query. Simplicity is awesome.</p>
<p>I also wanted to make sure days don’t get cut off on pagination. i.e. if I’m showing 12 items per page but the 13th item is in the same day as the 12th one, they’re gonna show up together. Again, easily doable because I load all posts <em>before</em> building the pages. It’d probably be very annoying to do otherwise.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/cms/media/articles/one-feed-to-rule-them-all.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/cms/media/articles/one-feed-to-rule-them-all.jpg"/>          
    </item>
  
    <item>
      <guid>https://blog.jquery.com/2026/01/17/jquery-4-0-0/</guid>
      <title>Cool Link: jQuery 4.0.0</title>
      <link>https://blog.jquery.com/2026/01/17/jquery-4-0-0/</link>
      <pubDate>Sun, 18 Jan 2026 21:01:11 +0000</pubDate>
      <content:encoded><![CDATA[
        
        
        <p>I definitely didn’t expect seeing a new major jQuery release in 2026, but here it is! This is the first major release in 10 years and it doesn’t bring a lot of new things on the surface, but seems to have been a major overhaul behind the scenes. Looks like a future v5 will bring in bigger changes.</p>
<p>jQuery might be old by JS framework standards, but it’s still very useful, and I actually still use it almost daily at my job.</p>
      ]]></content:encoded>
    </item>
  
    <item>
      <guid>https://www.youtube.com/watch?v=vWuioJSZiaU</guid>
      <title>Cool Link: The Truth About Lying (and why we do it) (video)</title>
      <link>https://www.youtube.com/watch?v=vWuioJSZiaU</link>
      <pubDate>Sun, 18 Jan 2026 19:00:10 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>by Miss Chalice</p>
        
        <p>Excuse me, I’m in a Lies of P obsession right now. This video (which contains some light spoilers) talks about the Truth/Lie choices in the game, which are an incredible narrative device, and how that relates to what makes us human.</p>
<p>It’s not surprising that a game based on the story of Pinocchio would have Lies and “becoming human” as parts of its theme, but I really like how they made it all make sense organically and not just like something they tacked in there because they had to.</p>
      ]]></content:encoded>
    </item>
  
    <item>
      <guid>https://fantinel.dev/quick-reviews/lies-of-p-overture</guid>
      <title>Quick Review: Lies of P Overture</title>
      <link>https://fantinel.dev/quick-reviews/lies-of-p-overture</link>
      <pubDate>Sun, 18 Jan 2026 12:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>Lies of P Overture <br> 2025, Neowiz</p>
        <p>My rating: Loved it!</p>
        <p><img src="https://m.media-amazon.com/images/M/MV5BNGEwNDYzYWMtOWVjNC00N2NjLTkwMTUtODI2MTAyMThmNWQ5XkEyXkFqcGc@._V1_FMjpg_UX1000_.jpg" /></p>
        
        <p>This prequel to Lies of P is everything that was good about the base game, but elevated into a 12-hour long campaign of pure awesomeness.</p>
<p>The story, level design, bosses and weapons are just state of the art. The final chapter had my eyes all teared up all the time.</p>
<p>Each chapter even has self-contained small side quests that don’t give much of a challenge but are great pieces of world-building to make Krat an even cooler setting.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://m.media-amazon.com/images/M/MV5BNGEwNDYzYWMtOWVjNC00N2NjLTkwMTUtODI2MTAyMThmNWQ5XkEyXkFqcGc@._V1_FMjpg_UX1000_.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://m.media-amazon.com/images/M/MV5BNGEwNDYzYWMtOWVjNC00N2NjLTkwMTUtODI2MTAyMThmNWQ5XkEyXkFqcGc@._V1_FMjpg_UX1000_.jpg"/>          
    </item>
  
    <item>
      <guid>https://henry.codes/writing/a-website-to-destroy-all-websites/</guid>
      <title>Cool Link: A Website To End All Websites</title>
      <link>https://henry.codes/writing/a-website-to-destroy-all-websites/</link>
      <pubDate>Sat, 17 Jan 2026 10:15:58 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>by Henry Desroches</p>
        
        <p>This is a very interesting read that compares the internet’s development to that of the <em>automobile</em>, but I also want to highlight the design of the article itself. So good 🤌</p>
      ]]></content:encoded>
    </item>
  
    <item>
      <guid>https://www.fieggen.com/shoelace/index.htm</guid>
      <title>Cool Link: Ian&apos;s Shoelace Site</title>
      <link>https://www.fieggen.com/shoelace/index.htm</link>
      <pubDate>Sat, 17 Jan 2026 10:04:48 +0000</pubDate>
      <content:encoded><![CDATA[
        
        
        <p>This is awesome: an entire site dedicated to shoelaces, how to lace, tie or simply learn about them. It even includes the “world’s fastest shoelace knot”, created by the website’s author himself! I gotta try it out.</p>
      ]]></content:encoded>
    </item>
  
    <item>
      <guid>https://fantinel.dev/quick-reviews/threads</guid>
      <title>Quick Review: Threads</title>
      <link>https://fantinel.dev/quick-reviews/threads</link>
      <pubDate>Sun, 11 Jan 2026 12:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>Threads <br> 1984, Mick Jackson</p>
        <p>My rating: I like it</p>
        <p><img src="https://m.media-amazon.com/images/M/MV5BZmMwNTMyZjYtZDNlZS00OGQ2LTk0MzQtZTgyNDkxNDQ4YjQxXkEyXkFqcGc@._V1_SX300.jpg" /></p>
        
        <p><em>Raw</em> and <em>gut-wrenching</em> are perfect adjectives for this one. It wants to get its message across and pulls no punches in doing so: nobody wins a nuclear war.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://m.media-amazon.com/images/M/MV5BZmMwNTMyZjYtZDNlZS00OGQ2LTk0MzQtZTgyNDkxNDQ4YjQxXkEyXkFqcGc@._V1_SX300.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://m.media-amazon.com/images/M/MV5BZmMwNTMyZjYtZDNlZS00OGQ2LTk0MzQtZTgyNDkxNDQ4YjQxXkEyXkFqcGc@._V1_SX300.jpg"/>          
    </item>
  
    <item>
      <guid>https://notneelpatel.xyz/WallpaperThemeConverter/</guid>
      <title>Cool Link: Wallpaper Theme Converter</title>
      <link>https://notneelpatel.xyz/WallpaperThemeConverter/</link>
      <pubDate>Thu, 08 Jan 2026 20:08:20 +0000</pubDate>
      <content:encoded><![CDATA[
        
        
        <p>You might have read before that I love the <a href="https://catppuccin.com">Catppuccin</a> color theme. It’s the same one I use on this website! I also love using that theme on the apps that allow me to, like Obsidian, VS Code and Vivaldi.</p>
<p>To match all of that, I need some wallpapers that fit the palette too. And I just came across this tool that automatically adapts the color palette of any image you upload to a theme of your choice! I’ve had good results with it so far. Definitely makes my desktop look way nicer :)</p>
      ]]></content:encoded>
    </item>
  
    <item>
      <guid>https://fantinel.dev/quick-reviews/the-lord-of-the-rings---the-two-towers-extended-edition</guid>
      <title>Quick Review: The Lord of the Rings - The Two Towers (Extended Edition)</title>
      <link>https://fantinel.dev/quick-reviews/the-lord-of-the-rings---the-two-towers-extended-edition</link>
      <pubDate>Tue, 06 Jan 2026 12:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>The Lord of the Rings - The Two Towers (Extended Edition) <br> 2002, Peter Jackson</p>
        <p>My rating: Loved it!</p>
        <p><img src="https://m.media-amazon.com/images/M/MV5BMGQxMDdiOWUtYjc1Ni00YzM1LWE2NjMtZTg3Y2JkMjEzMTJjXkEyXkFqcGc@._V1_SX300.jpg" /></p>
        
        <p>While this is probably my least favorite of the trilogy, it’s still one of the best movies ever made. An outstanding achievement in all regards. I can’t believe these movies exist.</p>
<p>The extra scenes for the extended edition are also pretty neat, and add some extra context that is very welcome for a fan like me. The overall movie pacing gets a bit worse which is why it’s totally understandable why they were cut from the theatrical release. And another reason I’m glad the extended editions exist!</p>
<p>I just wish the final Saruman scene was in this movie instead of the next one. I feel like it’d wrap around this movie’s story a bit more nicely.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://m.media-amazon.com/images/M/MV5BMGQxMDdiOWUtYjc1Ni00YzM1LWE2NjMtZTg3Y2JkMjEzMTJjXkEyXkFqcGc@._V1_SX300.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://m.media-amazon.com/images/M/MV5BMGQxMDdiOWUtYjc1Ni00YzM1LWE2NjMtZTg3Y2JkMjEzMTJjXkEyXkFqcGc@._V1_SX300.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/photography/st-peter-church-in-gramado</guid>
      <title>Photography: St. Peter Church in Gramado</title>
      <link>https://fantinel.dev/photography/st-peter-church-in-gramado</link>
      <pubDate>Mon, 05 Jan 2026 18:20:17 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>Downtown Gramado is proof that something can be both overrated and incredibly beautiful. Nothing beats it on a rainy winter night.</p>
<p>Taken in Gramado, RS, Brazil.</p>

        <p>Photo taken on Friday, 20 Jun 2025</p>

        <p><img src="https://fantinel.dev/cms/media/photographies/gramado-church.jpg" alt="Beautiful stone cathedral with really nice lighting. Cloudy skies in the background" /></p>
                
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="/cms/media/photographies/gramado-church.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="/cms/media/photographies/gramado-church.jpg"/>
      
    </item>
  
    <item>
      <guid>https://fantinel.dev/photography/cozy-house-by-the-lake</guid>
      <title>Photography: Cozy house by the lake</title>
      <link>https://fantinel.dev/photography/cozy-house-by-the-lake</link>
      <pubDate>Mon, 05 Jan 2026 18:17:40 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>Araucárias are my favorite tree. They are charming, look very unique, and their seeds make for amazing winter food.</p>
<p>Taken in Nova Petrópolis, RS, Brazil.</p>

        <p>Photo taken on Friday, 20 Jun 2025</p>

        <p><img src="https://fantinel.dev/cms/media/photographies/aldeia-do-imigrante.jpg" alt="Real pretty house among araucaria trees, standing tall in front of a lake. The sky is cloudy" /></p>
                
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="/cms/media/photographies/aldeia-do-imigrante.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="/cms/media/photographies/aldeia-do-imigrante.jpg"/>
      
    </item>
  
    <item>
      <guid>https://fantinel.dev/photography/umbrellas</guid>
      <title>Photography: Umbrellas</title>
      <link>https://fantinel.dev/photography/umbrellas</link>
      <pubDate>Mon, 05 Jan 2026 18:13:31 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>Taken on a very cute little street in Nova Petrópolis, RS, Brazil.</p>

        <p>Photo taken on Sunday, 17 Aug 2025</p>

        <p><img src="https://fantinel.dev/cms/media/photographies/umbrellas.jpg" alt="Photo taken from below of a street covered by colorful umbrellas and light bulbs, woven beneath the roofs. It’s night time" /></p>
                
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="/cms/media/photographies/umbrellas.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="/cms/media/photographies/umbrellas.jpg"/>
      
    </item>
  
    <item>
      <guid>https://fantinel.dev/photography/paçoquinha-on-the-beach</guid>
      <title>Photography: Paçoquinha on the beach</title>
      <link>https://fantinel.dev/photography/paçoquinha-on-the-beach</link>
      <pubDate>Mon, 05 Jan 2026 18:08:48 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>She was both terrified of the sea and ecstatic about the sand.</p>
<p>Taken in Arroio do Sal, RS, Brazil.</p>

        <p>Photo taken on Friday, 21 Feb 2025</p>

        <p><img src="https://fantinel.dev/cms/media/photographies/sunset-beach-dog.jpg" alt="A dog walks on the beach, with the sunset in the background." /></p>
                
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="/cms/media/photographies/sunset-beach-dog.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="/cms/media/photographies/sunset-beach-dog.jpg"/>
      
    </item>
  
    <item>
      <guid>https://fantinel.dev/photography/mont-blanc-goats</guid>
      <title>Photography: Mont Blanc Goats</title>
      <link>https://fantinel.dev/photography/mont-blanc-goats</link>
      <pubDate>Wed, 31 Dec 2025 00:28:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>When I saw this lighting I really wanted to try capturing it, and I think it turned out just right 👌</p>
<p>(I did see real mountain goats around there, but the ones in the picture are just props)</p>

        <p>Photo taken on Tuesday, 30 Dec 2025</p>

        <p><img src="https://fantinel.dev/cms/media/photographies/mont-blanc-goats.png" alt="Daytime picture of mountain goats in the snow, with the Mont Blanc mountain in the background. The sky is beautifully lit by the setting sun." /></p>
                
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="/cms/media/photographies/mont-blanc-goats.png"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="/cms/media/photographies/mont-blanc-goats.png"/>
      
    </item>
  
    <item>
      <guid>https://fantinel.dev/blog/cool-links-december-2025</guid>
      <title>Cool Links Vol. 18: December, 2025</title>
      <description>Links to the best stuff I&apos;ve read or watched during the month of December, 2025</description>
      <link>https://fantinel.dev/blog/cool-links-december-2025</link>
      <pubDate>Wed, 31 Dec 2025 00:00:00 +0000</pubDate>
      <category>Cool Links</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/cool-links-december-2025">
              read on the site!
            </a>
          </strong>
        </div>

        <p>Hey there! It’s time for the last post of 2025. I posted <a href="https://fantinel.dev/blog/2025-year-in-review">my yearly retrospective</a> last week, and with this latest digest of Cool Links, I’m officially wrapping up the year.</p>
<p>Not many links this months as I’ve been quite busy, but the ones in here are extra cool.</p>
<h2 id="fun">Fun</h2>
<p><strong><a href="https://www.youtube.com/watch?v=MVUuoXAkuUg">The greatest in-camera effect of all time (video)</a>, by Corridor Crew</strong></p>
<p>Ok, this video is <strong>amazing.</strong> Corridor Crew recreates the amazing practical effects from the first Lord of the Rings movie (the forced perspective ones with Gandalf and the hobbits), but not only that, there’s amazing storytelling on how it was made, all the cinema history before it, and why it has never been done again since then.</p>
<p><strong><a href="https://cluesbysam.com/">Clues by Sam</a></strong></p>
<p>Neat little daily browser puzzle game where you use clues to find out who’s a criminal and who’s innocent.</p>
<p><strong><a href="https://posthog.com">PostHog Website</a></strong></p>
<p>This website is <em>so cool!</em> It’s the kind of thing that you’d imagine was a personal website, but it’s actually a marketing page! A marketing team actually sat down and planned this out! I thought no such thing as fun marketing existed. Glad to be proven wrong.</p>
<p><strong><a href="https://neal.fun/size-of-life/">Size of Life</a>, by Neal Agarwal</strong></p>
<p>Another great page by Neal Agarwal; this one lets you see life in all its different sizes.</p>
<h2 id="dev--design">Dev / Design</h2>
<p><strong><a href="https://www.nicchan.me/blog/the-f-off-contact-page/">The f* off contact page</a>, by Nic Chan</strong></p>
<p>Great post about how clients hire experts to solve a problem, then completely ignore their expertise and try to copy what the big ones do. But they’re not big.</p>
<p>The “f*** off contact page” concept is amazing, too. It’s 100% real and out there, with more and more companies doing it (either intentionally or by just wanting to copy what others do).</p>
<p>Also, Nic Chan’s website is a treasure. It’s already been featured as a cool link here before but I wanted to point it out again. So cool!</p>
<p><strong><a href="https://chrome.dev/css-wrapped-2025/">CSS Wrapped 2025</a>, by The Chrome DevRel Team</strong></p>
<p>CSS is my favorite language and 2025 was <em>amazing</em> for it! The Chrome team built this page highlighting all the new exciting stuff that happened to CSS this year. I’ve used some of it but sadly still have to wait for other browsers to catch up before doing it on any serious work 😭</p>
<p>I recommend opening this in a Chromium-based browser so you can try it out firsthand, but there are video recordings of the features in case you’re unable to.</p>
<h2 id="tech">Tech</h2>
<p><strong><a href="https://www.youtube.com/watch?v=Jh9pFp1oM7E">I shrunk down into an M5 chip (video)</a>, by Marques Brownlee</strong></p>
<p>I usually love these videos that deal with the scale of things, and getting one from MKBHD was a surprise for sure, but a welcome one.</p>
<p>It’s incredible how far technology has come, and it’s a testament of how humans can achieve incredible things when we want to.</p>
<h2 id="wrapping-up-the-year">Wrapping up (the year)</h2>
<p>And with that, 2025 is wrapped up for this blog! I don’t know how your 2025 was, but I hope 2026 is better in every single way! Thanks for reading!</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/opengraph/cool-links/Q29vbCUyMExpbmtzJTIwVm9sLiUyMDE4/MjAyNS0xMi0zMVQwMCUzQTAwJTNBMDAuMDAwWg==/image.png"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/opengraph/cool-links/Q29vbCUyMExpbmtzJTIwVm9sLiUyMDE4/MjAyNS0xMi0zMVQwMCUzQTAwJTNBMDAuMDAwWg==/image.png"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/quick-reviews/pluribus-season-1</guid>
      <title>Quick Review: Pluribus (Season 1)</title>
      <link>https://fantinel.dev/quick-reviews/pluribus-season-1</link>
      <pubDate>Mon, 29 Dec 2025 12:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>Pluribus (Season 1) <br> 2025, Vince Gilligan</p>
        <p>My rating: Decent</p>
        <p><img src="https://m.media-amazon.com/images/M/MV5BOWNlM2E1MDMtYmI5MS00NDQ1LWI3NTctM2VlNjQ5OTAxYTNmXkEyXkFqcGc@._V1_SX300.jpg" /></p>
        
        <p>There’s a lot of quality in this show, but the glacial pacing (and how long it takes for the main character to develop any semblance of likeability) made me really struggle to <em>care</em> about anything that happened.</p>
<p>Might be better when binge-watched, but the weekly episode releases really did it a disservice.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://m.media-amazon.com/images/M/MV5BOWNlM2E1MDMtYmI5MS00NDQ1LWI3NTctM2VlNjQ5OTAxYTNmXkEyXkFqcGc@._V1_SX300.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://m.media-amazon.com/images/M/MV5BOWNlM2E1MDMtYmI5MS00NDQ1LWI3NTctM2VlNjQ5OTAxYTNmXkEyXkFqcGc@._V1_SX300.jpg"/>          
    </item>
  
    <item>
      <guid>https://www.youtube.com/watch?v=Jh9pFp1oM7E</guid>
      <title>Cool Link: I shrunk down into an M5 chip</title>
      <link>https://www.youtube.com/watch?v=Jh9pFp1oM7E</link>
      <pubDate>Mon, 29 Dec 2025 00:38:04 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>by Marques Brownlee</p>
        
        <p>I usually love these videos that deal with the scale of things, and getting one from MKBHD was a surprise for sure, but a welcome one.</p>
<p>It’s incredible how far technology has come, and it’s a testament of how humans can achieve incredible things when we want to.</p>
      ]]></content:encoded>
    </item>
  
    <item>
      <guid>https://fantinel.dev/blog/2025-year-in-review</guid>
      <title>2025: A Big Year</title>
      <description>The craziest year of my life.</description>
      <link>https://fantinel.dev/blog/2025-year-in-review</link>
      <pubDate>Mon, 22 Dec 2025 00:00:00 +0000</pubDate>
      <category>Retrospective</category><category>Meta</category><category>Personal</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/2025-year-in-review">
              read on the site!
            </a>
          </strong>
        </div>

        <p>I can say without any shadow of a doubt that 2025 has been the craziest year of my life. There were many changes for me, and those changes were <em>huge</em>. Like, <em>ocean-sized huge</em>.</p>
<p>For the past 6 years, I’ve been writing retrospectives about what I did, learned, felt, watched, played or listened to. It’s a great way to reflect on things and figure out ways to make the next year better.</p>
<p>If you’d like, you can read my posts for <a href="https://fantinel.dev/blog/2019-year-in-review">2019</a>, <a href="https://fantinel.dev/blog/2020-year-in-review">2020</a>, <a href="https://fantinel.dev/blog/2021-year-in-review">2021</a>, <a href="https://fantinel.dev/blog/2022-year-in-review">2022</a>, <a href="https://fantinel.dev/blog/2023-year-in-review">2023</a> and <a href="https://fantinel.dev/blog/2024-year-in-review">2024</a>.</p>
<h2 id="last-years-predictions">Last Year’s Predictions</h2>
<p>First, let’s have a look at how I thought this year was gonna be like a year ago, when I wrote last year’s post.</p>
<blockquote>
<p>Well, first of all the plan for 2025 is to move. We’re moving out of Brazil to give living abroad another chance.</p>
</blockquote>
<p>✅ Yep, that happened alright.</p>
<blockquote>
<p>Another not-as-big-but-still-big goal is to actually go ahead with my website’s redesign.</p>
</blockquote>
<p>✅ One more I got right! This year I finally worked on the big redesign, which I’ve been refining ever since.</p>
<blockquote>
<p>I definitely want to continue with the monthly links and adding some non-blog content to the site (like the movie quick reviews).</p>
</blockquote>
<p>✅ That’s 3 for 3! While I actually wrote less articles on the blog compared to last year, I now have so much more stuff in here, with dedicated sections for <a href="https://fantinel.dev/quick-reviews">Quick Reviews</a>, <a href="https://fantinel.dev/cool-links">Cool Links</a> and even <a href="https://fantinel.dev/photography">Photography</a>.</p>
<p>Who would’ve thought that the secret to getting new year predictions right is to predict easier stuff?</p>
<h2 id="personal-life">Personal Life</h2>
<h3 id="half-the-world-away">Half the World Away</h3>
<p>This September, my wife, my dog and I moved from my hometown in the south of Brazil to Italy. It had been something on our minds for a while (I even mentioned it being the plan on last year’s retrospective), but I don’t think any kind of planning we had back them would do justice to the amount of work and stress it is to move across the globe.</p>
<p>First, the moment you start planning such a big change, your life already changes significantly. When you go out and see a cool piece of decor, furniture or anything that takes up space, you start remembering that you’re not gonna make use of it, and everything you have is one extra thing you gotta get rid of soon.</p>
<p>Then, you start going around the area you live and see all sorts of nice things. But you have in mind that it might as well be the last time you check out that place, at least for a while.</p>
<p>Friends and family make plans for next year. They include you out of habit, and you wonder if it’s a good idea to remind them you won’t be there, or if it’s better to just nod. You wonder if you’ll be able to stay in contact with those people.</p>
<p>And as the move date draws closer, you gotta actually do stuff. You gotta figure out what to do with all the things, clothes, furniture, apartment you have. What should you bring with you? What do you wanna keep safe at your parents’? What can you sell? What can you donate?</p>
<p>You also gotta start figuring out what to do once you get to your destination. Luckily for me, my brother was already here, so I didn’t have to worry about finding a place before even getting into the plane.</p>
<p>It is a long, taxing process. At least it was for me. The week before the move was also very bittersweet; seeing everyone once more before the move is nice, having no idea when you’re seeing them again is not.</p>
<p>Getting to Italy, I was finally able to see my brother after 3 years. He’s moved to Italy back in 2022 and has been enjoying life here. We’ve always been extremely close, so these past 3 years apart have definitely been hard. We stayed at his place for a little over a month, while we looked for a place of our own to live.</p>
<p>And we found it! We moved to an apartment (which we actually found the online posting for while we were still in Brazil) in the city of <strong>Lecco</strong>. It’s a very scenic city situated right between the Alps and Lake Como, so you have an amazing view no matter where you are in it.</p>
<p>Lake Como held some meaning to my wife and I because we spent our honeymoon in Bellagio, also around the same lake. So the fact that we live in a place that amazed us so much a few years ago is dreamy, actually. I love walking around here.</p>
<p>There’s definitely been some hard moments, but they were <em>hard, not bad</em>. We are settled now, slowly rebuilding our home and experiencing firsthand what life halfway across the world is like. The idea was never for this to be a definitive move - it was always about having this experience. And if we like it better here, then yeah, it might be definitive. But it’s too early to tell.</p>
<h3 id="travel">Travel</h3>
<p>Besides the move, we did some traveling this year as well.</p>
<h4 id="rio-de-janeiro">Rio de Janeiro</h4>
<p>We lived in Brazil all our lives, but we had never been to Rio! It was fantastic to live the “genuine” Brazilian experience. The incredible natural beauty, friendly people, great food, sunny weather and good music - it was all there!</p>
<p>We went with two friends for the legendary free Lady Gaga concert in Copacabana. We were somewhere amidst those 2 million+ people!</p>
<p><img src="https://fantinel.dev/cms/media/articles/2025-year-in-review/rio.jpg" alt="Picture of Copacabana beach seen from above. Weather is sunny, sky and see are blue. The beach is curved and surrounded by tall buildings, which are themselves surrounded by native atlantic forests." title="Copacabana beach as seen from the Pão de Açúcar. You can actually see some of the structure from the previous day&#x27;s Lady Gaga concert. There were over 2 million people on that beach the night before this pic was taken!"></p>
<p>Our trip was unfortunately too short to see everything we wanted to. Pão de Açúcar (Sugarloaf) and Cristo Redentor (Christ the Redeemer) were definitely highlights though.</p>
<h4 id="northern-italy">Northern Italy</h4>
<p>Kind of cheating, I guess, but now that we live in northern Italy, we have a lot of amazing places nearby to sightsee in.</p>
<p>The cover image of this post was taken in Varenna, just 30 minutes away from here by train. A beautiful medieval-like Italian village right by the lake.</p>
<p>Another highlight is the city of Breuil-Cervinia, which is a mountain town right next to the Matterhorn mountain. The famous pictures of the Matterhorn are taken from the Swiss side of it, but the whole area on the Italian side is also breathtaking!</p>
<p><img src="https://fantinel.dev/cms/media/articles/2025-year-in-review/laco-blu.jpg" alt="Picture of a green shallow lake amidst the woods in the Alpen mountains." title="Laco Blu, in Italy. Misleading name, I know."></p>
<h2 id="blog">Blog</h2>
<p>This was another good year for the blog! In total I only had 9 non-cool-link posts but added a bunch of new stuff to it.</p>
<p>First of all, it finally got a new coat of paint in June, with <a href="https://fantinel.dev/blog/website-v5">v5 being launched</a>! I’m really proud of the result and I’ve been tweaking it ever since, adding new sections and moving the menu to the left (on desktop) to accommodate those.</p>
<ul>
<li><a href="https://fantinel.dev/quick-reviews"><strong>Quick Reviews</strong></a> are something I had been doing since last year, using <a href="https://quickreviews.app/">this web app</a> by Matt Birchler. I decided I wanted to make them my own and add them so the site, so I recreated the design here and think it turned out pretty great;</li>
<li><a href="https://fantinel.dev/cool-links"><strong>Cool Links</strong></a> is a central for all the stuff I’ve shared on the monthly Cool Link posts here. They show up in there in real time as I save them, so chances are you can see something in that page before it pops up on the blog;</li>
<li>And finally, the <a href="https://fantinel.dev/photography"><strong>Photography</strong></a> page was added last month, just for me to share the photos I’m most proud of. Lots of beautiful landscapes where I am now, so why not have a place to share it? It’s better than social media!</li>
</ul>
<p>The fact that there’s posts in 4 different sections also means you gotta open 4 different pages to see if there’s anything new (unless you use a RSS reader). Which is why my plan next year is to add a unified timeline feed around here.</p>
<h2 id="work">Work</h2>
<p>I still happily work at <a href="https://usefulgroup.com">Useful Group</a>, where I launched a couple of big projects this year and worked on a bunch more. I’m still incredibly grateful to be able to work with such amazing people, both professionally and individually.</p>
<p>No side projects this year except for one I’m working on right now: an iOS version of <a href="https://phanpy.social">Phanpy</a>, the best Mastodon client, which is originally a web app. I’m not doing any real development here, mainly just wrapping the existing web app into something that works better on iOS, but it’s still taxing to go through the App Store process. The app is currently in review and should be available sometime soon. I’ll definitely write about it here.</p>
<h2 id="fun">Fun</h2>
<blockquote>
<p>[!info]
This year, I’ve started writing <a href="https://fantinel.dev/quick-reviews">Quick Reviews</a> for a lot of the things below, so definitely check those out for more context.</p>
</blockquote>
<p>Lots of things were good for distracting from the overwhelming responsibilities this year. These were the best of them:</p>
<h3 id="tv-and-movies">TV and Movies</h3>
<ul>
<li>Watching <strong>Mother!</strong> feels like being in a nightmare, and it was a haunting experience. I absolute loved it, but never want to watch it again (kinda like the Last of Us Part II game). <a href="https://fantinel.dev/quick-reviews/mother/">Quick Review link</a>;</li>
<li><strong>The Taking of Deborah Logan</strong> is the perfect kind of horror movie: it builds up a good mystery and doesn’t ruin it by giving you all the answers. <a href="https://fantinel.dev/quick-reviews/the-taking-of-deborah-logan/">Quick Review link</a>;</li>
<li><strong>Nosferatu</strong> also deserves a mention. Its horror aspect comes more from tension and not mystery; but it’s at the same time a very beautiful movie. <a href="https://fantinel.dev/quick-reviews/nosferatu">Quick Review link</a>;</li>
<li><strong>Flow</strong> was able to make me connect so much with the characters, without a single line of dialogue. It totally deserved the Oscar for best animation it got! <a href="https://fantinel.dev/quick-reviews/flow/">Quick Review link</a></li>
</ul>
<h3 id="games">Games</h3>
<ul>
<li><strong>Lies of P</strong> is a game I’d been wanting to play for 2 years and I’m really glad I finally did. An amazing game that is <em>very</em> inspired by Bloodborne, my favorite game ever, which of course invites all kinds of comparisons. But it can actually stand on its own; the gameplay is amazing, the atmosphere is very well done, and the setting helps keeping things interesting. <a href="https://fantinel.dev/quick-reviews/lies-of-p/">Quick Review link</a>;</li>
<li><strong>No Man’s Sky</strong> has been one of my favorite ways to unwind this year. It’s appeared here before (this is far from my first time playing it), but the more I play it, the more I like it. It’s a dream come true of a game, honestly. <a href="https://fantinel.dev/quick-reviews/no-mans-sky/">Quick Review link</a>;</li>
</ul>
<h3 id="music">Music</h3>
<p>This year, we went to <em>two</em> Lady Gaga concerts! The first was in May, at her legendary concert in Copacabana. It was right on the beach and free for everyone. There were over 2 million people there! The concert was amazing and the energy of the audience was incredible, but we mostly watched it through the big screens since we were far from the stage.</p>
<p>So, back in October, we went to another concert, in Milan! It was largely the same concert but with a larger setlist, but we were able to stay right in front of the stage and saw everything up close! Both experiences were amazing.</p>
<p><img src="https://fantinel.dev/cms/media/articles/2025-year-in-review/gaga.png" alt="Lady Gaga on stage. She stands on top of a metal cage covered in a giant red dress, and points at the camera. There&#x27;s a lot of people inside that cage" title="No picture or video do justice to watching this live."></p>
<ul>
<li><strong>Mayhem</strong> by Lady Gaga was obviously a very important album this year, then. My wife has been a Little Monster since she was a kid, so I became a fan by indirectly listening to it many times 😅 And after also listening to the album live two times, I can say it’s pretty great - with the first 3 songs being the highlights for me;</li>
<li><strong>Skeletá</strong> by Ghost was the one I listened the most to - an amazing album from start to finish, with some of my favorite songs ever in it. Ghost gets better at every release and this is no exception. <a href="https://fantinel.dev/quick-reviews/skelet%C3%A1/">Quick Review link</a>;</li>
<li><strong>Sylvaine</strong> is an artist I discovered by accident but loved everything I heard so far. Her sound is heavy, but also very dreamy and ethereal. I love how she can swap between gutural vocals and the soft voice multiple times during the same song. “A Ghost Trapped in Limbo” and “Mørklagt” are my favorites;</li>
</ul>
<h2 id="what-to-expect-from-2026">What to expect from 2026</h2>
<p>I <em>want</em> 2026 to be a very chill year; I definitely need a break after so much happening in 2025. But I don’t think that will happen. Firstly because we’re still getting settled here and there’s still so much stuff to get sorted. Bureaucracy is never fun or relaxing. Secondly, because we are right in the middle of <em>a lot</em> of cool places. We definitely want to take advantage of that and sightsee a lot. And we most definitely will.</p>
<p>For this blog, I envision 2026 being a year I write a lot more, probably more personal stuff than usual. Which is something I’ve been wanting to do for years and only partially succeeded.</p>
<p>I’ll definitely launch the iOS version of Phanpy too. Though that’s probably happening in January already, which means it’s an easy guess to make!</p>
<p>And since I already have the Developer License for the year… maybe I could come up with some other interesting app idea to launch? We’ll see.</p>
<h2 id="wrapping-up">Wrapping up</h2>
<p>One thing I’m grateful for every year are the people out there who write about cool stuff and share their knowledge for free, for everyone, here on the internet. I learned so much on both professional and individual levels from them, and it’s always been a goal of mine to do the same for others with this blog.</p>
<p>So, thank <em>you</em> for reading this and giving me this opportunity. This kind of human connection is what made the web great years ago and it’s what still makes it great now.</p>
<p>See you next year!</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/cms/media/articles/2025-year-in-review/cover.png"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/cms/media/articles/2025-year-in-review/cover.png"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/photography/sunset-in-varenna</guid>
      <title>Photography: Sunset in Varenna</title>
      <link>https://fantinel.dev/photography/sunset-in-varenna</link>
      <pubDate>Sat, 13 Dec 2025 15:42:56 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>Really proud of the lighting in this one.</p>

        <p>Photo taken on Saturday, 06 Dec 2025</p>

        <p><img src="https://fantinel.dev/cms/media/photographies/varenna-sunset-lake.png" alt="Shot of the blue lake waters slightly lit up by the last rays of sunlight of the day." /></p>
                
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="/cms/media/photographies/varenna-sunset-lake.png"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="/cms/media/photographies/varenna-sunset-lake.png"/>
      
    </item>
  
    <item>
      <guid>https://neal.fun/size-of-life/</guid>
      <title>Cool Link: Size of Life</title>
      <link>https://neal.fun/size-of-life/</link>
      <pubDate>Thu, 11 Dec 2025 11:31:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>by Neal Agarwal</p>
        
        <p>Another great page by Neal Agarwal; this one lets you see life in all its different sizes.</p>
      ]]></content:encoded>
    </item>
  
    <item>
      <guid>https://posthog.com</guid>
      <title>Cool Link: PostHog Website</title>
      <link>https://posthog.com</link>
      <pubDate>Tue, 09 Dec 2025 15:12:09 +0000</pubDate>
      <content:encoded><![CDATA[
        
        
        <p>This website is <em>so cool!</em> It’s the kind of thing that you’d imagine was a personal website, but it’s actually a marketing page! A marketing team actually sat down and planned this out! I thought no such thing as fun marketing existed. Glad to be proven wrong.</p>
      ]]></content:encoded>
    </item>
  
    <item>
      <guid>https://chrome.dev/css-wrapped-2025/</guid>
      <title>Cool Link: CSS Wrapped 2025</title>
      <link>https://chrome.dev/css-wrapped-2025/</link>
      <pubDate>Tue, 09 Dec 2025 12:48:01 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>by The Chrome DevRel Team</p>
        
        <p>CSS is my favorite language and 2025 was <em>amazing</em> for it! The Chrome team built this page highlighting all the new exciting stuff that happened to CSS this year. I’ve used some of it but sadly still have to wait for other browsers to catch up before doing it on any serious work 😭</p>
<p>I recommend opening this in a Chromium-based browser so you can try it out firsthand, but there are video recordings of the features in case you’re unable to.</p>
      ]]></content:encoded>
    </item>
  
    <item>
      <guid>https://cluesbysam.com/</guid>
      <title>Cool Link: Clues by Sam</title>
      <link>https://cluesbysam.com/</link>
      <pubDate>Tue, 09 Dec 2025 00:01:15 +0000</pubDate>
      <content:encoded><![CDATA[
        
        
        <p>Neat little daily browser puzzle game where you use clues to find out who’s a criminal and who’s innocent.</p>
      ]]></content:encoded>
    </item>
  
    <item>
      <guid>https://www.youtube.com/watch?v=MVUuoXAkuUg</guid>
      <title>Cool Link: The greatest in-camera effect of all time (video)</title>
      <link>https://www.youtube.com/watch?v=MVUuoXAkuUg</link>
      <pubDate>Mon, 08 Dec 2025 15:12:58 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>by Corridor Crew</p>
        
        <p>Ok, this video is <strong>amazing.</strong> Corridor Crew recreates the amazing practical effects from the first Lord of the Rings movie (the forced perspective ones with Gandalf and the hobbits), but not only that, there’s amazing storytelling on how it was made, all the cinema history before it, and why it has never been done again since then.</p>
      ]]></content:encoded>
    </item>
  
    <item>
      <guid>https://www.nicchan.me/blog/the-f-off-contact-page/</guid>
      <title>Cool Link: The f*** off contact page</title>
      <link>https://www.nicchan.me/blog/the-f-off-contact-page/</link>
      <pubDate>Mon, 08 Dec 2025 10:20:14 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>by Nic Chan</p>
        
        <p>Great post about how clients hire experts to solve a problem, then completely ignore their expertise and try to copy what the big ones do. But they’re not big.</p>
<p>The “f*** off contact page” concept is amazing, too. It’s 100% real and out there, with more and more companies doing it (either intentionally or by just wanting to copy what others do).</p>
<p>Also, Nic Chan’s website is a treasure. It’s already been featured as a cool link here before but I wanted to point it out again. So cool!</p>
      ]]></content:encoded>
    </item>
  
    <item>
      <guid>https://fantinel.dev/quick-reviews/home-alone-2---lost-in-new-york</guid>
      <title>Quick Review: Home Alone 2 - Lost in New York</title>
      <link>https://fantinel.dev/quick-reviews/home-alone-2---lost-in-new-york</link>
      <pubDate>Sun, 07 Dec 2025 12:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>Home Alone 2 - Lost in New York <br> 1992, Chris Columbus</p>
        <p>My rating: Decent</p>
        <p><img src="https://m.media-amazon.com/images/M/MV5BOGEyYzRmNzYtYzJjZi00ZjhlLWJiNDktYzZhNTgxMzc1NThlXkEyXkFqcGc@._V1_SX300.jpg" /></p>
        
        <p>It’s still a fun movie, but it’s basically the same thing as the first one but with worse pacing and felt a bit too forced.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://m.media-amazon.com/images/M/MV5BOGEyYzRmNzYtYzJjZi00ZjhlLWJiNDktYzZhNTgxMzc1NThlXkEyXkFqcGc@._V1_SX300.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://m.media-amazon.com/images/M/MV5BOGEyYzRmNzYtYzJjZi00ZjhlLWJiNDktYzZhNTgxMzc1NThlXkEyXkFqcGc@._V1_SX300.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/quick-reviews/home-alone</guid>
      <title>Quick Review: Home Alone</title>
      <link>https://fantinel.dev/quick-reviews/home-alone</link>
      <pubDate>Sun, 07 Dec 2025 12:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>Home Alone <br> 1990, Chris Columbus</p>
        <p>My rating: I like it</p>
        <p><img src="https://m.media-amazon.com/images/M/MV5BNzNmNmQ2ZDEtMTc1MS00NjNiLThlMGUtZmQxNTg1Nzg5NWMzXkEyXkFqcGc@._V1_SX300.jpg" /></p>
        
        <p>Pretty fun watch all around. I would have loved this movie as a child, but somehow never got to watch it.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://m.media-amazon.com/images/M/MV5BNzNmNmQ2ZDEtMTc1MS00NjNiLThlMGUtZmQxNTg1Nzg5NWMzXkEyXkFqcGc@._V1_SX300.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://m.media-amazon.com/images/M/MV5BNzNmNmQ2ZDEtMTc1MS00NjNiLThlMGUtZmQxNTg1Nzg5NWMzXkEyXkFqcGc@._V1_SX300.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/photography/varenna</guid>
      <title>Photography: Varenna</title>
      <link>https://fantinel.dev/photography/varenna</link>
      <pubDate>Sat, 06 Dec 2025 22:42:56 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>One of the many incredible places surrounding Lake Como, in northern Italy.</p>

        <p>Photo taken on Saturday, 06 Dec 2025</p>

        <p><img src="https://fantinel.dev/cms/media/photographies/varenna.png" alt="Picturesque medieval-like italian village on the shore of a huge blue lake. The houses are very colorful and there's a lot of pine trees on the slope. A small boat floats on the lake, and in the background sit the snow-capped Alps." /></p>
                
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="/cms/media/photographies/varenna.png"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="/cms/media/photographies/varenna.png"/>
      
    </item>
  
    <item>
      <guid>https://fantinel.dev/photography/flow-from-the-grotte</guid>
      <title>Photography: Flow from the Grotte</title>
      <link>https://fantinel.dev/photography/flow-from-the-grotte</link>
      <pubDate>Sat, 06 Dec 2025 22:33:18 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>Always wanted to take a long exposure shot like this, with the water all smooth and fluffy. Taken just downstream of the Grotte delle Busserailles near Cervinia, in Italy.</p>

        <p>Photo taken on Saturday, 20 Sep 2025</p>

        <p><img src="https://fantinel.dev/cms/media/photographies/smooth-stream.png" alt="Daytime picture of a river running in the middle of the woods. The river is full of rocks and the water has a smooth appearance caused by the long exposure shot" /></p>
                
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="/cms/media/photographies/smooth-stream.png"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="/cms/media/photographies/smooth-stream.png"/>
      
    </item>
  
    <item>
      <guid>https://fantinel.dev/blog/cool-links-november-2025</guid>
      <title>Cool Links Vol. 17: November, 2025</title>
      <description>Links to the best stuff I&apos;ve read or watched during the month of November, 2025</description>
      <link>https://fantinel.dev/blog/cool-links-november-2025</link>
      <pubDate>Sun, 30 Nov 2025 00:00:00 +0000</pubDate>
      <category>Cool Links</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/cool-links-november-2025">
              read on the site!
            </a>
          </strong>
        </div>

        <p>Hey there! Winter has arrived here in Italy. We’ve had our first snow, not enough to paint the city white yet but the mountains are already <em>very picturesque</em>.</p>
<p>As I try to get used to this kind of temperature at this part of the year (I was in Brazil until September, where it’s summer right now), I took some time to do some website expansions!</p>
<p>First, I gave Cool Links a new home and <a href="https://fantinel.dev/blog/a-new-home-for-cool-links">wrote about it</a> here. Basically there’s now a feed of links that show up as I save them during the month. And you can filter them too!</p>
<p>Second, I launched <a href="https://fantinel.dev/photography">a new page</a> for photos I take. I’ve been posting a few of the ones I’m the most proud of. No professional work here - just me with a smartphone and landscapes that do all the work.</p>
<p>Third, I redesigned the navigation on the site itself (at least on desktop). I moved the header navbar to the left, since the top bar was getting too crowded. I like side navs and am pretty happy with the result.</p>
<p>Fourth, I posted my <a href="https://fantinel.dev/blog/default-apps-2025">Default Apps update</a> for 2025! Had way more changes this year compared to the last.</p>
<p>Oh, and for the cool links of the month… a lot of CSS-related ones, but not only that. Enjoy!</p>
<h2 id="fun">Fun</h2>
<p><strong><a href="https://mastodon.gamedev.place/@TomF/115589875974658415">The perils of doors in gamedev</a>, by Tom Forsyth</strong></p>
<p>This Mastodon thread is an amazing tale about game development, physics and time-traveling bugs.</p>
<h2 id="tech">Tech</h2>
<p><strong><a href="https://blog.kagi.com/slopstop">Introducing SlopStop: Community-driven AI slop detection</a>, by Kagi Search</strong></p>
<p>This is a really cool initiative! Kagi has been my search engine of choice for over a year and I’m really happy with how they’re aiming to stop AI slop from taking over their (still great) search results.</p>
<p>In my experience, their results are miles ahead of Google’s, Bing’s or whatever other search engine out there, partly because of their algorithm prioritizes good sites, partly because they allow you to prioritize/deprioritize/block the sites you want.</p>
<p>But a good algorithm only goes so far and with the amount of AI slop hitting the web every day, it’s gonna be harder and harder to avoid them. Now Kagi users can report certain articles as AI-generated so other users can know that beforehand and <em>not</em> click on them, or even block their domains.</p>
<p><strong><a href="https://blog.kagi.com/llms">LLMs are bullshitters. But that doesn’t mean they’re not useful </a>, by Vladimir Prelovac</strong></p>
<p>… wow. This is an amazing article that goes a bit into how LLMs work (is an easy-to-understand way), how flawed they are, and how useful they can be. Or dangerous.</p>
<p>Plus, the nurse and surgeon examples are hilarious.</p>
<p><strong><a href="https://xeiaso.net/blog/birth-death-seo/">The birth &#x26; death of search engine optimization</a>, by Xe Iaso</strong></p>
<p>This article walks through how the concept of SEO (Search Engine Optimization) was born, how it inevitably became broken and how easy it is to “win” it, as long as your content is made up and not actual real information.</p>
<h2 id="dev">Dev</h2>
<p><strong><a href="https://ishadeed.com/article/conditional-border-radius/">Conditional Border Radius In CSS</a>, by Ahmad Shadeed</strong></p>
<p>This is a <em>really</em> cool trick. Turns out that it’s possible, with pure CSS, to have border-radius be applied conditionally.</p>
<p>The given example is a perfect one: sometimes we have cards with rounded corners that look good on their own, but if you’re on mobile and have less space and want the cards to take up the full page width, the rounded corners look awful. You can technically write breakpoints for that, but with <code>clamp</code> you can make the border-radius disappear if the card is too close to the viewport edges!</p>
<p><strong><a href="https://www.joshwcomeau.com/css/subgrid/">Brand New Layouts with CSS Subgrid</a>, by Josh Comeau</strong></p>
<p>This is the first article that made me actually understand the use cases for CSS subgrid. I’m still not fully convinced I’m gonna use them often, but it’s nice to understand what problems they solve.</p>
<p><strong><a href="https://ishadeed.com/article/modern-css-section-layout/">Solved By Modern CSS: Section Layout</a>, by Ahmad Shadeed</strong></p>
<p>In this awesome post, Ahmad walks through all the possibilities modern CSS offers when building a section layout.</p>
<p>I knew about and have used some of those in the past, but that tip about <code>display: contents</code> was amazing! Never thought of using it like that.</p>
<p><strong><a href="https://emilkowal.ski/ui/you-dont-need-animations#purposeful-animations">You Don’t Need Animations</a>, by Emil Kowalski</strong></p>
<p>Great and to-the-point article with practical examples of when to use (or not use) animations properly in UIs.</p>
<p>I love me some <em>whooshy</em> animations, but they can be a pain in the ass when overused or when used in the wrong moment.</p>
<p><strong><a href="https://gomakethings.com/just-use-a-button/">Just use a button</a>, by Chris Ferdinandi</strong></p>
<p>The “div vs button” debate was never really a debate because one of the sides is objectively wrong, but this is still a good post to remind you of <em>why</em> it was never a debate in the first place.</p>
<p><strong><a href="https://stackoverflow.blog/2023/12/25/is-software-getting-worse">Is software getting worse? - Stack Overflow</a>, by Isaac Lyman</strong></p>
<p>This article has been sitting in my “Read Later” queue for almost 2 years 😳</p>
<p>It is an interesting article for sure, speaking about why speed and optimization has become such a rare thing in software development.</p>
<p>The second part of it, though, has kinda aged like milk, sadly. Developers no longer have a lot of leverage on their jobs, and we now live in a world where the thought of having no human developers involved at all in the code I’m running is real and frankly terrifying.</p>
<p>I’m hopeful companies will eventually figure out that AI-generated crap is still crap when the bubble bursts, but until then, there’s a lot of damage to be done.</p>
<h2 id="wrapping-up">Wrapping up</h2>
<p>Thanks for reading once again, and see you next month!</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/opengraph/cool-links/Q29vbCUyMExpbmtzJTIwVm9sLiUyMDE3/MjAyNS0xMS0zMFQwMCUzQTAwJTNBMDAuMDAwWg==/image.png"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/opengraph/cool-links/Q29vbCUyMExpbmtzJTIwVm9sLiUyMDE3/MjAyNS0xMS0zMFQwMCUzQTAwJTNBMDAuMDAwWg==/image.png"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/quick-reviews/the-lord-of-the-rings---the-fellowship-of-the-ring-extended-edition</guid>
      <title>Quick Review: The Lord of the Rings - The Fellowship of the Ring (Extended Edition)</title>
      <link>https://fantinel.dev/quick-reviews/the-lord-of-the-rings---the-fellowship-of-the-ring-extended-edition</link>
      <pubDate>Sat, 29 Nov 2025 12:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>The Lord of the Rings - The Fellowship of the Ring (Extended Edition) <br> 2001, Peter Jackson</p>
        <p>My rating: Loved it!</p>
        <p><img src="https://m.media-amazon.com/images/M/MV5BNzIxMDQ2YTctNDY4MC00ZTRhLTk4ODQtMTVlOWY4NTdiYmMwXkEyXkFqcGc@._V1_SX300.jpg" /></p>
        
        <p>Can’t believe I’d never watched the extended editions before. This movie is just timeless, a perfect introduction to a wonderful world, amazing pacing, acting, environments and practical effects.</p>
<p>It’s almost 4 hours long but I wanted to start the next one immediately after it finished.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://m.media-amazon.com/images/M/MV5BNzIxMDQ2YTctNDY4MC00ZTRhLTk4ODQtMTVlOWY4NTdiYmMwXkEyXkFqcGc@._V1_SX300.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://m.media-amazon.com/images/M/MV5BNzIxMDQ2YTctNDY4MC00ZTRhLTk4ODQtMTVlOWY4NTdiYmMwXkEyXkFqcGc@._V1_SX300.jpg"/>          
    </item>
  
    <item>
      <guid>https://www.joshwcomeau.com/css/subgrid/</guid>
      <title>Cool Link: Brand New Layouts with CSS Subgrid</title>
      <link>https://www.joshwcomeau.com/css/subgrid/</link>
      <pubDate>Fri, 28 Nov 2025 00:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>by Josh Comeau</p>
        
        <p>This is the first article that made me actually understand the use cases for CSS subgrid. I’m still not fully convinced I’m gonna use them often, but it’s nice to understand what problems they solve.</p>
      ]]></content:encoded>
    </item>
  
    <item>
      <guid>https://fantinel.dev/quick-reviews/lies-of-p</guid>
      <title>Quick Review: Lies of P</title>
      <link>https://fantinel.dev/quick-reviews/lies-of-p</link>
      <pubDate>Thu, 27 Nov 2025 12:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>Lies of P <br> 2023, Neowiz</p>
        <p>My rating: I like it</p>
        <p><img src="https://m.media-amazon.com/images/M/MV5BYzc3YTI5OTItNmRmMi00YzZkLTg0MjQtYWZiZmY5YjhjNzM1XkEyXkFqcGc@._V1_QL75_UX190_CR0,2,190,281_.jpg" /></p>
        
        <p>Amazing game, probably the best souslike that’s not made by From Software.</p>
<p>Lies of P is a dark retelling of Pinocchio, and the Belle Epoque with Puppets setting really grows on you. At some point it goes bonkers with that and it’s awesome. The Bloodborne inspiration is obvious and very welcome.</p>
<p>The final third of the game suffers from a steep difficulty wall, but doesn’t ruin it. There’s a difficulty setting that can help if a boss gets too difficult.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://m.media-amazon.com/images/M/MV5BYzc3YTI5OTItNmRmMi00YzZkLTg0MjQtYWZiZmY5YjhjNzM1XkEyXkFqcGc@._V1_QL75_UX190_CR0,2,190,281_.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://m.media-amazon.com/images/M/MV5BYzc3YTI5OTItNmRmMi00YzZkLTg0MjQtYWZiZmY5YjhjNzM1XkEyXkFqcGc@._V1_QL75_UX190_CR0,2,190,281_.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/blog/default-apps-2025</guid>
      <title>My Default Apps: 2025</title>
      <description>Updated list of my apps of choice for daily activities!</description>
      <link>https://fantinel.dev/blog/default-apps-2025</link>
      <pubDate>Wed, 26 Nov 2025 16:45:25 +0000</pubDate>
      <category>Apps</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/default-apps-2025">
              read on the site!
            </a>
          </strong>
        </div>

        <p>Following the end-of-year tradition, here is an updated list of the apps I choose to use for the vast majority of my daily activities on my Mac and iPhone.</p>
<p>Other years: <a href="https://fantinel.dev/blog/default-apps-2024">2024</a>, <a href="https://fantinel.dev/blog/default-apps-2023">2023</a>.</p>
<p>Apps that are new compared to last year are marked with the ✨ emoji.</p>
<ul>
<li>📧 Mail service: <a href="https://fastmail.com">Fastmail</a></li>
<li>📬 Mail client: <a href="https://fastmail.com">Fastmail</a>✨
<ul>
<li>I used to use Apple Mail but got tired of its unreliability. Fastmail’s iOS app and web client on desktop are close to perfect, really.</li>
</ul>
</li>
<li>📆 Calendar: <a href="https://www.busymac.com">BusyCal</a>✨
<ul>
<li>More reliable than Apple Calendar and I love the “Current &#x26; Upcoming” widget on iOS. I don’t use a lot of the features, but since it’s included in <a href="https://setapp.com">Setapp</a>, it’s a no-brainer.</li>
</ul>
</li>
<li>✅ Tasks: <a href="https://ticktick.com/">TickTick</a></li>
<li>📝 Notes: <a href="https://obsidian.md">Obsidian</a></li>
<li>🌐 Web browser: <a href="https://vivaldi.com/">Vivaldi</a>✨ on Mac, a mix of <a href="https://vivaldi.com/">Vivaldi</a>✨ and <a href="https://orionbrowser.com">Orion</a>✨ on iPhone
<ul>
<li>I <a href="https://fantinel.dev/blog/arc-browser-replacement">wrote extensively</a> about my migration from Arc to Vivaldi on the desktop. On mobile, Vivaldi is not as appealing. Orion scratches my need for simpler navigation when I just need to do a quick search.</li>
</ul>
</li>
<li>📰 RSS Reader: <a href="https://reederapp.com">Reeder</a>✨
<ul>
<li>Readwise Reader was great but too expensive. Reeder works pretty well and it’s a one-time purchase that I’m pretty happy with.</li>
</ul>
</li>
<li>🧮 Code Editor: <a href="https://windsurf.com">Windsurf</a>✨
<ul>
<li>It’s VS Code, but with better integrated AI features. It’s very powerful if used correctly, which I like to believe I’m doing.</li>
</ul>
</li>
<li>👨🏻‍💻 Terminal: <a href="https://iterm2.com/">iTerm 2</a></li>
<li>🔎 Search: <a href="https://kagi.com">Kagi</a></li>
<li>⌨️ Launcher: <a href="https://www.raycast.com/">Raycast</a></li>
<li>☁️ Cloud storage: iCloud</li>
<li>🌅 Photo library: iCloud</li>
<li>🌤️ Weather: Apple Weather</li>
<li>🎙️ Podcasts: Apple Podcasts / YouTube</li>
<li>🎶 Music: Apple Music</li>
<li>🛹 Clipboard manager: <a href="https://maccy.app/">Maccy</a></li>
<li>🔐 Passwords: <a href="https://1password.com/">1Password</a></li>
<li>💸 Budgeting: <a href="https://www.ynab.com/">YNAB</a></li>
<li>💁🏻‍♂️ Social: <a href="https://joinmastodon.org/">Mastodon</a></li>
<li>🐘 Mastodon: <a href="https://phanpy.social">Phanpy</a>✨
<ul>
<li>Simply the best social media client ever. Everything is so thoughtfully put together. It’s a web app which is great on desktop, slightly less so on mobile (mainly Apple’s fault). I made an iOS version of it, which should be launched somewhere <em>soon</em> (just maybe not the App Store).</li>
</ul>
</li>
<li>🎮 Gaming: PS5 and Switch</li>
<li>🖼️ Screenshots: <a href="https://cleanshot.com/">CleanShot X</a></li>
</ul>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/cms/media/articles/default-apps-2024.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/cms/media/articles/default-apps-2024.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/photography/first-snow-of-the-season</guid>
      <title>Photography: First snow of the season</title>
      <link>https://fantinel.dev/photography/first-snow-of-the-season</link>
      <pubDate>Mon, 24 Nov 2025 14:50:57 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>The snow made the Resegone Mountain even more picturesque than usual.</p>

        <p>Photo taken on Friday, 21 Nov 2025</p>

        <p><img src="https://fantinel.dev/cms/media/photographies/resegone-snowcapped.png" alt="Snowcapped mountains slightly obscured by clouds, with a gray sky. Some houses appear on the foot of the mountains." /></p>
                
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="/cms/media/photographies/resegone-snowcapped.png"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="/cms/media/photographies/resegone-snowcapped.png"/>
      
    </item>
  
    <item>
      <guid>https://blog.kagi.com/llms</guid>
      <title>Cool Link: LLMs are bullshitters. But that doesn&apos;t mean they&apos;re not useful </title>
      <link>https://blog.kagi.com/llms</link>
      <pubDate>Mon, 24 Nov 2025 12:57:48 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>by Vladimir Prelovac</p>
        
        <p>… wow. This is an amazing article that goes a bit into how LLMs work (is an easy-to-understand way), how flawed they are, and how useful they can be. Or dangerous.</p>
<p>Plus, the nurse and surgeon examples are hilarious.</p>
      ]]></content:encoded>
    </item>
  
    <item>
      <guid>https://xeiaso.net/blog/birth-death-seo/</guid>
      <title>Cool Link: The birth &amp; death of search engine optimization</title>
      <link>https://xeiaso.net/blog/birth-death-seo/</link>
      <pubDate>Mon, 24 Nov 2025 00:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>by Xe Iaso</p>
        
        <p>This article walks through how the concept of SEO (Search Engine Optimization) was born, how it inevitably became broken and how easy it is to “win” it, as long as your content is made up and not actual real information.</p>
      ]]></content:encoded>
    </item>
  
    <item>
      <guid>https://mastodon.gamedev.place/@TomF/115589875974658415</guid>
      <title>Cool Link: The perils of doors in gamedev</title>
      <link>https://mastodon.gamedev.place/@TomF/115589875974658415</link>
      <pubDate>Sun, 23 Nov 2025 16:22:03 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>by Tom Forsyth</p>
        
        <p>This Mastodon thread is an amazing tale about game development, physics and time-traveling bugs.</p>
      ]]></content:encoded>
    </item>
  
    <item>
      <guid>https://fantinel.dev/photography/swan-in-lake-como</guid>
      <title>Photography: Swan in Lake Como</title>
      <link>https://fantinel.dev/photography/swan-in-lake-como</link>
      <pubDate>Sat, 22 Nov 2025 19:33:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>This swan was kind enough to pose around for a bit, even with the (very) cold wind.</p>

        <p>Photo taken on Saturday, 22 Nov 2025</p>

        <p><img src="https://fantinel.dev/cms/media/photographies/lecco-lake-swan.png" alt="A swan swims around in the blue waters of a lake. In the background, snow-capped mountains and a blue sky." /></p>
                
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="/cms/media/photographies/lecco-lake-swan.png"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="/cms/media/photographies/lecco-lake-swan.png"/>
      
    </item>
  
    <item>
      <guid>https://ishadeed.com/article/conditional-border-radius/</guid>
      <title>Cool Link: Conditional Border Radius In CSS</title>
      <link>https://ishadeed.com/article/conditional-border-radius/</link>
      <pubDate>Tue, 18 Nov 2025 10:13:57 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>by Ahmad Shadeed</p>
        
        <p>This is a <em>really</em> cool trick. Turns out that it’s possible, with pure CSS, to have border-radius be applied conditionally.</p>
<p>The given example is a perfect one: sometimes we have cards with rounded corners that look good on their own, but if you’re on mobile and have less space and want the cards to take up the full page width, the rounded corners look awful. You can technically write breakpoints for that, but with <code>clamp</code> you can make the border-radius disappear if the card is too close to the viewport edges!</p>
      ]]></content:encoded>
    </item>
  
    <item>
      <guid>https://ishadeed.com/article/modern-css-section-layout/</guid>
      <title>Cool Link: Solved By Modern CSS: Section Layout</title>
      <link>https://ishadeed.com/article/modern-css-section-layout/</link>
      <pubDate>Sun, 16 Nov 2025 17:31:10 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>by Ahmad Shadeed</p>
        
        <p>In this awesome post, Ahmad walks through all the possibilities modern CSS offers when building a section layout.</p>
<p>I knew about and have used some of those in the past, but that tip about <code>display: contents</code> was amazing! Never thought of using it like that.</p>
      ]]></content:encoded>
    </item>
  
    <item>
      <guid>https://stackoverflow.blog/2023/12/25/is-software-getting-worse</guid>
      <title>Cool Link: Is software getting worse? - Stack Overflow</title>
      <link>https://stackoverflow.blog/2023/12/25/is-software-getting-worse</link>
      <pubDate>Sun, 16 Nov 2025 16:48:18 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>by Isaac Lyman</p>
        
        <p>This article has been sitting in my “Read Later” queue for almost 2 years 😳</p>
<p>It is an interesting article for sure, speaking about why speed and optimization has become such a rare thing in software development.</p>
<p>The second part of it, though, has kinda aged like milk, sadly. Developers no longer have a lot of leverage on their jobs, and we now live in a world where the thought of having no human developers involved at all in the code I’m running is real and frankly terrifying.</p>
<p>I’m hopeful companies will eventually figure out that AI-generated crap is still crap when the bubble bursts, but until then, there’s a lot of damage to be done.</p>
      ]]></content:encoded>
    </item>
  
    <item>
      <guid>https://emilkowal.ski/ui/you-dont-need-animations#purposeful-animations</guid>
      <title>Cool Link: You Don&apos;t Need Animations</title>
      <link>https://emilkowal.ski/ui/you-dont-need-animations#purposeful-animations</link>
      <pubDate>Sun, 16 Nov 2025 16:29:03 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>by Emil Kowalski</p>
        
        <p>Great and to-the-point article with practical examples of when to use (or not use) animations properly in UIs.</p>
<p>I love me some <em>whooshy</em> animations, but they can be a pain in the ass when overused or when used in the wrong moment.</p>
      ]]></content:encoded>
    </item>
  
    <item>
      <guid>https://blog.kagi.com/slopstop</guid>
      <title>Cool Link: Introducing SlopStop: Community-driven AI slop detection</title>
      <link>https://blog.kagi.com/slopstop</link>
      <pubDate>Sun, 16 Nov 2025 16:19:57 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>by Kagi Search</p>
        
        <p>This is a really cool initiative! Kagi has been my search engine of choice for over a year and I’m really happy with how they’re aiming to stop AI slop from taking over their (still great) search results.</p>
<p>In my experience, their results are miles ahead of Google’s, Bing’s or whatever other search engine out there, partly because of their algorithm prioritizes good sites, partly because they allow you to prioritize/deprioritize/block the sites you want.</p>
<p>But a good algorithm only goes so far and with the amount of AI slop hitting the web every day, it’s gonna be harder and harder to avoid them. Now Kagi users can report certain articles as AI-generated so other users can know that beforehand and <em>not</em> click on them, or even block their domains.</p>
      ]]></content:encoded>
    </item>
  
    <item>
      <guid>https://fantinel.dev/quick-reviews/pinocchio</guid>
      <title>Quick Review: Pinocchio</title>
      <link>https://fantinel.dev/quick-reviews/pinocchio</link>
      <pubDate>Sun, 16 Nov 2025 12:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>Pinocchio <br> 2022, Guillermo del Toro, Mark Gustafson</p>
        <p>My rating: I like it</p>
        <p><img src="https://m.media-amazon.com/images/M/MV5BN2I3NDQzZmQtODJmOS00M2QyLWIxZTItYTA1ZTYzOWJlM2ZmXkEyXkFqcGc@._V1_SX300.jpg" /></p>
        
        <p>Cute with a hint of sadness, I love the combination. The art style and character designs are amazing!</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://m.media-amazon.com/images/M/MV5BN2I3NDQzZmQtODJmOS00M2QyLWIxZTItYTA1ZTYzOWJlM2ZmXkEyXkFqcGc@._V1_SX300.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://m.media-amazon.com/images/M/MV5BN2I3NDQzZmQtODJmOS00M2QyLWIxZTItYTA1ZTYzOWJlM2ZmXkEyXkFqcGc@._V1_SX300.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/photography/sunset-in-bellagio</guid>
      <title>Photography: Sunset in Bellagio</title>
      <link>https://fantinel.dev/photography/sunset-in-bellagio</link>
      <pubDate>Sun, 09 Nov 2025 17:28:21 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>Sunset in Bellagio, Italy.</p>

        <p>Photo taken on Saturday, 01 Nov 2025</p>

        <p><img src="https://fantinel.dev/cms/media/photographies/bellagio-sunset.png" alt="A large ferry crosses a calm lake at sunset, with soft golden light reflecting on the water. In the background, mist and clouds drift around dark mountain slopes, while autumn leaves frame the top of the image. The atmosphere is peaceful and slightly moody, evoking the quiet transition from day to evening." /></p>
                
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="/cms/media/photographies/bellagio-sunset.png"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="/cms/media/photographies/bellagio-sunset.png"/>
      
    </item>
  
    <item>
      <guid>https://fantinel.dev/photography/leccos-bell-tower</guid>
      <title>Photography: Lecco&apos;s Bell Tower</title>
      <link>https://fantinel.dev/photography/leccos-bell-tower</link>
      <pubDate>Sun, 09 Nov 2025 00:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>Bell tower of St. Nicholas’ Basilica in Lecco, Italy.</p>

        <p>Photo taken on Saturday, 08 Nov 2025</p>

        <p><img src="https://fantinel.dev/cms/media/photographies/lecco-tower.png" alt="" /></p>
                
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="/cms/media/photographies/lecco-tower.png"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="/cms/media/photographies/lecco-tower.png"/>
      
    </item>
  
    <item>
      <guid>https://fantinel.dev/blog/a-new-home-for-cool-links</guid>
      <title>A new home for Cool Links</title>
      <description>Or more of a new room in the same home, I guess</description>
      <link>https://fantinel.dev/blog/a-new-home-for-cool-links</link>
      <pubDate>Fri, 07 Nov 2025 16:38:56 +0000</pubDate>
      <category>Meta</category><category>Cool Links</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/a-new-home-for-cool-links">
              read on the site!
            </a>
          </strong>
        </div>

        <p>In July 2024, I started gathering cool links from around the web, with the best stuff I’ve read or watched during each month. Since then, I’ve been posting them monthly, always at the last day of the month.</p>
<p>I’ve been really enjoying doing that, it’s the perfect opportunity to write a few words about stuff without having to commit to a full blog post. Plus, it keeps the spirit of the web alive!</p>
<p>The process of saving links and organizing them was always very manual and it still is to some extent, but a couple months ago I did the responsible thing and started organizing my archive of links. Which means I can reference them, sort, filter, and whatever else I want.</p>
<p>The next logical evolution of that was making that archive available on this very website — and it is now, on the <a href="https://fantinel.dev/cool-links">new Cool Links page</a> !</p>
<p>Links are added there automatically as I save them, which means they’ll be added throughout the entire month. I’ve even added them to <a href="https://fantinel.dev/rss.xml">the RSS feed</a>, if you’re into that.</p>
<p>Of course, the monthly posts (and newsletters) with Cool Links will still exist. I feel like a compilation of multiple links is still the best way to read them. But now you can also find them on their own little room, too.</p>
<p>There’s already some there that haven’t been posted on the blog yet. So if you’re too impatient you can go check it out!</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/cms/media/articles/cool-links-screenshot.png"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/cms/media/articles/cool-links-screenshot.png"/>          
    </item>
  
    <item>
      <guid>https://gomakethings.com/just-use-a-button/</guid>
      <title>Cool Link: Just use a button</title>
      <link>https://gomakethings.com/just-use-a-button/</link>
      <pubDate>Wed, 05 Nov 2025 00:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>by Chris Ferdinandi</p>
        
        <p>The “div vs button” debate was never really a debate because one of the sides is objectively wrong, but this is still a good post to remind you of <em>why</em> it was never a debate in the first place.</p>
      ]]></content:encoded>
    </item>
  
    <item>
      <guid>https://fantinel.dev/quick-reviews/gone-girl</guid>
      <title>Quick Review: Gone Girl</title>
      <link>https://fantinel.dev/quick-reviews/gone-girl</link>
      <pubDate>Sun, 02 Nov 2025 12:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>Gone Girl <br> 2014, David Fincher</p>
        <p>My rating: I like it</p>
        <p><img src="https://m.media-amazon.com/images/M/MV5BMTk0MDQ3MzAzOV5BMl5BanBnXkFtZTgwNzU1NzE3MjE@._V1_SX300.jpg" /></p>
        
        <p>I had no idea what to expect from this movie and it kept surprising me over and over. It’s already pretty long but I wish it kept going for a bit longer.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://m.media-amazon.com/images/M/MV5BMTk0MDQ3MzAzOV5BMl5BanBnXkFtZTgwNzU1NzE3MjE@._V1_SX300.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://m.media-amazon.com/images/M/MV5BMTk0MDQ3MzAzOV5BMl5BanBnXkFtZTgwNzU1NzE3MjE@._V1_SX300.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/blog/cool-links-october-2025</guid>
      <title>Cool Links Vol. 16: October, 2025</title>
      <description>Links to the best stuff I&apos;ve read or watched during the month of October, 2025</description>
      <link>https://fantinel.dev/blog/cool-links-october-2025</link>
      <pubDate>Fri, 31 Oct 2025 00:00:00 +0000</pubDate>
      <category>Cool Links</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/cool-links-october-2025">
              read on the site!
            </a>
          </strong>
        </div>

        <p>Hey there, Happy Halloween 🎃 ! As I enjoy my favorite season and prepare for my first winter in the northern hemisphere, I gathered just treats, no tricks, for you this month.</p>
<h2 id="cool-apps">Cool Apps</h2>
<p><strong><a href="https://edit.photo/">Edit Photo</a>, by Pintura Labs</strong></p>
<p>Okay, this is pretty cool. This lil’ website allows you to do quick image edits right on your browser. Nothing new there - except for the fact that it actually works with <strong>no account, no ads, no popups, no upsell</strong>. Truly a marvel!</p>
<p><strong><a href="https://edit.video/">Edit Video</a>, by Pintura Labs</strong></p>
<p>The same as Edit Photo, but for Videos!</p>
<p><strong><a href="https://squoosh.app/">Squoosh</a></strong></p>
<p>Another cool little web utility. This one lets you <em>squoosh</em> your image files to greatly reduce their file size without any significant loss in quality. Especially useful if you have a website of your own and want to <a href="https://fantinel.dev/blog/web-images-modern-formats/">optimize your images</a>.</p>
<p><strong><a href="https://notebooknavigator.com/">Notebook Navigator - Modern File Explorer for Obsidian</a>, by Johan Sandberg</strong></p>
<p>This is <em>beautiful</em>. This Obsidian plugin completely overhauls the file navigation and makes it actually usable. It fixes one of the app’s biggest problems for me: navigation. I’ve been using it pretty much everyday since finding it and it just made Obsidian exponentially better to use.</p>
<p>You can add custom icons to folders as well, which I used to need a separate plugin for.</p>
<h2 id="fun">Fun</h2>
<p><strong><a href="https://theoatmeal.com/comics/ai_art">A cartoonist’s review of AI art</a>, by The Oatmeal</strong></p>
<p>A really fun web comic of an artist explaining his thoughts about AI art. I think I agree with all the points there.</p>
<h2 id="dev">Dev</h2>
<p><strong><a href="https://gradient.style/">CSS HDR Gradients</a>, by Adam Argyle</strong></p>
<p>A really cool CSS gradient generator that supports all the new CSS color stuff that’s been coming out in the past years (and that I honestly don’t know much about).</p>
<p>Aside from the cool UI and easy-to-understand code it generates, it can generate HDR and SDR gradients; which means that on supported browsers and devices, your gradient might pop out with higher dynamic range (and have the SDR as a fallback). Great if you really want the colors to <em>pop</em>.</p>
<p><strong><a href="https://blog.jim-nielsen.com/2025/more-control-equals-less-performance/">Write Code That Runs in the Browser, or Write Code the Browser Runs</a>, by Jim Nielsen</strong></p>
<p>Really cool thoughts on the tradeoffs between control and performance in web development, and how whatever you build will <em>never</em> outperform the browser’s built-in APIs.</p>
<p><strong><a href="https://bytesauna.com/post/coding-vs-software-engineering">AI can code, but it can’t build software</a>, by Matias Heikkilä</strong></p>
<p>Yes! Any good developer will tell you that coding is the easiest part of the job. Making software actually go beyond a feature demo is what’s really hard. It’s something I’ve been taught ever since I began working on the field, actually. Learning to code is essential, but learning where to put the code and how to foresee all the hundreds of complexities is my actual job.</p>
<p>Expectations, feature scalability and security are very much <em>human</em> components of the job and can’t be properly done by something that’s not human.</p>
<h2 id="wrapping-up">Wrapping up</h2>
<p>Thanks for reading once again, and see you next month!</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/opengraph/cool-links/Q29vbCUyMExpbmtzJTIwVm9sLiUyMDE2/MjAyNS0xMC0zMVQwMCUzQTAwJTNBMDAuMDAwWg==/image.png"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/opengraph/cool-links/Q29vbCUyMExpbmtzJTIwVm9sLiUyMDE2/MjAyNS0xMC0zMVQwMCUzQTAwJTNBMDAuMDAwWg==/image.png"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/quick-reviews/monster---the-ed-gein-story</guid>
      <title>Quick Review: Monster - The Ed Gein Story</title>
      <link>https://fantinel.dev/quick-reviews/monster---the-ed-gein-story</link>
      <pubDate>Sun, 26 Oct 2025 12:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>Monster - The Ed Gein Story <br> 2025, Ian Brennan</p>
        <p>My rating: Didn't like it</p>
        <p><img src="https://m.media-amazon.com/images/M/MV5BZGY2OTcxYWItZDgxNi00Y2E1LTk0YTgtZDcwZjZkNzU4OTJjXkEyXkFqcGc@._V1_SX300.jpg" /></p>
        
        <p>I can totally see this being decent if they stripped away at least half of it and made 3 or 4 decent episodes, but in true Netflix fashion they opted for 8 bad ones instead.</p>
<p>A show that doesn’t really know what it wants to say, with a pacing so awful that often killed any sort of interest in what was going on.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://m.media-amazon.com/images/M/MV5BZGY2OTcxYWItZDgxNi00Y2E1LTk0YTgtZDcwZjZkNzU4OTJjXkEyXkFqcGc@._V1_SX300.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://m.media-amazon.com/images/M/MV5BZGY2OTcxYWItZDgxNi00Y2E1LTk0YTgtZDcwZjZkNzU4OTJjXkEyXkFqcGc@._V1_SX300.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/blog/a-walk-in-the-woods</guid>
      <title>A Walk in the Woods</title>
      <description>Just a quick blog about a cool day in Civiasco, Italy. With pictures!</description>
      <link>https://fantinel.dev/blog/a-walk-in-the-woods</link>
      <pubDate>Tue, 14 Oct 2025 16:17:00 +0000</pubDate>
      <category>Photography</category><category>Trip</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/a-walk-in-the-woods">
              read on the site!
            </a>
          </strong>
        </div>

        <p>Last Sunday I went to a small town in the foot of the italian Alps called Civiasco, with my wife and my brother. Last week we saw a poster for an event that was going to happen there while going out for a walk, and it looked interesting. It was called “Colma D’Autunno”, which in my understanding means “Peak of Autumn”.</p>
<p><img src="https://fantinel.dev/cms/media/articles/Poster%20Colma%20d&#x27;Autunno.jpeg" alt="Event poster for &#x22;Colma d&#x27;Autunno&#x22; dated October 12, 2025, featuring circular image of pastoral scene with sheep, mounted on outdoor bulletin board."></p>
<p>My wife and I always loved autumn, but the yellow and red leaves were not that common back in Brazil, so we never experienced the autumn aesthetic in real life. It was only an hour away from where we are so we figured it’d be cool.</p>
<p>As we arrived there, they were serving polenta with cheese and a sauce with donkey meat (!) called <em>Tapelucco</em>, which I’d never had before. The polenta and cheese were good as always, but I didn’t enjoy the meat that much. Sadly, I forgot to take a pic of the plate, but the portion was <em>very</em> generous.</p>
<p>After lunch, we went for a guided tour around the woods, going up the mountain a bit. Even though it took a while (the “guided” part was mostly a biology class on plants), it was fun to walk around in that environment. It’s the kind of woods we’ve always seen in movies but had never been to before.</p>
<p><img src="https://fantinel.dev/cms/media/articles/Shoes%20in%20the%20woods.jpeg" alt="Ground view of autumn leaves covering forest floor with five feet in sneakers visible from above." title="We did not have proper gear for hiking (or even for the cold), but we survived."></p>
<p><img src="https://fantinel.dev/cms/media/articles/Peek%20at%20Civiasco.jpeg" alt="Crowd of people gathered outdoors facing mountain village nestled in valley, with layered mountain ranges and overcast sky in background." title="That little town in the background is the scenic Civiasco."></p>
<p><img src="https://fantinel.dev/cms/media/articles/A%20really%20cool%20tree.jpeg" alt="Large tree with golden autumn foliage standing on grassy hillside, with forested mountains and overcast sky in background." title="This tree is a stunner."></p>
<p>One of the most common trees in these woods is the Castagno, or Chestnut tree. Which means the ground was full of chestnuts! We had a couple of them raw (which I only later learned you shouldn’t eat raw…), and after the walk they started serving <em>Castagnata</em> (roasted chestnuts) for free for everyone. We had it with some <em>Vin Brulé</em>.</p>
<p><img src="https://fantinel.dev/cms/media/articles/Chesnuts.jpeg" alt="Hand holding 9 chestnus" title="The taste is not bad, but I didn&#x27;t like the texture."></p>
<p>After the walk, we stayed for around 40min for a concert by a guy named Nick Hart, from the UK. He sang old English folksongs and it was a pretty cool experience!</p>
<p><img src="https://fantinel.dev/cms/media/articles/Nick%20Hart.png" alt="Musician Nick Hart performing with acoustic guitar at outdoor mountain venue, with forested hillside in background and small audience seated below." title="Live music with the Alps and a valley in the background. Not bad, huh?"></p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/cms/media/articles/Woods.jpeg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/cms/media/articles/Woods.jpeg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/quick-reviews/ea-sports-fc-25</guid>
      <title>Quick Review: EA Sports FC 25</title>
      <link>https://fantinel.dev/quick-reviews/ea-sports-fc-25</link>
      <pubDate>Sun, 12 Oct 2025 12:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>EA Sports FC 25 <br> 2024, Electronic Arts</p>
        <p>My rating: I like it</p>
        <p><img src="https://m.media-amazon.com/images/I/81+1uDXS83L._UF1000,1000_QL80_.jpg" /></p>
        
        <p>I know, I know. It’s EA. But I love football and there’s enough good here to let me live the fantasy of a career mode.</p>
<p>The menus are a complete mess and half of them don’t work most of the time, but the gameplay is cool. I’ve founded Polenta FC and have had a blast managing the club.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://m.media-amazon.com/images/I/81+1uDXS83L._UF1000,1000_QL80_.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://m.media-amazon.com/images/I/81+1uDXS83L._UF1000,1000_QL80_.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/quick-reviews/no-mans-sky</guid>
      <title>Quick Review: No Man&apos;s Sky</title>
      <link>https://fantinel.dev/quick-reviews/no-mans-sky</link>
      <pubDate>Sun, 12 Oct 2025 12:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>No Man's Sky <br> 2016, Hello Games</p>
        <p>My rating: Loved it!</p>
        <p><img src="https://m.media-amazon.com/images/M/MV5BZjIzMmNjNmMtNTkxOS00ZGFlLTg2ZDItZjM2MWNlOTZjMjAyXkEyXkFqcGc@._V1_FMjpg_UX1000_.jpg" /></p>
        
        <p>I’ve played this game on and off since 2021, and surprisingly never stopped to give it a review.</p>
<p>It’s a dream come true, honestly. Space exploration at its finest, exploring worlds is always fun, there’s so much to do and still the best thing to do is nothing. I love just walking around a pretty planet and taking screenshots of the amazing views. Whatever problems it has just fade away in the vastness of space.</p>
<p>I absolutely adore the dialogues, too. So many existential quotes that just hit the right spot.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://m.media-amazon.com/images/M/MV5BZjIzMmNjNmMtNTkxOS00ZGFlLTg2ZDItZjM2MWNlOTZjMjAyXkEyXkFqcGc@._V1_FMjpg_UX1000_.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://m.media-amazon.com/images/M/MV5BZjIzMmNjNmMtNTkxOS00ZGFlLTg2ZDItZjM2MWNlOTZjMjAyXkEyXkFqcGc@._V1_FMjpg_UX1000_.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/blog/cool-links-september-2025</guid>
      <title>Cool Links Vol. 15: September, 2025</title>
      <description>Links to the best stuff I&apos;ve read or watched during the month of September, 2025</description>
      <link>https://fantinel.dev/blog/cool-links-september-2025</link>
      <pubDate>Tue, 30 Sep 2025 08:09:53 +0000</pubDate>
      <category>Cool Links</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/cool-links-september-2025">
              read on the site!
            </a>
          </strong>
        </div>

        <p>Hi there! As of today, Cool Links has been officially written from both sides of the Atlantic! Last month I mentioned how tired my wife and I were of packing and now we’re still tired, but we’re finally in Italy!</p>
<p>This move has been a huge undertaking and honestly we still haven’t gotten used to it. We’re staying at my brother’s and looking for a place of our own. Hopefully, we’ll find one soon!</p>
<p>This doesn’t mean I couldn’t grab some cool stuff from around the web, though. I even discovered <a href="https://gomakethings.com">Chris Ferdinandi’s blog</a> which is amazing. There’s 3 links from him here and I had to try real hard to not put even more. Definitely worth a follow/subscription/whatever if you like what he writes!</p>
<h2 id="fun">Fun</h2>
<p>🌟 <strong><a href="https://everynoise.com/">Every Noise at Once</a>, by Glenn McDonald</strong></p>
<p>Grab your headphones and get ready to lose some hours. This website compiles every subgenre of music and algorithmically sorts them out in relation to one another. It’s great to learn about new genres you might like or to find something similar to what you already know!</p>
<p><strong><a href="https://adrift.site/">Adrift</a></strong></p>
<blockquote>
<p>Adrift is a quiet space where doubts become paper boats and drift together across a shared sea.</p>
</blockquote>
<p>What a neat lil’ website. You can write your own doubts or self-care notes and let them float out in a virtual sea, alongside the notes of many others. There’s some background music too.</p>
<h2 id="deep-reads">Deep Reads</h2>
<p><strong><a href="https://gomakethings.com/the-meaning-of-life/">The meaning of life…</a>, by Chris Ferdinandi</strong></p>
<blockquote>
<p>… is just to be alive.</p>
</blockquote>
<p>Beautiful reminder of why chasing goals and meaning only leads us away from them. A bit related to my <a href="https://fantinel.dev/blog/longterm-goals/">longterm goals</a> post from last year.</p>
<p><strong><a href="https://gomakethings.com/means-of-production/">Means of Production</a>, by Chris Ferdinandi</strong></p>
<blockquote>
<p><strong>One of capitalism’s greatest successes is that it’s robbed us of imagination.</strong>
(…)
We struggle to imagine what life could look like under a different system. How it would be better. How it would be worse. How it would be different.
(…)
Utopias don’t exist. They never will. But I refuse to accept this system we toil under—while better than monarchies and fiefdoms—is as good as it gets.</p>
</blockquote>
<p><strong><a href="https://pluralistic.net/2025/09/13/consumption-choices/#marginal-benefits">Wallet voting</a>, by Cory Doctorow</strong></p>
<blockquote>
<p>Make individual choices that make your life better. Take <em>collective</em> action to make society better.</p>
</blockquote>
<p>Cory has such a nice way with words — he can express complex thoughts so simply.</p>
<p>This one is a banger. It’s both encouragement to do more against evil and reassurance for when you feel like giving up.</p>
<h2 id="dev">Dev</h2>
<p><strong><a href="https://accented.dev/">Accented</a>, by Pavel Pomerantsev</strong></p>
<p>This tool looks pretty cool! It’s a two-liner solution for web apps that automatically highlights accessibility issues on whatever you’re working on.</p>
<p>I haven’t tested it myself yet (busy month), but will definitely look into it soon.</p>
<p><strong><a href="https://blog.kevinroleke.com/npm">npm: How did we get here?</a>, by Kevin Roleke</strong></p>
<p>I think it’s widely known that the JS dev community relies too much on dependencies, especially through npm packages, and that it’s really hard to avoid this problem (I use as few packages as possible, but each dependency has its own hundreds of dependencies which also have hundreds more…).</p>
<p>But I think I never stopped to think of how <em>easy</em> it is to publish a package there. Which also means, it’s <em>too easy</em> to publish a malicious or compromised package, that gets downloaded and executed on our computers with no proper vetting. Scary.</p>
<p><strong><a href="https://gomakethings.com/why-i-still-prefer-ems-over-rems/">Why I still prefer ems over rems</a>, by Chris Ferdinandi</strong></p>
<p>Neat short article that goes over a bit of the differences between <code>em</code>s and <code>rem</code>s in CSS, with nice examples.</p>
<h2 id="wrapping-up">Wrapping up</h2>
<p>Thanks for reading it all the way here! Hope to see you next month as well! ;)</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/opengraph/cool-links/Q29vbCUyMExpbmtzJTIwVm9sLiUyMDE1/MjAyNS0wOS0zMFQwOCUzQTA5JTNBNTMuNTMxWg==/image.png"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/opengraph/cool-links/Q29vbCUyMExpbmtzJTIwVm9sLiUyMDE1/MjAyNS0wOS0zMFQwOCUzQTA5JTNBNTMuNTMxWg==/image.png"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/blog/craft-obsidian-ramblings</guid>
      <title>A Madman&apos;s Ramblings on Craft and Obsidian</title>
      <description>Why won&apos;t my brain ever give me a break?</description>
      <link>https://fantinel.dev/blog/craft-obsidian-ramblings</link>
      <pubDate>Thu, 25 Sep 2025 18:04:41 +0000</pubDate>
      <category>Apps</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/craft-obsidian-ramblings">
              read on the site!
            </a>
          </strong>
        </div>

        <blockquote>
<p>[!info]
The ramblings below are from my personal journal where I jot down whatever’s going through my mind. I felt like this one could resonate with other people so I’m sharing it here with some minor edits.</p>
</blockquote>
<p>I’m thinking of trying out <a href="https://www.craft.do">Craft</a> as my note-taking/second brain app again.</p>
<p>Truth is, <a href="https://obsidian.md">Obsidian</a> resonates so much with me on a lot of things. Principles, extendability, markdown, <a href="https://stephango.com/file-over-app">the file-over-app philosophy</a>, and all the geekiness of it.</p>
<p>It is also truth that Obsidian is not convenient <em>at all</em>.</p>
<p>It’s hard to jot things down quickly — except maybe on the laptop, where I can have the daily note open and just jot things on the Scratch Pad section of daily notes.</p>
<p>But on the phone it’s just cumbersome — I feel like I actively try to <em>avoid</em> opening Obsidian on the phone altogether. And most of it comes from the fact that ==dealing with text on a phone just plainly sucks==. It was the main problem of <a href="https://notion.so">Notion</a> too.</p>
<p>Craft excels at it (at least I haven’t seen anything as good) because it doesn’t assume you’re trying to select text, and is instead dealing with paragraphs/whatever as blocks. This just makes it easier to work with on the phone.</p>
<p>There’s plenty of downsides too. Treating everything as a block and not as text is good on mobile, but not so much on a laptop. There’s some bugs here and there (example: I can’t apply the Daily Note Template reliably, it always glitches out with a different outcome).</p>
<p>Plus, Obsidian has <em>a lot</em> of qualities too. The <a href="https://help.obsidian.md/links">wikilinks</a> are amazing, and the network you can build with them is great. Being able to link to terms even if their pages don’t exist yet, for example, is a fantastic example of the philosophy behind Obsidian — <em>links, everywhere</em>. You don’t have to know in advance what will be important, you just do some basic prepping (that honestly becomes second nature after a while) and, if those things eventually become important, you’ll already have a network of mentions to that thing.</p>
<p><img src="https://fantinel.dev/cms/media/articles/obsidian-graph-view.png" alt="Screenshot of Obsidian showing a network of linked notes" title="The Graph View has zero functional purpose, but it looks so cool, I love it."></p>
<p>And then there’s the last thing, which is quite major: migrating things from my Obsidian vault to Craft. Looks like that’s a pain. While Markdown gets converted automatically, wikilinks conversion is not reliable. And without wikilinks, the whole structure falls apart.</p>
<hr>
<p>This piece started as a quick way to jot down what I was doing, but it eventually became the whole reasoning as to why I think Obsidian is still the right choice for me, mobile difficulties and all. There’s some things I wish it had though; and probably there are ways to do it, I just need to do some research:</p>
<ol>
<li>A quick way (probably an iOS/macOS shortcut to quickly jot things to the <em>Journal</em> section of my daily notes;
<ul>
<li>Added difficulty: if the daily note does not exist yet, it needs to be created <strong>and</strong> have the daily note template applied to it.</li>
</ul>
</li>
<li>Same as above, but on the <em>Scratch Pad</em> section.</li>
<li>I like the idea of having a local AI analyze the files in my vault and be able to ask questions. Example: the other day I wanted to find something I <em>knew</em> I had written down some months ago, but couldn’t remember where. Having a <em>local</em> AI model look it up is a good use case in my book.
<ul>
<li>✨<em>Update</em>: well, since it’s all just .md files on a folder, I can actually use Windsurf (a VS Code fork with built-in AI models I use) to do that. Works well, and speaks volumes about the file-over-app philosophy!</li>
</ul>
</li>
</ol>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/cms/media/articles/craft-obsidian-ramblings.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/cms/media/articles/craft-obsidian-ramblings.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/quick-reviews/mother</guid>
      <title>Quick Review: Mother!</title>
      <link>https://fantinel.dev/quick-reviews/mother</link>
      <pubDate>Tue, 09 Sep 2025 12:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>Mother! <br> 2017, Darren Aronofsky</p>
        <p>My rating: Loved it!</p>
        <p><img src="https://m.media-amazon.com/images/M/MV5BMzc5ODExODE0MV5BMl5BanBnXkFtZTgwNDkzNDUxMzI@._V1_SX300.jpg" /></p>
        
        <p>Watching this movie feels like a nightmare. Things happen and you really don’t have much say in it, then they start spiraling out of control and you’re completely unable to do anything. You can’t run, yell or fight.</p>
<p>The religious allegories are quite obvious and are great, but that nightmarish feeling was really something I hadn’t felt with a movie before.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://m.media-amazon.com/images/M/MV5BMzc5ODExODE0MV5BMl5BanBnXkFtZTgwNDkzNDUxMzI@._V1_SX300.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://m.media-amazon.com/images/M/MV5BMzc5ODExODE0MV5BMl5BanBnXkFtZTgwNDkzNDUxMzI@._V1_SX300.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/blog/cool-links-august-2025</guid>
      <title>Cool Links Vol. 14: August, 2025</title>
      <description>Links to the best stuff I&apos;ve read or watched during the month of August, 2025</description>
      <link>https://fantinel.dev/blog/cool-links-august-2025</link>
      <pubDate>Sun, 31 Aug 2025 23:00:00 +0000</pubDate>
      <category>Cool Links</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/cool-links-august-2025">
              read on the site!
            </a>
          </strong>
        </div>

        <p>Hi there! This month’s Cool Links are coming in a little bit later because I’m in the middle of moving. Which means, this is the last issue of Cool Links written in Brazil, at least for a while!</p>
<p>As I write this, my wife and I are resting a bit after 4 days of packing, selling, donating, and moving stuff over from the apartment we used to rent and into my parents’ house. We’re staying here until the 12th, when we actually depart to our next home, in Italy!</p>
<p>Without further ado, let’s check out the links of the month:</p>
<h2 id="fun">Fun</h2>
<p><strong><a href="https://gamer.church/">GAMER CHURCH</a></strong></p>
<p>What exactly is this site? <em>I don’t know.</em> But it looks awesome.</p>
<h2 id="tech">Tech</h2>
<p><strong><a href="https://apps.apple.com/app/ublock-origin-lite/id6745342698">uBlock Origin Lite for (iOS) Safari</a></strong></p>
<p>Finally! I’ve been using uBlock Origin on my desktop browsers since it’s existed, and it makes the commercial web usable. Now it’s finally available for iOS Safari (maybe on the Mac too?)</p>
<p>Safari has had some ad blockers for a while, but none were as good as this one. This one blocks ads, trackers, and even allows hiding some page elements you select, just like the desktop version.</p>
<p>A must-have in all my web browsers.</p>
<p><strong><a href="https://help.obsidian.md/bases">Obsidian Bases</a></strong></p>
<p>Obsidian, my note-taking, second-brain and CMS app just got a huge feature: Notion-like databases, here simply called <em>Bases</em>. You can use them with any files in your vault and in my initial testing, it’s pretty powerful!</p>
<p>I’m already using it to manage Cool Links, and looking forward to use it for more of my stuff in the future.</p>
<p><strong><a href="https://piccalil.li/blog/are-peoples-bosses-really-making-them-use-ai/">Are people’s bosses really making them use AI tools?</a>, by Andy Bell</strong></p>
<p>Time and time again, we’ve been seeing companies that go all-in on AI in hopes of not falling behind or standing out while the bubble doesn’t burst. This article has some real life testimonies of employees that are being forced to use AI in their work - even if it makes things harder and makes the results worse.</p>
<h2 id="dev">Dev</h2>
<p><strong><a href="https://ishadeed.com/article/time-layout/">Better CSS layouts: Time.com Hero Section</a>, by Ahmad Shadeed</strong></p>
<p>Sharing Ahmad Shadeed’s posts here kinda feel like cheating at this point. They’re always a gem!</p>
<p>This one goes extremely in-depth into redesigning the hero layout of Time.com. Ahmad explains his thought process on every step of the way, and dives into a lot of fun, new-ish CSS principles like container and style queries, :has, grid and even text wrapping!</p>
<p>A masterclass, really.</p>
<p><strong><a href="https://una.im/5-css-functions/">5 Useful CSS functions using the new @function rule</a>, by Una Kravets</strong></p>
<p>CSS is finally getting functions! And if you’re struggling to think of good use cases for them or thinks they don’t make sense at all Una will change your mind real quick.</p>
<p>Too bad they’re still only supported in Chromium and are probably ways off from being usable in production. But it’s nice to get a glimpse of what the future holds.</p>
<p><strong><a href="https://www.joshwcomeau.com/svg/interactive-guide-to-paths/">An Interactive Guide to SVG Paths</a>, by Josh Comeau</strong></p>
<p>I always have trouble understanding SVGs, but thanks to this article, I will have teensy bit less trouble than before. Josh’s articles are always a gem with all the interactivity and this is no exception.</p>
<h2 id="wrapping-up">Wrapping up</h2>
<p>Hope you enjoyed the cool links from this month, and see you on the next!</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/opengraph/cool-links/Q29vbCUyMExpbmtzJTIwVm9sLiUyMDE0/MjAyNS0wOC0zMVQyMyUzQTAwJTNBMDAuMDAwWg==/image.png"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/opengraph/cool-links/Q29vbCUyMExpbmtzJTIwVm9sLiUyMDE0/MjAyNS0wOC0zMVQyMyUzQTAwJTNBMDAuMDAwWg==/image.png"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/blog/arc-browser-replacement</guid>
      <title>The Quest for a Good Arc Replacement</title>
      <description>I’ve been trying to find a good replacement for Arc and got reminded of how hard it is to find a good option for a browser.</description>
      <link>https://fantinel.dev/blog/arc-browser-replacement</link>
      <pubDate>Sun, 03 Aug 2025 00:00:00 +0000</pubDate>
      <category>Apps</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/arc-browser-replacement">
              read on the site!
            </a>
          </strong>
        </div>

        <p>I’ve been a happy Arc user since 2022. It’s well-designed, significantly improved how I use a web browser, works well, and has (had) a team of passionate and talented people behind it that frequently pumped out some nice, weird, fun ideas. However, lately I’ve been trying to find a good replacement for it and got reminded of how hard it is to find a <em>good</em> option for a browser.</p>
<h2 id="why-do-i-need-a-replacement">Why do I need a replacement?</h2>
<p>Well, Arc has gathered a considerably big user base, and more importantly, a user base that loves the product. However, it’s always been free. You know what that means, right? It means the company operates at a loss, surviving off of VC money. And they have to pay that debt off eventually.</p>
<p>Personally, I’d be very happy to pay a subscription for Arc, and I know a lot of people would do so too. It’d be a far healthier business model, even if it meant the product would evolve more slowly. But you see, once VC money comes in, everything scales considerably. A loyal paying user base is not enough; you need to shoot for the stars or you fail.</p>
<p>Most tech companies go through this exact same route; and when they need to start paying off that debt, they <a href="https://en.wikipedia.org/wiki/Enshittification">enshittify</a>; their product becomes a shadow of what it once was because it now needs to squeeze as much money as possible at every opportunity.</p>
<p>With Arc though, that never happened. They did some experiments with “Arc Max”, the AI offerings inside the browser, but the browser itself never got enshittified. Thankfully, I guess, it only got put aside. Arc is now no longer under active development, and is instead just receiving Chromium updates and security fixes.</p>
<p>My theory is that they figured out that they’d never be able to monetize a niche web browser as much as they needed, and just gave up on the product. And started working on Dia, an “AI-first browser” (that’s pretty much dead on arrival because everything it does is already being done by the big ones).</p>
<p>So, while Arc is still as good as it ever was, I know it’s not gonna be long before it’s sunsetted and stops getting Chromium updates. I’m trying to get a bit ahead and find a replacement to dip my toes in before I’m forced to make the jump.</p>
<h2 id="most-options-are-just-a-no-no">Most options are just a no-no</h2>
<p>Yes, I am especially picky with the software I use. But hey, I make a living on web browsers (and a lot of my hobbies also take place inside one), so I’m allowed to care this much about it, mkay?</p>
<p>When we’re talking web browsers, we have three “families”, defined by the three major browser engines available on the market:</p>
<ul>
<li>Chromium-based browsers, that use the same engine as Chrome. Lots of options here, with main examples being Arc, Edge, Brave, Vivaldi, Opera;</li>
<li>Firefox-based browsers, which surprisingly use the Firefox engine. Not a lot of variety, but Zen is the standout;</li>
<li>Webkit-based browsers, which use the same engine as Safari. Besides Safari, I only know about Orion using this engine (on the Mac);</li>
<li>🎗️ Reminder: <strong>on iOS and iPadOS, every browser uses the Safari engine</strong>, because Apple doesn’t want you to be happy (they don’t allow alternative browser engines in their App Store).</li>
</ul>
<p>As a web developer and just someone that likes the web, <strong><a href="https://webventures.rejh.nl/blog/2024/history-of-safari-show-stoppers/">I have a strong dislike for the WebKit engine</a>,</strong> so using it willingly is just not for me.</p>
<p>Firefox has a really cool mission in theory (Mozilla sucks as a company though), but has been notoriously slow at adopting new web standards and has been behind even Safari lately (perhaps it has something to do with Mozilla forgetting what a web browser is for?). I love learning new things and implementing them on my work (with <a href="https://fantinel.dev/blog/progressive-enhancement/">Progressive Enhancement</a>, always), so it’d <em>suck</em> to be unable to learn new things because they don’t work on my browser of choice.</p>
<blockquote>
<p>[!info]
If the browser engine isn’t a dealbreaker for you, <a href="https://zen-browser.app">Zen browser</a> is aiming to be pretty much a carbon copy of Arc, and it’s open source!</p>
</blockquote>
<p>Which leaves me with Chromium-based browsers. Chrome, Edge and Opera are spyware (Edge has some cool features though) and Brave is owned by a bigot; leaving Vivaldi as the only real option in there.</p>
<h2 id="vivaldi-not-the-composer">Vivaldi (not the composer)</h2>
<p><a href="https://vivaldi.com">Vivaldi</a> was founded by the same people who worked in the original Opera browser, back in the 90s. Since Opera was sold and became a shadow of its former self, Vivaldi was created to try and fill in the power-user-shaped gap in the browser space.</p>
<p>It’s Chromium-based, built in Europe, and seems to be very privacy-friendly, with a “make the web better” mission similar to Mozilla. Sadly, it’s not open source, so these claims are not verifiable.</p>
<p>It’s main selling feature is being <em>extremely customizable</em>, both visually and in how you use it.</p>
<p>I had already tried Vivaldi out in the past, but it never stuck the landing for me. I don’t really need <em>extreme customizability</em>, I prefer a thoughtfully designed UX instead. Which is why Arc appealed to me in the first place. But since no other options feel like they’d appeal to that preference, I wanted to figure out if I could either mimic my Arc workflow in Vivaldi or maybe settle for a middle ground that I could adapt to.</p>
<p>I figured I’d start by thinking exactly what I liked about Arc:</p>
<ol>
<li>It looks good;</li>
<li>The Command palette-thing (cmd+t) flow is <em>wonderful</em>. Opening an empty new tab makes zero sense, lemme use my keyboard and get straight to what I wanna do!</li>
<li>The vertical tabs on the side and being able to swipe between spaces is great to keep tabs from getting out of control.</li>
</ol>
<p>With that in mind, let’s analyse Vivaldi on three criteria: The Looks, The Clacks, and The Tabs.</p>
<h3 id="criteria-one-the-looks">Criteria One: <em>The Looks</em></h3>
<p>This is the Achilles’ heel of Vivaldi, in my opinion. It doesn’t look <em>bad</em>, but it’s far from being as good-looking as Arc is. Some of what Arc does is just not doable (since Vivaldi doesn’t use macOS’ native technologies to build its UI), and the focus on customization means a lot of compromises have to be made. That being said, some tweaks here are there made it look kinda good.</p>
<p>I’m using a Catppuccin theme because I like the color palette (it’s the same as this website) and used the sliders in the Theme editor to tweak colors, radii, and spacing a little bit.</p>
<p><img src="https://fantinel.dev/cms/media/articles/vivaldi-theme.png" alt="Screenshot of the Vivaldi browser. It&#x27;s a very normal looking browser, with a light pastel theme applied."></p>
<h3 id="criteria-two-the-clacks">Criteria Two: <em>The Clacks</em></h3>
<p><em>(as in the sound a keyboard makes)</em></p>
<p>After thinking about it, the command-palette-thing as a replacement for the new tab is Arc’s unsung hero:</p>
<p><img src="https://fantinel.dev/cms/media/articles/arc-command-tab.png" alt="Screenshot of Arc&#x27;s command palette. I typed &#x22;Vivaldi&#x22; and the command palette is displaying suggestions for the Vivaldi site, searching for Vivaldi, and some matches in my browsing history. My active tab is still visible in the background."></p>
<p>It’s so elegant; it works well, it’s fast, it doesn’t get in your way, it keeps your current context in view, and it’s keyboard-centric. After three years of using it every day I barely paid any mind to it, but trying to use a browser without it was a revelation. I didn’t know how good it was until it was gone.</p>
<p>You wanna know something cool? Vivaldi has it, too! It’s not as elegant visually, but it works just as well! It’s called “Quick Commands”, and by default is accessed with cmd+e (on a Mac).</p>
<p><img src="https://fantinel.dev/cms/media/articles/vivaldi-command-palette.png" alt="Screenshot of Vivaldi&#x27;s command palette. Similar to Arc&#x27;s, it displays a search suggestion, bookmarks and history entries that match my query. My active tab is also visible in the background."></p>
<p>My muscle memory was using cmd+t (new tab) to open the command palette though, so I remapped it to that shortcut. A small issue was that, by default it opens URLs in the same tab you’re currently in, but hey, Vivaldi has a toggle for everything! A quick look into the Quick Command settings presented me with a beautiful “Open Links in New Tab” checkbox.</p>
<h3 id="criteria-three-the-tabs">Criteria Three: <em>The Tabs</em></h3>
<p>The main thing you notice when you start using Arc: the tabs are on the left! And they’re displayed vertically!</p>
<p>Vivaldi, of course, allows you to do just that. You can keep your tabs on the top, like all the boring kids do; you can put them on the left or right if you’re cool, or you can even put them in the bottom of the window if you’re all about breaking society’s expectations.</p>
<p>Putting them on the left was the first thing I did. Here’s how it looks:</p>
<p><img src="https://fantinel.dev/cms/media/articles/vivaldi-side-tabs.png" alt="Screenshot of Vivaldi, with tabs on the left. Their look is not very polished."></p>
<p>You immediately notice that while the tabs are now indeed on the left, they don’t look right (hah!). The address bar and bookmarks are still on the top and take up a lot of space, and there’s just… <em>something</em> missing in there. No folders, no spaces, they don’t feel as clickable, I’m not sure how to explain it. It’s not <em>fully</em> collapsible like Arc’s, but you can drag it to make it small enough that it only shows the favicons.</p>
<p>This annoyed me because I couldn’t build an exact replica of my Arc workflow, initially. The lack of spaces so I could properly separate my work and personal tabs was almost enough to make me give up on using it. But I found out about the <em>Tab Stacks</em>.</p>
<h4 id="tab-stacks-and-the-portable-rabbit-holes">Tab Stacks (and the portable rabbit holes)</h4>
<p>Tab Stacks are available in Chromium and have existed for a while now. It basically allows you to group tabs together manually, assign them a color, and collapse/expand that stack like an accordion. However, in Vivaldi they have a (optional) much better UI. Instead of expanding to the sides, they just expand into a second level of tabs.</p>
<p><img src="https://fantinel.dev/cms/media/articles/vivaldi-tab-stacks.png" alt="Screenshot of Vivaldi&#x27;s tab bar, showing two levels of tabs."></p>
<p>Surprisingly, this approach aligns much better with how I like to manage my tabs than Arc’s ever did! Very often at work I have to put whatever I’m working on on hold and quickly jump into something else for a few minutes. Being able to just stack relevant tabs together and <em>hide the irrelevant ones</em> makes tab management much less overwhelming.</p>
<p>With Arc, if I had to context switch like this, I’d often get the tabs mixed up (creating a separate space for it meant I lost the important tabs pinned to my general “Work” space). With Tab Stacks, going back to that context is just a click away, and the tabs reappear at the second level of tabs.</p>
<p>Something that makes Tab Stacks even better though: a checkbox in Vivaldi Settings that makes all tabs opened from within other tabs to spawn in a stack. Or, as I’d like to call it, <strong>the Portable Rabbit Hole setting</strong>.</p>
<p>Let’s say you’re enjoying a short break and reading the latest post of Cool Links on a beautiful website. There are so many links to click on and so many tabs to open, but you need to get back to work in 10 minutes and those tabs will get lost amidst the Jira tickets. Well, with your Portable Rabbit Hole, you no longer have that problem!</p>
<p>All the tabs you’ve opened by clicking on those cool links will be neatly stacked together so you can come back to them later. You can even add some color and a name to that stack if you want.</p>
<p><img src="https://fantinel.dev/cms/media/articles/portable-rabbit-hole.gif" alt="Screen recording showing the flow of opening links in a new tab and having them all stack together in a single group instead of getting mixed up with other tabs."></p>
<h2 id="so-did-i-make-the-move">So, did I make the move?</h2>
<p>Yes… and no.</p>
<p>I’ve been using Vivaldi pretty much daily for about a month now, and using Arc less and less. But I still miss it sometimes. So I’ve had a few days where I “relapsed” and went back to Arc for a bit.</p>
<p>And then something weird happened. The Arc workflow became weird to me again. Something about having bookmarks mixed together with open tabs started feeling weird. The loss of Tab Stacks also felt like a downgrade.</p>
<p>It’s funny how I was so afraid of not being able to get used to a “normal” browser after so much time using Arc, and now I got so used to Vivaldi that Arc became weird again. It’s still nice to use though, and I might use it as my main browser again if I feel like it.</p>
<p>In the end this just makes me feel better that when Arc inevitably dies, I’ll just handle it and everything will be fine. No point in not enjoying it now while I still have it.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/cms/media/articles/arc-browser-replacement-cover.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/cms/media/articles/arc-browser-replacement-cover.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/blog/cool-links-july-2025</guid>
      <title>Cool Links Vol. 13: July, 2025</title>
      <description>Links to the best stuff I&apos;ve read or watched during the month of July, 2025</description>
      <link>https://fantinel.dev/blog/cool-links-july-2025</link>
      <pubDate>Thu, 31 Jul 2025 00:00:00 +0000</pubDate>
      <category>Cool Links</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/cool-links-july-2025">
              read on the site!
            </a>
          </strong>
        </div>

        <p>Hi there! This month’s Cool Links are all about the web.</p>
<h2 id="fun">Fun</h2>
<p><strong><a href="https://drawafish.com/">Draw a Fish Dot Com</a>, by fifteen.games</strong></p>
<p><em>This is what the web was made for.</em> Just draw a fish. Then watch your creation swim with fishes from all over the world. Beautiful.</p>
<h2 id="deep-reads">Deep Reads</h2>
<p><strong><a href="https://www.markpitblado.me/blog/netstuck/">Netstuck</a>, by Mark Pitblado</strong></p>
<p>This article talks a bit about the <em>Netstuck</em> effect, when you feel forced to use a service or platform just because everyone is there, and how that’s different from a monopoly.</p>
<p>As someone with no commercial social media accounts, I relate to this a lot. And even though I can live without a Facebook or Instagram account, I tried not using WhatsApp in Brazil for a couple years and just had to concede at some point, because there’s just no way of getting in contact with anyone (especially businesses) in here without it.</p>
<h2 id="dev">Dev</h2>
<p><strong><a href="https://jsdate.wtf/">new Date(“wtf”)</a></strong></p>
<p>This game tests your knowledge on JavaScript’s Date class. It’s funny because it’s sad.</p>
<p><strong><a href="https://www.granola.ai/blog/dont-animate-height">Don’t animate height!</a>, by Jim Fisher</strong></p>
<p>Neat article in which Jim explains why a seemingly harmless CSS transition property (using <code>height</code>) was spiking up CPU usage. It goes through the thought process of identifying the issue, understanding it, and solving it. (spoiler: animating <code>transform</code> is much cheaper!)</p>
<p><strong><a href="https://www.joshwcomeau.com/svg/friendly-introduction-to-svg">A Friendly Introduction to SVG</a>, by Josh W. Comeau</strong></p>
<p>I <em>know</em> SVGs are amazing, and I’ve done some cool things with them. But I never <em>really</em> knew what I was doing. This fantastic post by Josh Comeau goes over the basics and focuses on SVGs you can create yourself through code, instead of relying on vector editing softwares like Figma or Illustrator.</p>
<p>I haven’t tried these tips in practice yet, but hopefully I’ll have a neat idea I can use them on soon 👀</p>
<h2 id="tech">Tech</h2>
<p><strong>(pt-br) <a href="https://trilux.org/2025/07/como-eu-salvo-links-e-preservo-conteudos-no-mundo-pos-pocket.html">Como eu salvo links e preservo conteúdos no mundo pós-Pocket</a>, by Augusto Campos</strong></p>
<p>This article (in Brazilian Portuguese) was a huge inspiration to me in reworking how I save my Cool Links, starting this month. I’m using the Obsidian Web Clipper instead of the custom solution the author had, but the principles are still the same.</p>
<p>Building up a weekly routine to organize things has also been a good change, as I don’t have a lot to do at the end of the month when I’m putting this post together.</p>
<h2 id="wrapping-up">Wrapping up</h2>
<p>I hope you enjoyed the links this month! Life’s been busy with all the move preppin’, but I’m glad I was still able to grab some links.</p>
<p>I’ve changed how I save the links to make it easier to organize them at the end of the month, and I’m planning to build an archive of all links I’ve posted so far, in a single page. No promises as to when though; my August looks really busy already 😵</p>
<p>Anyway, see you next month!</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/opengraph/cool-links/Q29vbCUyMExpbmtzJTIwVm9sLiUyMDEz/MjAyNS0wNy0zMVQwMCUzQTAwJTNBMDAuMDAwWg==/image.png"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/opengraph/cool-links/Q29vbCUyMExpbmtzJTIwVm9sLiUyMDEz/MjAyNS0wNy0zMVQwMCUzQTAwJTNBMDAuMDAwWg==/image.png"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/quick-reviews/skeletá</guid>
      <title>Quick Review: Skeletá</title>
      <link>https://fantinel.dev/quick-reviews/skeletá</link>
      <pubDate>Mon, 21 Jul 2025 12:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>Skeletá <br> 2025, Ghost</p>
        <p>My rating: Loved it!</p>
        <p><img src="https://fantinel.dev/cms/media/quick-reviews/skeleta.jpg" /></p>
        
        <p>Every single Ghost album improves on the last, and this is no exception. Every single song in this is a banger, with special mentions to <em>Lachryma</em>, <em>Satanized</em>, <em>De Profundis Borealis</em> and <em>Umbra</em>. These might just be the top 4 Ghost songs ever for me.</p>
<p>I gotta say though, the album as a whole is not as cohesive as IMPERA was. It has better songs individually, but IMPERA felt like a better album (at least on first listen) because every song complements each other. Skeletá doesn’t have a single musical identity like that, but whatever it’s doing, every song works individually.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/cms/media/quick-reviews/skeleta.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/cms/media/quick-reviews/skeleta.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/blog/quick-reviews</guid>
      <title>New Website Section: Quick Reviews!</title>
      <description>A special place to dump my unsolicited opinions of pieces of media!</description>
      <link>https://fantinel.dev/blog/quick-reviews</link>
      <pubDate>Sun, 20 Jul 2025 00:00:00 +0000</pubDate>
      <category>Meta</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/quick-reviews">
              read on the site!
            </a>
          </strong>
        </div>

        <p>For a little over a year, I’ve been writing quick reviews of movies and TV shows I’ve watched. I was inspired by <a href="https://mastodon.social/@matt_birchler">Matt Birchler on Mastodon</a> and his own <a href="https://quickreviews.app">Quick Reviews web app</a>, and while I initially used his app to write my own and post to social media, I decided to give my reviews a home in here.</p>
<p>So, after gathering my reviews as Markdown files in Obsidian, they’re now in a new section here. Meet <a href="https://fantinel.dev/quick-reviews">Quick Reviews</a>!</p>
<p>The review cards are not images, but just web elements like everything else. Which means they are responsive and lightweight. I made it as easy as possible to publish them on my side, and there’s even a download button to allow me to quickly post them to social media!</p>
<h2 id="the-technical-part">The Technical Part</h2>
<p>If you’re interested in how I made it work, it’s actually not that complicated.</p>
<p>This website already uses Markdown files for the blog posts, and since it uses Astro, fetching data for the Quick Reviews was as easy as declaring a new Content Collection, pointing to the right folder, and declaring its schema. It will all make sense in a bit.</p>
<h3 id="data-schema">Data Schema</h3>
<p>First of all, I needed to define what a review would look like. Its main pieces of information are the movie/show’s name, the rating, and the content of my review. Some metadata is also nice to help tidy things up, like the date I reviewed it, the year the movie came out, and who the director was.</p>
<p>In Markdown, this kind of data is all part of the file’s <em>Frontmatter</em>, a special section at the top of the file. The only exception would be the review’s content, which is just pure Markdown, right below the frontmatter. It looks like this:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="markdown"><code><span class="line"><span style="color:#79B8FF;font-weight:bold">---</span></span>
<span class="line"><span style="color:#E1E4E8">type: (movie/tv show)</span></span>
<span class="line"><span style="color:#E1E4E8">metadata: (year, director, etc)</span></span>
<span class="line"><span style="color:#E1E4E8">image: (movie poster)</span></span>
<span class="line"><span style="color:#E1E4E8">rating: (my rating)</span></span>
<span class="line"><span style="color:#E1E4E8">date: (when I reviewed it)</span></span>
<span class="line"><span style="color:#79B8FF;font-weight:bold">---</span></span>
<span class="line"></span>
<span class="line"><span style="color:#E1E4E8">(what I have to say about it)</span></span></code></pre>
<p>With just that, I’m able to display it however I want on my site. I added two extra properties that are specific to how I want to display them on my website: <code>theme</code>, with a light an dark option that mainly control the color of the text, and <code>customBg</code>, which allows me to set any color I want as the background.</p>
<p>On the Astro side of things, I declared this same schema, so Astro knows what data to look for:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { z } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> 'astro:content'</span><span style="color:#E1E4E8">;</span></span>
<span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { QuickReviewRating, QuickReviewType } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> './quick-review-types'</span><span style="color:#E1E4E8">; </span><span style="color:#6A737D">// These are enums that match to what's in Obsidian</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">export</span><span style="color:#F97583"> const</span><span style="color:#79B8FF"> quickReviewSchema</span><span style="color:#F97583"> =</span><span style="color:#E1E4E8"> z.</span><span style="color:#B392F0">object</span><span style="color:#E1E4E8">({</span></span>
<span class="line"><span style="color:#E1E4E8">	type: z.</span><span style="color:#B392F0">enum</span><span style="color:#E1E4E8">([QuickReviewType.Movie, QuickReviewType.TvShow, QuickReviewType.Game, QuickReviewType.Album]),</span></span>
<span class="line"><span style="color:#E1E4E8">	metadata: z.</span><span style="color:#B392F0">string</span><span style="color:#E1E4E8">().</span><span style="color:#B392F0">optional</span><span style="color:#E1E4E8">().</span><span style="color:#B392F0">nullable</span><span style="color:#E1E4E8">(),</span></span>
<span class="line"><span style="color:#E1E4E8">	image: z.</span><span style="color:#B392F0">string</span><span style="color:#E1E4E8">().</span><span style="color:#B392F0">optional</span><span style="color:#E1E4E8">().</span><span style="color:#B392F0">nullable</span><span style="color:#E1E4E8">(),</span></span>
<span class="line"><span style="color:#E1E4E8">	rating: z.</span><span style="color:#B392F0">enum</span><span style="color:#E1E4E8">([QuickReviewRating.DidntLikeIt, QuickReviewRating.Decent, QuickReviewRating.LikeIt, QuickReviewRating.LovedIt]),</span></span>
<span class="line"><span style="color:#E1E4E8">	date: z.coerce.</span><span style="color:#B392F0">date</span><span style="color:#E1E4E8">(),</span></span>
<span class="line"><span style="color:#E1E4E8">	theme: z.</span><span style="color:#B392F0">enum</span><span style="color:#E1E4E8">([</span><span style="color:#9ECBFF">'dark'</span><span style="color:#E1E4E8">, </span><span style="color:#9ECBFF">'light'</span><span style="color:#E1E4E8">]),</span></span>
<span class="line"><span style="color:#E1E4E8">	customBg: z.</span><span style="color:#B392F0">string</span><span style="color:#E1E4E8">().</span><span style="color:#B392F0">optional</span><span style="color:#E1E4E8">().</span><span style="color:#B392F0">nullable</span><span style="color:#E1E4E8">()</span></span>
<span class="line"><span style="color:#E1E4E8">});</span></span></code></pre>
<p>And then, on <code>content.config.ts</code>, I used that schema in my new Content Collection:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#F97583">~</span><span style="color:#E1E4E8">filename content.config.ts</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { quickReviewSchema } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> "@schemas/quick-review"</span><span style="color:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> quickReviews</span><span style="color:#F97583"> =</span><span style="color:#B392F0"> defineCollection</span><span style="color:#E1E4E8">({</span></span>
<span class="line"><span style="color:#E1E4E8">	loader: </span><span style="color:#B392F0">glob</span><span style="color:#E1E4E8">({ pattern: </span><span style="color:#9ECBFF">"**/*.md"</span><span style="color:#E1E4E8">, base: </span><span style="color:#9ECBFF">"./public/cms/quick-reviews"</span><span style="color:#E1E4E8"> }),</span></span>
<span class="line"><span style="color:#E1E4E8">	schema: quickReviewSchema</span></span>
<span class="line"><span style="color:#E1E4E8">});</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">export</span><span style="color:#F97583"> const</span><span style="color:#79B8FF"> collections</span><span style="color:#F97583"> =</span><span style="color:#E1E4E8"> { quickReviews };</span></span></code></pre>
<p>And with that, I can already use Astro’s <code>getCollection</code> to fetch and use that data however I want!</p>
<h3 id="saving-reviews">Saving reviews</h3>
<p>This system is only as good as its ease of adding new reviews to. If adding reviews is too much of a hassle, then my lazy self will just stop doing it.</p>
<p>Obsidian has a built-in Template feature that allows you to quickly add a pre-set structure to a file, like the Frontmatter above. That already makes it better, since I can just create a file, apply the template, and just fill the missing fields. However, some steps were particularly annoying.</p>
<p>Finding a movie’s image, year and director usually required either a web search or opening its page in IMDB. That’s not really a big deal, but it’s still annoying to have to make that trip every time I want to write something.</p>
<p>Luckily, Obsidian has a gigantic ecosystem of plugins that can fill any possible niche. There is a community plugin called <a href="https://quickadd.obsidian.guide">QuickAdd</a> that adds a bunch of possibilities to <em>templates</em>. And, guess what, the plugin author even has an example of how to setup a template that automatically grabs info from IMDB into a note! I just followed <a href="https://quickadd.obsidian.guide/docs/Examples/Macro_MovieAndSeriesScript">the guide for the Movie &#x26; Series script</a> and, voilà, I could have that info in my Quick Reviews in seconds!</p>
<p>I didn’t need all that data on my quick reviews, so I just added the ones I needed to my template. The end result looks like this:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="markdown"><code><span class="line"><span style="color:#E1E4E8">~filename quick-review-template.md</span></span>
<span class="line"></span>
<span class="line"><span style="color:#79B8FF;font-weight:bold">---</span></span>
<span class="line"><span style="color:#E1E4E8">type: "{{VALUE:type}}"</span></span>
<span class="line"><span style="color:#E1E4E8">metadata: "{{VALUE:Year}}, {{VALUE:director}}"</span></span>
<span class="line"><span style="color:#E1E4E8">image: "{{VALUE:Poster}}"</span></span>
<span class="line"><span style="color:#E1E4E8">rating: </span></span>
<span class="line"><span style="color:#E1E4E8">date: ""</span></span>
<span class="line"><span style="color:#E1E4E8">theme: dark</span></span>
<span class="line"><span style="color:#E1E4E8">customBg:</span></span>
<span class="line"><span style="color:#79B8FF;font-weight:bold">---</span></span>
<span class="line"></span></code></pre>
<p>And this is what the flow looks like:</p>
<p><img src="https://fantinel.dev/cms/media/articles/quick-review-flow.gif" alt="Screen recording showing the flow of adding a new review in Obsidian. I use a keyboard shortcut to invoke the Quick Review dialog, type in &#x22;Kill Bill&#x22;, select Kill Bill 2, and write that it&#x27;s pretty good, setting the rating to &#x22;I like it&#x22;"></p>
<h2 id="future-plans">Future Plans</h2>
<p>Honestly, I don’t want this section to be complicated at all. It is above all a place to dump my thoughts about specific pieces of media on. I added some quick filtering for media type, but that’s it. I might revisit the layout of the reviews at some point and maybe add a special RSS feed for them, but besides that, I think I’m done.</p>
<p>Let me know if there’s anything you think would be cool, though. As long as it’s fun, I’m not opposed to it!</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/cms/media/articles/quick-reviews-cover.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/cms/media/articles/quick-reviews-cover.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/blog/cool-links-june-2025</guid>
      <title>Cool Links Vol. 12: June, 2025</title>
      <description>Links to the best stuff I&apos;ve read or watched during the month of June, 2025</description>
      <link>https://fantinel.dev/blog/cool-links-june-2025</link>
      <pubDate>Mon, 30 Jun 2025 00:00:00 +0000</pubDate>
      <category>Cool Links</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/cool-links-june-2025">
              read on the site!
            </a>
          </strong>
        </div>

        <p>Hey there! Happy Pride 🏳️‍🌈!</p>
<p>I hope you’re comfortable and keeping chill/warm depending on which side of the world you are. This month, aside from finally <a href="https://fantinel.dev/blog/website-v5">launching v5 of my website</a>,  I’ve gathered up a few cool links for you to read.</p>
<h2 id="fun">Fun</h2>
<p><strong><a href="https://www.nicchan.me/">Nic Chan’s Website</a></strong></p>
<p>Came across this website on Mastodon and oh wow, it’s so nice! I’ve seen a bunch of websites trying to mimic the old Windows vibes, but this one did it in a way that makes sense. Content is well laid-out and it doesn’t go too far into the “mimicking Windows” aspect.</p>
<h2 id="deep-reads">Deep Reads</h2>
<p><strong><a href="https://youtube.com/watch?v=oYlcUbLAFmw">The Internet Used to Be a Place</a>, by Sarah Davis Baker</strong> (video)</p>
<p>Amazing video about the internet we’ve lost (or rather, was taken from us), and how we can rebuild it.</p>
<p>Absolute fan of Sarah’s storytelling here, the way she weaves through the topics and links (hah!) them to that Hypnospace game is amazing!</p>
<h2 id="dev">Dev</h2>
<p><strong><a href="https://bradfrost.com/blog/post/the-new-separation-of-concerns/">The New Separation of Concerns</a>, by Brad Frost</strong></p>
<p>I learned about separation of concerns over a decade ago, when I was still learning how to code. On the web side, it would very often be related to what language you were using; HTML is for markup, CSS for styles, JS for interactivity, whatever-backend-language-you-use for business logic. That made sense back then but now the lines have become much blurrier.</p>
<p>Brad Frost goes a bit into what a more modern version of that separation of concerns might look like. I haven’t tried out his course, so I can’t comment on how good it is. But I’m a big fan of his atomic design principles so I’d say it might be a good one!</p>
<p><strong><a href="https://webweekly.email/">Web Weekly newsletter</a>, by Stefan Judis</strong></p>
<p>I’ve been subscribed to this newsletter for over a year and it never occurred to me to recommend it here. Stefan Judis does a weekly roundup of web dev-related things and puts them out in a nice, easy-to-read, lighthearted newsletter. It’s been one of my favorites for a while and if you’re reading this, chances are you’ll enjoy his content too!</p>
<h2 id="wrapping-up">Wrapping up</h2>
<p>This is volume 12 of Cool Links, which means I’ve been gathering links like this for a year!</p>
<p>It was the first step in my plan to write more. I figured that by commenting on what other people wrote, I’d feel more comfortable with writing myself. Even though I haven’t actually written more posts here during the last year, I feel like it’s working. I have a few drafts that I haven’t developed enough to post, and some that I just discarded after a bit. But what matters is that I actually wrote something; posting it is just a consequence. 😅</p>
<p>Overall it’s been fun collecting links like these every month, and it’s a way of keeping the spirit of the web alive. It is, after all, a bunch of sites that link to each other. And if at least one person discovers a neat website because of posts like these, then it’s mission accomplished. 🫡</p>
<p>See you next month!</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/opengraph/cool-links/Q29vbCUyMExpbmtzJTIwVm9sLiUyMDEy/MjAyNS0wNi0zMFQwMCUzQTAwJTNBMDAuMDAwWg==/image.png"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/opengraph/cool-links/Q29vbCUyMExpbmtzJTIwVm9sLiUyMDEy/MjAyNS0wNi0zMFQwMCUzQTAwJTNBMDAuMDAwWg==/image.png"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/quick-reviews/jojo-rabbit</guid>
      <title>Quick Review: Jojo Rabbit</title>
      <link>https://fantinel.dev/quick-reviews/jojo-rabbit</link>
      <pubDate>Sun, 29 Jun 2025 12:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>Jojo Rabbit <br> 2019, Taika Waititi</p>
        <p>My rating: Loved it!</p>
        <p><img src="https://m.media-amazon.com/images/M/MV5BYmFmNmUyMjYtZTFjNS00OWQyLThhZmMtMGZkYTQ3YjY0ZDQ1XkEyXkFqcGc@._V1_SX300.jpg" /></p>
        
        <p>A beautiful movie about innocence during a time that wanted nothing but to prey on it. I’d love to say it’s all in the past.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://m.media-amazon.com/images/M/MV5BYmFmNmUyMjYtZTFjNS00OWQyLThhZmMtMGZkYTQ3YjY0ZDQ1XkEyXkFqcGc@._V1_SX300.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://m.media-amazon.com/images/M/MV5BYmFmNmUyMjYtZTFjNS00OWQyLThhZmMtMGZkYTQ3YjY0ZDQ1XkEyXkFqcGc@._V1_SX300.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/blog/website-v5</guid>
      <title>Fantinel.dev v5 is here!</title>
      <description>Out with the green waves, in with the rainbow of pastel colors!</description>
      <link>https://fantinel.dev/blog/website-v5</link>
      <pubDate>Sat, 28 Jun 2025 00:00:00 +0000</pubDate>
      <category>Meta</category><category>Svelte</category><category>Front-End</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/website-v5">
              read on the site!
            </a>
          </strong>
        </div>

        <p>If you’re reading this and you’re not from a distant future, this means that you are right now on version 5 of fantinel.dev!</p>
<p>Over a year ago, after posting about <a href="https://fantinel.dev/blog/5-year-blog-anniversary">my blog’s 5th anniversary</a>, I <a href="https://fantinel.dev/blog/thinking-of-a-redesign/">posted about my wish to redesign it</a>. First of all: time flies! I hadn’t realized it was over a year ago. Secondly, it’s finally here! That means it’s been a year of me working on the new design, building it, refining it, and above all, procrastinating!</p>
<h2 id="how-i-got-here">How I got here</h2>
<p>Of course, I didn’t actually work on it for a year. It’s been a very on-and-off process, mostly off. A lot of things happened during this year and I got busy with work, travel and prepping for a big move (that still didn’t happen). A lot of the free time I had also went into other things I like - mostly videogames. That’s fine! I never want any personal project to be anything more than a hobby. So I don’t feel bad at all for how long it took.</p>
<p>So the biggest chunks of my work on the new website were in short burst of hyperfocusing on it. Focusing can be my superpower quite often. For one or two weeks at a time, I’d wake up thinking about it, have breakfast thinking about it, work thinking about it, and then finally <em>work on it</em> while thinking about it, usually during the evenings. I don’t recall any dreams about the website but I wouldn’t be surprised if that happened.</p>
<h3 id="design">Design</h3>
<p>I think I started working on the design itself right after that blog post, at the end of March/beginning of April, 2024. I started out with something much more similar to what I had before. My initial plan was to use Figma to rebuild what I had, then start experimenting inside the tool.</p>
<p><img src="https://fantinel.dev/cms/media/articles/redesign-figma-limbo.png" alt="Screenshot of a Figma artboard with a design similar to the previous version of the website" title="Figma artboard of my initial work. Aside from a few tweaks here and there, this looked pretty much the same as it had looked since 2021."></p>
<p>However, after working on this for a bit, I reflected on what I said on that redesign post:</p>
<blockquote>
<p>I want my personal website to reflect a bit of who I am. Am I the same person I was 3-4 years ago?</p>
</blockquote>
<p>And the answer is: <em>not really</em>. While I’ve not completely changed as a person, I like to think I’ve improved quite a bit since then. Some tastes changed, a lot of opinions evolved, the hair got whiter, and the back pains are now an almost daily visitor. And more importantly, I got bored of the old design. It was fun for a while but I eventually got tired and felt restricted by it.</p>
<p>As I became an even bigger fan of pastel colors since then, I decided I’d take the colors of my favorite code theme, <a href="https://catppuccin.com/">Catppuccin</a>, and take them for a spin on my new design. I liked it so much that I just decided to use that color palette for everything!</p>
<p>The previous design was built with light mode in mind and then adapted to dark mode. However, I like the dark Catppuccin palette so much that this time I did the opposite: it was designed entirely in dark mode and I only got to work on the light mode after I already started building it. I still want to respect the user’s theme preference above everything, so I don’t default to the dark mode.</p>
<p>Overall, that worked, but to me it’s clear that the dark mode looks much better than the light one. To me, at least. But I’m continuing to tweak light mode here and there whenever I see things I can improve.</p>
<p>I decided to start with just the basics: the home page, the blog archive and the blog post pages. Once I got those to a place I liked, the rest would come naturally.</p>
<p><img src="https://fantinel.dev/cms/media/articles/redesign-figma-overview.png" alt="Screenshot of the Figma artboard showing a bunch of designs of the current site. They have the dark color palette I use now"></p>
<h3 id="development">Development</h3>
<p>Once I was satisfied with the design, I let it simmer in my mind for a few months before I started building anything. After coming back to the new design a few months later, I still liked what I saw. Good sign! So it was time to start building it.</p>
<p>However… I’m a web developer. Not just that, web development is <em>my hobby</em> too. So I couldn’t simply implement the new design in the existing project. The horror! Why would I keep using the code that was working well, decently organized and that would save me time?</p>
<p>That’s why @@I rebuilt it from scratch!@@</p>
<h4 id="astro">Astro</h4>
<p>I had been eyeing <a href="https://astro.build">Astro</a> for a while, and decided it was worth a shot to at least experiment. I could still reuse the code for my components, since Astro is fully compatible with Svelte. I’d just have to rework the part that did the routing, endpoints, build and data handling. Good thing that was really easy! Astro works similarly to SvelteKit (what I used before) and other popular ones like NextJS. So if you’ve worked with any of them, most of the principles stay the same. Just need a few implementation tweaks here and there.</p>
<p>You see, there was nothing wrong with SvelteKit, which I used before. It is very easy to understand, does a lot of things automatically and builds a really performant website. The thing that drew me to Astro is that it shares a lot of the same principles, but is <em>framework-agnostic</em>. That means I could bring over my Svelte components, which is great, but also that the knowledge I get from using it might be useful if I ever need to build a website that uses React, Vue or, I don’t know, even PHP components. Astro doesn’t care.</p>
<p>Something I noticed while developing is that I imported my Theme Toggle, a Svelte component that uses JavaScript to toggle the current theme into a page. It rendered correctly, but clicking on it wasn’t doing anything.</p>
<p>Turns out, ==Astro does not ship any JavaScript by default==, even from your JS framework components! That is an <em>amazing</em> default behavior, as it encourages <a href="https://fantinel.dev/blog/progressive-enhancement/">progressive enhancement</a>, decreases the size of the website (have you ever seen those 10MB+ NextJS landing pages???) and is just overall a good development pattern. If I really <em>need</em> JavaScript for this component, I have to explicitly <a href="https://docs.astro.build/en/guides/framework-components/#hydrating-interactive-components">add the <code>client</code> directive</a> to it.</p>
<p>In my case, that theme toggle does actually need JS to work. Which is why it hides itself if JS is not available, only showing up if it has what it needs to work. But some components don’t need JS, they’re just better with JS. The Table of Contents in this blog post is dynamic. As you scroll down it will highlight the current chapter you’re in, and if you’re on mobile it will stick to the bottom of the screen and do its best to get out of your way.</p>
<p>However, at the end of the day it’s still just a table of contents, a collection of anchor links that link to sections of this page. If JS is not available, it shouldn’t break, it shouldn’t hide. It just doesn’t do the dynamic stuff, but it’s still useful by letting you click on it. This is the kind of thought that I always have in mind when building something, and that Astro seems to encourage.</p>
<h4 id="svelte-5">Svelte 5</h4>
<p>I’ve used Svelte since v3 and loved it. It just made a lot of sense to me, I always vibed with its goals and principles, the performance gains it offered were awesome, and it was always a breath of fresh air to work with it. My previous site was using Svelte 4, which was a refinement over Svelte 3. I loved it.</p>
<p>On this rebuild, I naturally upgraded to Svelte 5, which came out recently. Svelte 5 is a big update, bringing some syntax changes, performance gains, and big things under the hood.</p>
<p>… I’m not sure I like it.</p>
<p>You see, to me what was magical about Svelte is that its syntax was just some sugar on top of JS and HTML. Add a <code>$</code> character here and there for reactivity, a <code>{#each}</code> over there for looping over a list of items, and you’re done. Svelte 5 still supports that syntax and it seems to me they will keep supporting it for a while, but they’ve introduced a new syntax in the form of “Runes”. I wanted to give the new stuff I try so I built everything with the new syntax.</p>
<p>After the initial learning curve I got used to it so it’s not a big deal anymore, but the fact that there was a learning curve was a letdown. I still like it better than working with other frameworks, though.</p>
<p>Some notes I took during development:</p>
<ul>
<li>The runes stuff in Svelte 5 takes a while getting used to;</li>
<li>Dealing with slots is way more complicated now; Have to use “snippets” instead, which might be more flexible but it’s way harder to understand. I really miss <code>&#x3C;slot>s</code> 😭;</li>
<li>The new <code>$props</code> syntax is way more intuitive than the previous <code>export let</code> one, although more verbose;</li>
<li>Feels like Svelte 3-4 were <em>sprinkles on top of the web</em>, while Svelte 5 is a framework like others are.</li>
</ul>
<h4 id="storybook">Storybook</h4>
<p>I used to use <a href="https://histoire.dev/">Histoire</a> for developing components in isolation, but it doesn’t seem they support Svelte 5 and development is quite slow for my taste. So, begrudgingly, I started using Storybook for that.</p>
<p>I <em>always</em> have issues setting up Storybook, and it was no different this time. I <a href="https://fantinel.dev/blog/storybook-astro-svelte">wrote up an article</a> about my journey to getting it to work.</p>
<h2 id="whats-next">What’s next</h2>
<p>So, with the redesign now live, what’s next for this website? Hopefully a lot, actually!</p>
<p>You might notice there’s a big “Under construction”  banner on the home page, and for good reason: I’m not even close to finishing everything I want to do with it. Honestly, I never will. This is, after all, my <em>playground</em> to experiment with things and do whatever I feel like.</p>
<p>If you want some spoilers, here’s what I think I’ll work on next:</p>
<ul>
<li>Quick Reviews: I’ve been writing small reviews for movies and TV shows I’ve watched for a while now, and I want to set up a section on the website just for those;</li>
<li>A /now page: kind of a trendy thing among bloggers, the idea is a page that shows the things I’m doing currently, like what I’m working on, games I’m playing, music I’m listening, etc.</li>
<li>A /uses page: a centralized version of my yearly <a href="https://fantinel.dev/blog/default-apps-2024/">Default Apps posts</a>, possibly including hardware as well.</li>
</ul>
<p>To be honest, I’ll probably come up with something else before I’m done with those three. Who knows?</p>
<h2 id="wrapping-up">Wrapping up</h2>
<p>Thanks for reading it all the way down here! If you’re interested in seeing the code to see how everything works, everything is open source and <a href="https://github.com/matfantinel/fantinel.dev">available on GitHub</a>.</p>
<p><img src="https://fantinel.dev/cms/media/articles/friendship-ended.png" alt="Meme with text: Friendship ended with GREEN WAVES now PASTEL RAINBOW is my best friend"></p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/cms/media/articles/website-v5-cover.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/cms/media/articles/website-v5-cover.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/quick-reviews/the-last-of-us-season-2</guid>
      <title>Quick Review: The Last of Us (Season 2)</title>
      <link>https://fantinel.dev/quick-reviews/the-last-of-us-season-2</link>
      <pubDate>Thu, 26 Jun 2025 12:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>The Last of Us (Season 2) <br> 2025, Craig Mazin</p>
        <p>My rating: Decent</p>
        <p><img src="https://m.media-amazon.com/images/M/MV5BYWI3ODJlMzktY2U5NC00ZjdlLWE1MGItNWQxZDk3NWNjN2RhXkEyXkFqcGc@._V1_SX300.jpg" /></p>
        
        <p>Breaks my heart because I love the game, but this adaptation just didn’t do it for me. Season 1 was pretty good, but I feel like none of the changes on Season 2 worked. Ellie is just a completely different character, and I feel like none of her actions are justified in the show. I gotta say some of the heaviest moments hit really hard on the show too. Episodes 2 and 6 were standouts though. Really enjoyed those.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://m.media-amazon.com/images/M/MV5BYWI3ODJlMzktY2U5NC00ZjdlLWE1MGItNWQxZDk3NWNjN2RhXkEyXkFqcGc@._V1_SX300.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://m.media-amazon.com/images/M/MV5BYWI3ODJlMzktY2U5NC00ZjdlLWE1MGItNWQxZDk3NWNjN2RhXkEyXkFqcGc@._V1_SX300.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/quick-reviews/the-taking-of-deborah-logan</guid>
      <title>Quick Review: The Taking of Deborah Logan</title>
      <link>https://fantinel.dev/quick-reviews/the-taking-of-deborah-logan</link>
      <pubDate>Sun, 15 Jun 2025 12:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>The Taking of Deborah Logan <br> 2014, Adam Robitel</p>
        <p>My rating: Loved it!</p>
        <p><img src="https://m.media-amazon.com/images/M/MV5BNjhhNTM1OGUtYTRkZC00Mzg4LWFiY2ItYTUwMDg3NDIxYjExXkEyXkFqcGc@._V1_SX300.jpg" /></p>
        
        <p>Holy crap, now this is a good horror movie. A slow burn that really pays off. Its found footage structure makes perfect sense in the story and also reflects a lot on what you think and feel during the entire movie. It finds the perfect balance of explaining things enough to be satisfying but not enough to break the horror aspect (something that most movies just suck at).</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://m.media-amazon.com/images/M/MV5BNjhhNTM1OGUtYTRkZC00Mzg4LWFiY2ItYTUwMDg3NDIxYjExXkEyXkFqcGc@._V1_SX300.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://m.media-amazon.com/images/M/MV5BNjhhNTM1OGUtYTRkZC00Mzg4LWFiY2ItYTUwMDg3NDIxYjExXkEyXkFqcGc@._V1_SX300.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/quick-reviews/spirited-away</guid>
      <title>Quick Review: Spirited Away</title>
      <link>https://fantinel.dev/quick-reviews/spirited-away</link>
      <pubDate>Sat, 14 Jun 2025 12:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>Spirited Away <br> 2001, Hayao Miyazaki</p>
        <p>My rating: Didn't like it</p>
        <p><img src="https://m.media-amazon.com/images/M/MV5BNTEyNmEwOWUtYzkyOC00ZTQ4LTllZmUtMjk0Y2YwOGUzYjRiXkEyXkFqcGc@._V1_SX300.jpg" /></p>
        
        <p>Okay, first of all I think this is an amazing movie, it looks and sounds amazing, all the characters are extremely creative, it’s a beautiful coming-of-age story and it’s a major pop culture icon. But watching it felt like a bit of a chore, I can’t really explain why. Good thing the ratings here are about my enjoyment of the movies, not their “objective” quality 😅</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://m.media-amazon.com/images/M/MV5BNTEyNmEwOWUtYzkyOC00ZTQ4LTllZmUtMjk0Y2YwOGUzYjRiXkEyXkFqcGc@._V1_SX300.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://m.media-amazon.com/images/M/MV5BNTEyNmEwOWUtYzkyOC00ZTQ4LTllZmUtMjk0Y2YwOGUzYjRiXkEyXkFqcGc@._V1_SX300.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/cool-links-may-2025</guid>
      <title>Cool Links Vol. 11: May, 2025</title>
      <description>Links to the best stuff I&apos;ve read or watched during the month of May, 2025</description>
      <link>https://fantinel.dev/blog/cool-links-may-2025</link>
      <pubDate>Sat, 31 May 2025 12:00:00 +0000</pubDate>
      <category>Cool Links</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/cool-links-may-2025">
              read on the site!
            </a>
          </strong>
        </div>

        <p>Hey there!</p>
<p>Winter has started to settle in where I live, and my hands are already freezing all the time. The cold and humid weather means more time inside, which means more links!</p>
<h2 id="fun">Fun</h2>
<p><strong><a href="https://idonthavespotify.donado.co/">I Don’t Have Spotify</a></strong></p>
<p>Sharing links to albums or songs in streaming apps sucks because not everyone uses the same one. This neat website takes in a link (although named after Spotify, it works with others as well) and spits out the link for the same music on whatever other platform you want.</p>
<p>Great for when you share a link with others too!</p>
<p><strong><a href="https://pong-wars.koenvangilst.nl/">Pong Wars</a>, by Koen van Gilst</strong></p>
<p>This is a hypnotizing endless battle between two squares, pong-style. All built with JS, HTML and CSS! The source code surprisingly simple and a fun read too, if you’re into that sort of thing.</p>
<h2 id="tech">Tech</h2>
<p><strong><a href="https://adblock.turtlecute.org/">Test Ad Block - Toolz</a></strong></p>
<p>If you use any adblockers (you should!), this website is a neat way to test how effective your setup is. The higher, the better, but if you get a <em>green</em> result you should already feel safe browsing online. I got 98% with my <a href="https://nextdns.io">NextDNS</a> + UBlock Origin combo!</p>
<p><strong><a href="https://kagi.com/stats?stat=leaderboard">Kagi Search Stats</a></strong></p>
<p>Kagi has been my search engine of choice for over a year now, and I just found out about their public stats page. Pretty cool they have this info open out there!</p>
<p>The most interesting part is the Domain Insights, that ranks the domains that get most blocked/prioritized on Kagi (Kagi allows you to prioritize results from specific domains, or simply block some altogether). It seems people really hate getting Pinterest results 😅</p>
<p><strong><a href="https://www.stefanjudis.com/blog/the-internet-archive-opt-out-itch/">The Internet Archive opt out itch</a>, by Stefan Judis</strong></p>
<p>In this article, Stefan ponders the ethics of the Internet Archive’s opt-out behavior. The work they do is really good for the web in general — but, on an individual level, it kinda sucks that someone is archiving your website without asking?</p>
<p>He also raises the point that while you can ask for your website to be excluded from being archived, doing so might make you (or your company) look shady and untrustworthy. Like, <em>what are you trying to hide so much</em>?</p>
<p><strong><a href="https://vitonsky.net/blog/2025/05/17/language-detection/">Don’t Guess My Language</a>, by Robert Vitonsky</strong></p>
<p>As a bilingual person that’s soon moving to another country I really struggle with websites and apps that keep trying to serve you content in a specific language instead of the one you choose. Google is one of the worst in this regard.</p>
<p>Even worse is when the content is translated automatically. It sucks! Google <em>again</em> sucks at this. It keeps reverting things to Portuguese and even re-enabling automatic dubbing (gross, I know) on videos, even though I explicitly have my device, browser, and Google account set to English.</p>
<p><strong><a href="https://daverupert.com/2025/05/notion-to-obsidian/">Moving from Notion to Obsidian</a>, by Dave Rupert</strong></p>
<p>I love Obsidian, and have been using it for over a year for taking notes about everything. The thing about his kind of app though is that you’re always looking for ways to tweak and improve your system. This article is great at explaining how Dave uses Obsidian for himself and as usual has a list of neat plugins.</p>
<p>One day, maybe, I’ll write my own post about how I use it. I’m just not confident enough on my system yet, probably…</p>
<h2 id="dev">Dev</h2>
<p><strong><a href="https://seanvoisen.com/blog/programming-is-a-feeling-ai-is-changing-it/">Programming is a feeling, and AI is changing it</a>, by Sean Voisen</strong></p>
<blockquote>
<p>Programming is an activity, but it’s also a feeling. For those of us who actually enjoy programming, there is a deep satisfaction that comes from solving problems through well-written code, a kind of ineffable joy found in the elegant expression of a system through our favorite syntax. It is akin to the same satisfaction a craftsperson might find at the end of the day after toiling away on well-made piece of furniture, the culmination of small dopamine hits that come from sweating the details on something and getting them just right. Maybe nobody will notice those details, but it doesn’t matter. We care, we notice, we get joy from the aesthetics of the craft.</p>
</blockquote>
<h2 id="ai--llms--slightly-depressing">AI / LLMs / Slightly Depressing</h2>
<p><strong><a href="https://dansinker.com/posts/2025-05-23-who-cares/">The Who Cares Era</a>, by Dan Sinker</strong></p>
<blockquote>
<p>If you don’t care, it’s miraculous.</p>
</blockquote>
<p>I’ve had this talk with my wife a few times already. Around us, it just feels that nobody cares about anything. Everything is hastily produced so it can be ignored by other people. It’s just disheartening to be the only ones noticing AI slop everywhere and see people not only believing it’s real, but also <em>not really caring if it’s real or not</em>.</p>
<p>This article also reminded of this one that I posted back in December: <a href="https://stevenscrawls.com/care-doesnt-scale/">Care Doesn’t Scale</a>.</p>
<blockquote>
<p>In a moment where machines churn out mediocrity, make something yourself. Make it imperfect. Make it rough. Just make it.</p>
</blockquote>
<p><strong><a href="https://whitep4nth3r.com/blog/the-promise-that-wasnt-kept/">The promise that wasn’t kept</a>, by Salma Alam-Naylor</strong></p>
<p>Fantastic piece that highlights how much of a distraction AI has become to creating value, simply because everyone is too focused on the tools and not on the work.</p>
<blockquote>
<p>But we can’t rely on tools as a shortcut to gain valuable experience. Experience takes time to develop, and your tools are only as good as your fundamental knowledge and skills. If you skip the knowledge and skills part, and if you fail to learn about what you’re doing and the implications of how you’re doing it and the human value you have the potential to deliver, then you have little hope of building human value into your software.</p>
</blockquote>
<p><strong><a href="https://productpicnic.beehiiv.com/p/the-everything-app-is-a-symptom-of-nothing-management-part-1">The Everything App is a symptom of Nothing Management (part 1)</a></strong></p>
<p>This is a spot on overview of how pretty much every tech company now has no clear direction besides making more money. No vision, no goals, no passion, except for making the number go up.</p>
<p>Yea, every company needs to make money because workers need money to survive, but when a system only ever rewards those that seek money above everything else, that system has failed and will continue to fail unless a big shift happens.</p>
<p>The passionate, skilled, full-of-ideas people that could solve real problems and/or improve the lives of others have been crushed by the weight of big companies looking for one more way to exploit you.</p>
<h2 id="wrapping-up">Wrapping up</h2>
<p>Thanks for tuning in, and see you next month!</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/opengraph/cool-links/Q29vbCUyMExpbmtzJTIwVm9sLiUyMDEx/MjAyNS0wNS0zMVQxMiUzQTAwJTNBMDAuMDAwWg==/image.png"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/opengraph/cool-links/Q29vbCUyMExpbmtzJTIwVm9sLiUyMDEx/MjAyNS0wNS0zMVQxMiUzQTAwJTNBMDAuMDAwWg==/image.png"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/quick-reviews/the-ugly-stepsister</guid>
      <title>Quick Review: The Ugly Stepsister</title>
      <link>https://fantinel.dev/quick-reviews/the-ugly-stepsister</link>
      <pubDate>Sat, 24 May 2025 12:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>The Ugly Stepsister <br> 2025, Emilie Blichfeldt</p>
        <p>My rating: I like it</p>
        <p><img src="https://m.media-amazon.com/images/M/MV5BYzk2MjdkZDMtNmU0ZC00ZTEyLTk0ZTAtZDM1MmFhNDdhMjFiXkEyXkFqcGc@._V1_SX300.jpg" /></p>
        
        <p>Pretty solid body horror movie. Some real memorable scenes with really good acting, with a solid story that builds on top of the classic Cinderella. Can’t go wrong with this one!</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://m.media-amazon.com/images/M/MV5BYzk2MjdkZDMtNmU0ZC00ZTEyLTk0ZTAtZDM1MmFhNDdhMjFiXkEyXkFqcGc@._V1_SX300.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://m.media-amazon.com/images/M/MV5BYzk2MjdkZDMtNmU0ZC00ZTEyLTk0ZTAtZDM1MmFhNDdhMjFiXkEyXkFqcGc@._V1_SX300.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/quick-reviews/flow</guid>
      <title>Quick Review: Flow</title>
      <link>https://fantinel.dev/quick-reviews/flow</link>
      <pubDate>Fri, 23 May 2025 12:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>Flow <br> 2024, Gints Zilbalodis</p>
        <p>My rating: Loved it!</p>
        <p><img src="https://m.media-amazon.com/images/M/MV5BOTM5ODBlOTAtYjcwZi00YzkzLWIzODEtMTM2MTZlNDFmMWU2XkEyXkFqcGc@._V1_SX300.jpg" /></p>
        
        <p>What an incredibly beautiful movie 🥺</p>
<p>Without any dialogue, it was able to make me connect to every character much more than most movies do. Cute and heartwarming, it totally deserved the Oscar for best animation it got!</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://m.media-amazon.com/images/M/MV5BOTM5ODBlOTAtYjcwZi00YzkzLWIzODEtMTM2MTZlNDFmMWU2XkEyXkFqcGc@._V1_SX300.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://m.media-amazon.com/images/M/MV5BOTM5ODBlOTAtYjcwZi00YzkzLWIzODEtMTM2MTZlNDFmMWU2XkEyXkFqcGc@._V1_SX300.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/quick-reviews/schindlers-list</guid>
      <title>Quick Review: Schindler&apos;s List</title>
      <link>https://fantinel.dev/quick-reviews/schindlers-list</link>
      <pubDate>Sat, 17 May 2025 12:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>Schindler's List <br> 1993, Steven Spielberg</p>
        <p>My rating: Loved it!</p>
        <p><img src="https://m.media-amazon.com/images/M/MV5BNjM1ZDQxYWUtMzQyZS00MTE1LWJmZGYtNGUyNTdlYjM3ZmVmXkEyXkFqcGc@._V1_SX300.jpg" /></p>
        
        <p>A real punch in the gut and a must-watch. Not much more that can be said.</p>
<p>Fuck nazis.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://m.media-amazon.com/images/M/MV5BNjM1ZDQxYWUtMzQyZS00MTE1LWJmZGYtNGUyNTdlYjM3ZmVmXkEyXkFqcGc@._V1_SX300.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://m.media-amazon.com/images/M/MV5BNjM1ZDQxYWUtMzQyZS00MTE1LWJmZGYtNGUyNTdlYjM3ZmVmXkEyXkFqcGc@._V1_SX300.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/cool-links-april-2025</guid>
      <title>Cool Links Vol. 10: April, 2025</title>
      <description>Links to the best stuff I&apos;ve read or watched during the month of April, 2025</description>
      <link>https://fantinel.dev/blog/cool-links-april-2025</link>
      <pubDate>Wed, 30 Apr 2025 12:00:00 +0000</pubDate>
      <category>Cool Links</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/cool-links-april-2025">
              read on the site!
            </a>
          </strong>
        </div>

        <p>Hey there! We’re a quarter of the way through 2025 already, can you believe it? This weekend (May 3rd) I’ll be in Rio de Janeiro <em>trying</em> to watch a free Lady Gaga concert in Copacabana beach. Fingers crossed I can actually see anything 🤞</p>
<p>For the Cool Links this month, we have a couple of “fun” ones, some web-dev related that were on my Read Later queue for a while, and some nuggets of wisdom on chaotic times.</p>
<p>Enjoy the reads!</p>
<h2 id="fun">Fun</h2>
<p><strong><a href="https://hypertext.tv/">Hypertext TV</a></strong></p>
<p>This website simulates a TV schedule (with CRT-style filters!) with multiple channels, each featuring an interesting indie website. The programming changes often (just like shows on a tv channel), so it’s an interesting site to keep on your bookmarks and visit a few times a day.</p>
<p><strong><a href="https://posthuman.blog/this-reddit-post-fried-my-brain/">A Reddit Bot Drove Me Insane</a></strong></p>
<p>The author here worries that so many people on Reddit are interacting with posters that are nothing more than robots, without any idea of that being the case. Even worse, some people are aware of that, but don’t care.</p>
<p>I saw a comment in a <a href="https://manualdousuario.net/orbita-post/en-um-robo-no-reddit-me-deixou-maluco">brazilian forum</a> that deeply resonated: “Maybe the biggest pain this realization causes is that, deep down, almost nobody cares about anything. We’re the ones who are wrong for searching for meaning in environments dominated by chaos”.</p>
<h2 id="dev">Dev</h2>
<p><strong><a href="https://www.debugbear.com/blog/content-visibility-api">Faster Rendering with the content-visibility CSS Property</a>, by Umar Hansa</strong></p>
<p>It’s like image lazy loading, but for page elements! I’ve got to try this out sometime and measure the effectiveness of this technique. Depending on the results, this might end up as its own blog post ;)</p>
<p><strong><a href="https://inclusive-components.design/cards">Cards</a>, by Inclusive Components</strong></p>
<p>Amazing step-by-step explanation on building Card components, with a special focus on accessibility. I love this kind of articles that explain the thinking behind every step and every line of code!</p>
<p>Card elements are everywhere and we all do them a bit differently. I’ll pay much more attention to the things mentioned here to ensure they’re as accessible as possible.</p>
<p><strong><a href="https://www.404s.design/">404s — gallery of error 404 page designs</a></strong></p>
<p>This site collects all kinds of designs for 404 pages found in the wild. Pretty cool source for inspiration or to admire other people’s creativity!</p>
<h2 id="misc">Misc</h2>
<p><strong><a href="https://european-alternatives.eu/">European alternatives for digital products</a></strong></p>
<p>We rely too much on software companies nowadays, and most of the popular ones are USA-based. With the USA becoming increasingly less trustworthy on an almost daily basis, people have started gathering EU-based alternatives to the most popular services.</p>
<p>This is interesting even if you’re not based in Europe, as companies there are forced to respect your data and privacy by law.</p>
<p><strong><a href="https://www.theverge.com/social/639811/view-counts-tiktok-instagram-x-youtube-lies">On TikTok, YouTube, X, and everywhere, “views” are a meaningless number</a>, by David Pierce</strong></p>
<p>Great article pointing out that “Views” are an useless metric and that the platforms that count it have zero incentive to <em>not</em> lie about them.</p>
<h2 id="wrapping-up">Wrapping up</h2>
<p>Thanks for tuning in, and see you next month!</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/opengraph/cool-links/Q29vbCUyMExpbmtzJTIwVm9sLiUyMDEw/MjAyNS0wNC0zMFQxMiUzQTAwJTNBMDAuMDAwWg==/image.png"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/opengraph/cool-links/Q29vbCUyMExpbmtzJTIwVm9sLiUyMDEw/MjAyNS0wNC0zMFQxMiUzQTAwJTNBMDAuMDAwWg==/image.png"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/storybook-astro-svelte</guid>
      <title>Setting up Storybook on an Astro project</title>
      <description>I really, really thought this was gonna be easy.</description>
      <link>https://fantinel.dev/blog/storybook-astro-svelte</link>
      <pubDate>Sun, 27 Apr 2025 21:04:59 +0000</pubDate>
      <category>Front-End</category><category>Dev</category><category>Svelte</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/storybook-astro-svelte">
              read on the site!
            </a>
          </strong>
        </div>

        <p>I really, <em>really</em> thought this was gonna be easy. But with front-end development, are things ever simple?</p>
<p>I’ve dropped a few hints here and there that I’m redesigning my website and blog. I’m also rewriting it from scratch, with <a href="https://astro.build">Astro</a>! It’s a <em>meta-framework</em>, which means it takes care of the part that handles routing and data-fetching, but stays out of component and reactivity. It’s like NextJS or SvelteKit.</p>
<p>The main appeal of Astro, though, is that it is framework-agnostic. You can use it as the base and then build all your components with React, Svelte, Vue, Vanilla JS, or even JQuery if that makes sense for you. It’s one of the main appeals of Astro alongside active development, a clear project direction, and the whole <em>zero client-side JS by default</em> philosophy.</p>
<p>My soon-to-be-website uses Astro as its foundation, but keeps Svelte as the framework used for the components, to handle reactivity and just the overall template niceties. It also allows me to just port over existing components from my current SvelteKit website, and it’s plug-and-play on Astro. Sweet!</p>
<p>Previously, I’ve used <a href="https://histoire.dev/">Histoire</a>, a Vite-based simpler alternative to Storybook. It’s fast and really easy to set up, which was the main reason I used it. It’s not a fully-featured but I didn’t need all the extra features of Storybook anyway. The thing is, development on that one is quite slow and it apparently doesn’t work well with Svelte 5 yet. So, I figured I’d set up Storybook itself on my project. It’s just a simple collection of Svelte components, right? How hard could it be?</p>
<p><em>Significantly.</em></p>
<blockquote>
<p>[!info]
The post below was written from my experience getting frustrated with the lack of proper documentation and a lot of trial and error. I was only able to get things working after a few hours digging into Storybook (and its plugins)‘s documentation and source code, and a good deal of arguing with ChatGPT. I’m writing this guide so you don’t have to go through all the hoops I did.</p>
<p>Also, I’m using Svelte on my examples as it’s the framework I use, but I think the vast majority of this guide could apply to you if you use React, Vue, or none at all.</p>
</blockquote>
<h2 id="understanding-the-layers">Understanding the layers</h2>
<p>There are a lot of layers to the tech stack here. Astro uses Vite as a build tool to handle dependencies, dev servers, compiling, and whatever else it does. You might have heard of Webpack, which does the same. Vite is basically a newer, leaner version of that.</p>
<p>When you install Svelte (or React, or Vue) on an Astro project, you’re actually just installing the Vite integration for that specific framework. Astro doesn’t need to know what the client-side framework is because they’re pretty much separated.</p>
<p>When you use Storybook, you likely don’t want to interact with anything on the Astro side of things. You write stories for your components, and they will all be Svelte/React/Vue files that don’t need Astro to work. Which is why there’s no “Astro package” for Storybook, or any starting template.</p>
<p>Which means in this case, we want to setup Storybook for a Svelte + Vite project.</p>
<h2 id="step-by-step">Step-by-step</h2>
<p>So, with my Astro + Svelte project in hands, these are the first steps I did:</p>
<ol>
<li>Following the <a href="https://storybook.js.org/docs/get-started/frameworks/svelte-vite#writing-native-svelte-stories">official guide</a>, I ran <code>npm create storybook@latest</code></li>
<li>Selected “Documentation” as the main use</li>
<li>Even when selecting Documentation, Storybook still installed a lot of unit and visual testing packages that I did not want. So I ran the command below to get rid of all that bloat. You might want to double-check if you actually need one of these packages.
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="bash"><code><span class="line"><span style="color:#B392F0">npm</span><span style="color:#9ECBFF"> uninstall</span><span style="color:#9ECBFF"> @chromatic-com/storybook</span><span style="color:#9ECBFF"> @storybook/experimental-addon-test</span><span style="color:#9ECBFF"> @storybook/test</span><span style="color:#9ECBFF"> @vitest/browser</span><span style="color:#9ECBFF"> @vitest/coverage-v8</span><span style="color:#9ECBFF"> playwright</span><span style="color:#9ECBFF"> vitest</span></span></code></pre>
</li>
<li>Deleted the built-in “stories” folder that Storybook adds by default. You can keep this one if you want some pre-built examples.</li>
</ol>
<p>This looks like it should be it, right? But if you run <code>npm run storybook</code>, you’ll see that it gives out an error. The error on the browser is a bit generic but on the terminal you’ll see something like:</p>
<blockquote>
<p>Internal server error: Failed to parse source for import analysis because the content contains invalid JS syntax. If you are using JSX, make sure to name the file with the .jsx or .tsx extension.
Plugin: vite:import-analysis
File:(…)/node_modules/@storybook/svelte/dist/components/SlotDecorator.svelte</p>
</blockquote>
<p>Vite is trying to compile the Storybook code but is erroneously identifying a closing <code>&#x3C;/script></code> tag as JSX syntax, even though it’s a Svelte file. This hints to the fact that the Vite code that is running is not loading the plugin needed to make Svelte work (<code>vite-plugin-svelte</code>). Astro does that automatically, but Storybook does not, as it just assumes a Svelte + Vite project would have that plugin installed as a dependency. So, to fix it, I:</p>
<ol>
<li>Installed the plugin with <code>npm install --save-dev @sveltejs/vite-plugin-svelte</code></li>
<li>Added the plugin to the Vite config in my <code>.storybook/main.ts</code> file:</li>
</ol>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#F97583">~</span><span style="color:#E1E4E8">filename .storybook</span><span style="color:#F97583">/</span><span style="color:#E1E4E8">main.ts</span></span>
<span class="line"><span style="color:#F97583">import</span><span style="color:#F97583"> type</span><span style="color:#E1E4E8"> { StorybookConfig } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> '@storybook/svelte-vite'</span><span style="color:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> config</span><span style="color:#F97583">:</span><span style="color:#B392F0"> StorybookConfig</span><span style="color:#F97583"> =</span><span style="color:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#9ECBFF">	"stories"</span><span style="color:#E1E4E8">: [</span></span>
<span class="line"><span style="color:#9ECBFF">		"../src/**/*.stories.@(js|jsx|ts|tsx|svelte)"</span></span>
<span class="line"><span style="color:#E1E4E8">	],</span></span>
<span class="line"><span style="color:#9ECBFF">	"addons"</span><span style="color:#E1E4E8">: [</span></span>
<span class="line"><span style="color:#9ECBFF">		"@storybook/addon-essentials"</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#9ECBFF">		"@storybook/addon-svelte-csf"</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#E1E4E8">	],</span></span>
<span class="line"><span style="color:#9ECBFF">	"framework"</span><span style="color:#E1E4E8">: {</span></span>
<span class="line"><span style="color:#9ECBFF">		"name"</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">"@storybook/svelte-vite"</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#9ECBFF">		"options"</span><span style="color:#E1E4E8">: {}</span></span>
<span class="line"><span style="color:#E1E4E8">	},</span></span>
<span class="line"><span style="color:#9ECBFF">	"viteFinal"</span><span style="color:#E1E4E8">: </span><span style="color:#F97583">async</span><span style="color:#E1E4E8"> (</span><span style="color:#FFAB70">config</span><span style="color:#E1E4E8">) </span><span style="color:#F97583">=></span><span style="color:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#6A737D">		// Needs to be dynamically imported</span></span>
<span class="line"><span style="color:#F97583">		const</span><span style="color:#E1E4E8"> { </span><span style="color:#79B8FF">svelte</span><span style="color:#E1E4E8"> } </span><span style="color:#F97583">=</span><span style="color:#F97583"> await</span><span style="color:#F97583"> import</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">'@sveltejs/vite-plugin-svelte'</span><span style="color:#E1E4E8">);</span></span>
<span class="line"><span style="color:#E1E4E8">		</span></span>
<span class="line"><span style="color:#E1E4E8">		config.plugins </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> config.plugins </span><span style="color:#F97583">||</span><span style="color:#E1E4E8"> [];</span></span>
<span class="line"><span style="color:#E1E4E8">		config.plugins.</span><span style="color:#B392F0">push</span><span style="color:#E1E4E8">(</span></span>
<span class="line"><span style="color:#B392F0">			svelte</span><span style="color:#E1E4E8">({</span></span>
<span class="line"><span style="color:#E1E4E8">				exclude: [</span><span style="color:#9ECBFF">"node_modules/@storybook/**"</span><span style="color:#E1E4E8">],</span></span>
<span class="line"><span style="color:#E1E4E8">			})</span></span>
<span class="line"><span style="color:#E1E4E8">		);</span></span>
<span class="line"><span style="color:#E1E4E8">		</span></span>
<span class="line"><span style="color:#F97583">		return</span><span style="color:#E1E4E8"> config;</span></span>
<span class="line"><span style="color:#E1E4E8">	}</span></span>
<span class="line"><span style="color:#E1E4E8">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">export</span><span style="color:#F97583"> default</span><span style="color:#E1E4E8"> config;</span></span></code></pre>
<p>And that should be enough for your stories to work!</p>
<h2 id="example-story-syntax-with-svelte-5">Example Story syntax with Svelte 5</h2>
<p>If, like me, you’re struggling a bit to find a good example of the latest syntax for Svelte stories (the older still works though), I found some good ones <a href="https://github.com/storybookjs/addon-svelte-csf/tree/next/examples">in the addon-svelte-csf project on GitHub</a>.</p>
<h2 id="other-useful-things">Other useful things</h2>
<h3 id="load-your-global-css-styles-on-stories">Load your global CSS styles on stories</h3>
<p>If you have a global SCSS file that your components rely on, you can just add a line to <code>.storybook/preview.ts</code>:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#F97583">~</span><span style="color:#E1E4E8">filename .storybook</span><span style="color:#F97583">/</span><span style="color:#E1E4E8">preview.ts</span></span>
<span class="line"><span style="color:#F97583">import</span><span style="color:#9ECBFF"> '../src/styles/global.scss'</span><span style="color:#E1E4E8">; </span><span style="color:#6A737D">// Or whatever your file path is</span></span>
<span class="line"></span>
<span class="line"><span style="color:#E1E4E8">{</span><span style="color:#F97583">...</span><span style="color:#E1E4E8">}</span></span></code></pre>
<h3 id="use-tsconfig-aliases-in-stories">Use tsconfig aliases in Stories</h3>
<p>If you use aliases set up in tsconfig.json in your components, for example so you can use <code>@components/button</code> instead of <code>src/components/button</code>, you either need to re-configure them for Storybook, or you can reuse the ones from tsconfig instead. To do that, you’ll need:</p>
<ol>
<li>Install this package to allow Vite to understand tsconfig paths: <code>npm install --save-dev vite-tsconfig-paths</code></li>
<li>Add this to your Storybook’s main.ts file:</li>
</ol>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#F97583">~</span><span style="color:#E1E4E8">filename .storybook</span><span style="color:#F97583">/</span><span style="color:#E1E4E8">main.ts</span></span>
<span class="line"><span style="color:#F97583">import</span><span style="color:#F97583"> type</span><span style="color:#E1E4E8"> { StorybookConfig } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> '@storybook/svelte-vite'</span><span style="color:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> config</span><span style="color:#F97583">:</span><span style="color:#B392F0"> StorybookConfig</span><span style="color:#F97583"> =</span><span style="color:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#E1E4E8">	{</span><span style="color:#F97583">...</span><span style="color:#E1E4E8">}</span></span>
<span class="line"><span style="color:#9ECBFF">	"viteFinal"</span><span style="color:#E1E4E8">: </span><span style="color:#F97583">async</span><span style="color:#E1E4E8"> (</span><span style="color:#FFAB70">config</span><span style="color:#E1E4E8">) </span><span style="color:#F97583">=></span><span style="color:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#6A737D">		// Needs to be dynamically imported</span></span>
<span class="line"><span style="color:#F97583">		const</span><span style="color:#79B8FF"> tsconfigPaths</span><span style="color:#F97583"> =</span><span style="color:#F97583"> await</span><span style="color:#F97583"> import</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">'vite-tsconfig-paths'</span><span style="color:#E1E4E8">).</span><span style="color:#B392F0">then</span><span style="color:#E1E4E8">(</span><span style="color:#FFAB70">m</span><span style="color:#F97583"> =></span><span style="color:#E1E4E8"> m.default);</span></span>
<span class="line"><span style="color:#E1E4E8">		{</span><span style="color:#F97583">...</span><span style="color:#E1E4E8">}</span></span>
<span class="line"><span style="color:#E1E4E8">		config.plugins.</span><span style="color:#B392F0">push</span><span style="color:#E1E4E8">(</span></span>
<span class="line"><span style="color:#E1E4E8">			{</span><span style="color:#F97583">...</span><span style="color:#E1E4E8">}</span></span>
<span class="line"><span style="color:#B392F0">			tsconfigPaths</span><span style="color:#E1E4E8">({</span></span>
<span class="line"><span style="color:#E1E4E8">				projects: [</span><span style="color:#9ECBFF">'./tsconfig.json'</span><span style="color:#E1E4E8">],</span></span>
<span class="line"><span style="color:#E1E4E8">			})</span></span>
<span class="line"><span style="color:#E1E4E8">		);</span></span>
<span class="line"><span style="color:#E1E4E8">		</span></span>
<span class="line"><span style="color:#F97583">		return</span><span style="color:#E1E4E8"> config;</span></span>
<span class="line"><span style="color:#E1E4E8">	}</span></span>
<span class="line"><span style="color:#E1E4E8">	{</span><span style="color:#F97583">...</span><span style="color:#E1E4E8">}</span></span>
<span class="line"><span style="color:#E1E4E8">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">export</span><span style="color:#F97583"> default</span><span style="color:#E1E4E8"> config;</span></span></code></pre>
<h3 id="support-sass-aliases-on-storybook">Support SASS aliases on Storybook</h3>
<p>If your project uses aliases for SASS as well, you can alter the <code>viteFinal</code> section of your <code>.storybook/main.ts</code> file to make Vite resolve those aliases as well:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#F97583">~</span><span style="color:#E1E4E8">filename .storybook</span><span style="color:#F97583">/</span><span style="color:#E1E4E8">main.ts</span></span>
<span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> path </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> 'path'</span><span style="color:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#E1E4E8">{</span><span style="color:#F97583">...</span><span style="color:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#9ECBFF">"viteFinal"</span><span style="color:#E1E4E8">: </span><span style="color:#F97583">async</span><span style="color:#E1E4E8"> (</span><span style="color:#FFAB70">config</span><span style="color:#E1E4E8">) </span><span style="color:#F97583">=></span><span style="color:#E1E4E8"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#E1E4E8">	{</span><span style="color:#F97583">...</span><span style="color:#E1E4E8">}</span></span>
<span class="line"><span style="color:#E1E4E8">	</span></span>
<span class="line"><span style="color:#6A737D">	// Add alias for SCSS @styles</span></span>
<span class="line"><span style="color:#E1E4E8">	config.resolve </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> config.resolve </span><span style="color:#F97583">||</span><span style="color:#E1E4E8"> {};</span></span>
<span class="line"><span style="color:#F97583">	const</span><span style="color:#79B8FF"> stylesPath</span><span style="color:#F97583"> =</span><span style="color:#E1E4E8"> path.</span><span style="color:#B392F0">resolve</span><span style="color:#E1E4E8">(process.</span><span style="color:#B392F0">cwd</span><span style="color:#E1E4E8">(), </span><span style="color:#9ECBFF">'src/styles'</span><span style="color:#E1E4E8">);</span></span>
<span class="line"><span style="color:#F97583">	if</span><span style="color:#E1E4E8"> (Array.</span><span style="color:#B392F0">isArray</span><span style="color:#E1E4E8">(config.resolve.alias)) {</span></span>
<span class="line"><span style="color:#E1E4E8">		config.resolve.alias.</span><span style="color:#B392F0">push</span><span style="color:#E1E4E8">({</span></span>
<span class="line"><span style="color:#E1E4E8">		find: </span><span style="color:#9ECBFF">'@styles'</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#E1E4E8">		replacement: stylesPath</span></span>
<span class="line"><span style="color:#E1E4E8">		});</span></span>
<span class="line"><span style="color:#E1E4E8">	} </span><span style="color:#F97583">else</span><span style="color:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#E1E4E8">		config.resolve.alias </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#F97583">		...</span><span style="color:#E1E4E8">(config.resolve.alias </span><span style="color:#F97583">||</span><span style="color:#E1E4E8"> {}),</span></span>
<span class="line"><span style="color:#9ECBFF">		'@styles'</span><span style="color:#E1E4E8">: stylesPath</span></span>
<span class="line"><span style="color:#E1E4E8">	};</span></span>
<span class="line"><span style="color:#E1E4E8">}</span></span></code></pre>
<h2 id="wrapping-up">Wrapping up</h2>
<p>Looking back, it’s not really a lot of steps to get it working. But it was a journey to get to this point. Hopefully I’m saving you some time on this!</p>
<p>Both parts of the stack are actually working as they should - Astro does the website part and stays out of the rest, Vite builds the tools, Svelte handles the components, and Storybook renders them. It was just a lack of either clear documentation or better error messages that kept me in the dark for a while. Hopefully there’s more effort in improving this in the future!</p>
<p>I don’t have comments on my blog, but feel free to <a href="mailto:matt@fantinel.dev">shoot me an email</a> if you have questions or suggestions. I’ll be glad to help out (or be helped)!</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/cms/media/articles/storybook-astro-svelte.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/cms/media/articles/storybook-astro-svelte.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/quick-reviews/supernatural-season-7</guid>
      <title>Quick Review: Supernatural (Season 7)</title>
      <link>https://fantinel.dev/quick-reviews/supernatural-season-7</link>
      <pubDate>Sun, 20 Apr 2025 12:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>Supernatural (Season 7) <br> 2011, Sera Gamble</p>
        <p>My rating: Didn't like it</p>
        <p><img src="https://fantinel.dev/cms/media/quick-reviews/supernatural-season-7.jpg" /></p>
        
        <p>This one was a tough watch. After the good surprise that Season 6 was, this one never really stuck the landing. It gives up the plot that Season 6 sets in like 15 minutes and then just drags on for 23 episodes. I never really knew if I should take a scene seriously or if it was meant to be a comic relief - and to be honest, if feels like the show didn’t know either.</p>
<p>I gotta say the Bobby arc on the second half of the season was pretty good, though.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/cms/media/quick-reviews/supernatural-season-7.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/cms/media/quick-reviews/supernatural-season-7.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/quick-reviews/the-hobbit---the-battle-of-the-five-armies</guid>
      <title>Quick Review: The Hobbit - The Battle of the Five Armies</title>
      <link>https://fantinel.dev/quick-reviews/the-hobbit---the-battle-of-the-five-armies</link>
      <pubDate>Sun, 20 Apr 2025 12:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>The Hobbit - The Battle of the Five Armies <br> 2014, Peter Jackson</p>
        <p>My rating: I like it</p>
        <p><img src="https://m.media-amazon.com/images/M/MV5BMTYzNDE3OTQ3MF5BMl5BanBnXkFtZTgwODczMTg4MjE@._V1_SX300.jpg" /></p>
        
        <p>Has some pretty amazing moments and is overall very good, but just like the first one, it overdoes the action scenes, with one after another. So it ends up feeling bloated. Not enough to make it a bad movie, far from it! I never understood why the Hobbit movies are so underrated.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://m.media-amazon.com/images/M/MV5BMTYzNDE3OTQ3MF5BMl5BanBnXkFtZTgwODczMTg4MjE@._V1_SX300.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://m.media-amazon.com/images/M/MV5BMTYzNDE3OTQ3MF5BMl5BanBnXkFtZTgwODczMTg4MjE@._V1_SX300.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/quick-reviews/the-hobbit---the-desolation-of-smaug</guid>
      <title>Quick Review: The Hobbit - The Desolation of Smaug</title>
      <link>https://fantinel.dev/quick-reviews/the-hobbit---the-desolation-of-smaug</link>
      <pubDate>Sun, 13 Apr 2025 12:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>The Hobbit - The Desolation of Smaug <br> 2013, Peter Jackson</p>
        <p>My rating: Loved it!</p>
        <p><img src="https://m.media-amazon.com/images/M/MV5BMzU0NDY0NDEzNV5BMl5BanBnXkFtZTgwOTIxNDU1MDE@._V1_SX300.jpg" /></p>
        
        <p>The first time I watched this one back in 2013, I remember not enjoying it as much as I enjoyed the first. But now, I think it’s much better.</p>
<p>I think it could be a bit goofier overall, but I pretty much enjoyed everything in this rewatch (except for the out-of-place romance). Smaug’s scenes are simply amazing, too.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://m.media-amazon.com/images/M/MV5BMzU0NDY0NDEzNV5BMl5BanBnXkFtZTgwOTIxNDU1MDE@._V1_SX300.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://m.media-amazon.com/images/M/MV5BMzU0NDY0NDEzNV5BMl5BanBnXkFtZTgwOTIxNDU1MDE@._V1_SX300.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/quick-reviews/severance-season-2</guid>
      <title>Quick Review: Severance (Season 2)</title>
      <link>https://fantinel.dev/quick-reviews/severance-season-2</link>
      <pubDate>Sat, 05 Apr 2025 12:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>Severance (Season 2) <br> 2025</p>
        <p>My rating: Decent</p>
        <p><img src="https://m.media-amazon.com/images/M/MV5BZDI5YzJhODQtMzQyNy00YWNmLWIxMjUtNDBjNjA5YWRjMzExXkEyXkFqcGc@._V1_SX300.jpg" /></p>
        
        <p>An overall disappointing season, but the ending was fantastic.</p>
<p>It feels like they had enough story for 4 episodes but were forced to make 10. So the good things are stretched out between a lot of plots that don’t really go anywhere (and that I honestly didn’t care about).</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://m.media-amazon.com/images/M/MV5BZDI5YzJhODQtMzQyNy00YWNmLWIxMjUtNDBjNjA5YWRjMzExXkEyXkFqcGc@._V1_SX300.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://m.media-amazon.com/images/M/MV5BZDI5YzJhODQtMzQyNy00YWNmLWIxMjUtNDBjNjA5YWRjMzExXkEyXkFqcGc@._V1_SX300.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/cool-links-march-2025</guid>
      <title>Cool Links Vol. 9: March, 2025</title>
      <description>Links to the best stuff I&apos;ve read or watched during the month of March, 2025</description>
      <link>https://fantinel.dev/blog/cool-links-march-2025</link>
      <pubDate>Mon, 31 Mar 2025 12:00:00 +0000</pubDate>
      <category>Cool Links</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/cool-links-march-2025">
              read on the site!
            </a>
          </strong>
        </div>

        <p>Hey there! Autumn is here in Brazil and the leaves have started to go yellow, which means it’s my favorite season of the year! Mainly because it’s neither too hot nor too cold, but also because it’s <em>very</em> pretty.</p>
<p>I’ve prepped a selection of links to read through while you drink your coffee, tea, or whatever’s your choice of beverage. The last one is a quite long video, but I highly recommend watching it!</p>
<h2 id="fun">Fun</h2>
<p><strong><a href="https://pippinbarr.com/it-is-as-if-you-were-on-your-phone/info/">It is as if you were on your phone</a></strong></p>
<blockquote>
<p>pretend to be on your phone so that you pass as human, but actually do essentially nothing instead</p>
</blockquote>
<p>Do you feel pressured to be on your phone all the time, so you can pass as a human? This neat web app allows you to do just that, but while doing absolutely nothing instead.</p>
<p><em>(honestly, it’s a better use of your phone than scrolling through social media…)</em></p>
<h2 id="dev">Dev</h2>
<p><strong><a href="https://developer.chrome.com/blog/a-customizable-select">The select element can now be customized with CSS</a> 🎉</strong></p>
<p>Customizing <code>&#x3C;select></code> elements is something every web developer has had to do, probably. And the thing about that is that… you really can’t customize it. Or couldn’t, until now.</p>
<p>Having to implement a custom look on this field was always, to me, the perfect definition of “reinventing the wheel”. You gotta pick up this element that works reliably, is accessible, natively supported by all browsers, and users have been using for 30+ years, and then… build it from scratch, with JavaScript (which already kills the accessibility for some people).</p>
<p>Now, as of Chromium 135, you can finally customize them as you always expected you could! This will probably take a while to get to Safari and Firefox, but here’s the cool thing: if those browsers don’t support this new thing, the <code>&#x3C;select></code> will just look like a normal field and work just as well. A perfect example of progressive enhancement!</p>
<p><strong><a href="https://ishadeed.com/article/css-relative-colors/">CSS Relative Colors</a>, by Ahmad Shadeed</strong></p>
<p>Ahmad’s blog has been featured here a few times already, and here’s another gem! A fully interactive, well-written and just a plain joy to read article explaining different strategies to handle colors in CSS, focusing on all those little color variations we need to handle when building something.</p>
<p><strong><a href="https://tonsky.me/blog/checkbox">In Loving Memory of Square Checkbox</a>, by Nikita Prokopov</strong></p>
<p>In times where software “needs” to stand out rather than be familiar, we lose our heroes. Rest in peace, square checkboxes!</p>
<p><strong><a href="https://www.npmjs.com/package/patch-package">patch-package on npm</a></strong></p>
<p>Sooo many times I’ve had to debug something in a npm package dependency of a project I’m working on, only to realize I need to change some of the code to make it work.</p>
<p>That’s usually a pain though, since you either have to open a pull request with a fix and wait for it to be merged, or setup your own fork of the package and host it somewhere.</p>
<p>This package aims to avoid that. It applies patches to other packages in your project, so you don’t have to go through the process of setting up a fork.</p>
<h2 id="tech">Tech</h2>
<p><strong><a href="https://www.macstories.net/stories/the-ipads-sweet-solution/">The iPad’s “Sweet” Solution</a>, by Federico Viticci</strong></p>
<p>Really nice article that pretty much sums up the iPad situation: it is interesting hardware but that has pretty much no software that showcases what it does best.</p>
<p>The best iPad apps are… web apps. And the iPad’s only available browser being Safari doesn’t make things better.</p>
<h2 id="deep-reads-or-watch-in-this-case">Deep Reads (or watch, in this case)</h2>
<p><strong><a href="https://youtube.com/watch?v=QEJpZjg8GuA">Algorithms are breaking how we think</a>, by Technology Connections</strong></p>
<p>This is an <em>incredibly well-articulated</em> rant about how recommendation algorithms are changing how our brains work. Automation is good for us and it’s everywhere, but what about when thinking, the very thing that makes us human, starts being automated?</p>
<p>Letting recommendation algorithms (that, as we all know, prioritize revenue) decide the information we get, the tone of that information, and the context of every social interaction is pretty much giving up on our autonomy.</p>
<p>The entire video is worth watching, but this part about <em>context collapse</em> was one of the most interesting bits. It makes perfect sense, but I had never thought of it this way:</p>
<blockquote>
<p>Algorithmic feeds on social media are unfortunately quite good at fostering something known as context collapse. To understand this, imagine you’re dining in a restaurant and you’re close enough to a table of people to hear snippets of their conversation. You don’t know who any of the people at that table are, but if you manage to overhear them talk about something you’re really interested in, you might feel tempted to join their conversation. But in the context of a restaurant setting, that’s considered very rude, so it rarely ever happens.</p>
<p>On social media, though, the same kinds of quasi-private conversations between parties who know each other are happening all the time, but since the platform is just one big space and it might decide to put that conversation in front of random people, that social boundary of etiquette which is normally respected is just not there. And lots of conflicts happen as a result.</p>
<p>A really common one you might accidentally step into on social media happens when you stumble across a conversation among friends making sarcastic jokes with each other, but since you don’t know who those people are, you don’t have the context you need to recognize they’re joking. And so, if you reply with a serious critique, well, that’s a social misfire which some will react poorly to.</p>
<p>And that’s a pretty mild form of context collapse.  It can be much, much worse when people want to discuss things like politics. And unless we realize recommendation algorithms are what’s fostering these reactionary conflicts, they’re going to continue so long as we use platforms in the ways that we do. It’s for all these reasons that I believe algorithmic complacency is creating a crisis of both curiosity and human connection.</p>
</blockquote>
<h2 id="wrapping-up">Wrapping up</h2>
<p>Thanks for tuning in another month, and I hope to see (write to? Be read by?) you again next month!</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/opengraph/cool-links/Q29vbCUyMExpbmtzJTIwVm9sLiUyMDk=/MjAyNS0wMy0zMVQxMiUzQTAwJTNBMDAuMDAwWg==/image.png"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/opengraph/cool-links/Q29vbCUyMExpbmtzJTIwVm9sLiUyMDk=/MjAyNS0wMy0zMVQxMiUzQTAwJTNBMDAuMDAwWg==/image.png"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/quick-reviews/the-hobbit---an-unexpected-journey</guid>
      <title>Quick Review: The Hobbit - An Unexpected Journey</title>
      <link>https://fantinel.dev/quick-reviews/the-hobbit---an-unexpected-journey</link>
      <pubDate>Sun, 30 Mar 2025 12:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>The Hobbit - An Unexpected Journey <br> 2012, Peter Jackson</p>
        <p>My rating: I like it</p>
        <p><img src="https://m.media-amazon.com/images/M/MV5BMTcwNTE4MTUxMl5BMl5BanBnXkFtZTcwMDIyODM4OA@@._V1_SX300.jpg" /></p>
        
        <p>Judge me, but I actually like The Hobbit movies. This is my first rewatch since it came out in 2012, and while this one has a few too many action scenes (which are fun, just get tiresome after 3 hours), it’s still a really good movie in an universe I love.</p>
<p>Sure, its quality never matches the Lord of the Rings trilogy, but to be honest I don’t think anything else ever will.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://m.media-amazon.com/images/M/MV5BMTcwNTE4MTUxMl5BMl5BanBnXkFtZTcwMDIyODM4OA@@._V1_SX300.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://m.media-amazon.com/images/M/MV5BMTcwNTE4MTUxMl5BMl5BanBnXkFtZTcwMDIyODM4OA@@._V1_SX300.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/quick-reviews/adolescence</guid>
      <title>Quick Review: Adolescence</title>
      <link>https://fantinel.dev/quick-reviews/adolescence</link>
      <pubDate>Fri, 21 Mar 2025 12:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>Adolescence <br> 2025</p>
        <p>My rating: I like it</p>
        <p><img src="https://m.media-amazon.com/images/M/MV5BNGY1YjBiNzMtYWZhNC00OWViLWE0MzItNjc4YzczOGNiM2I0XkEyXkFqcGc@._V1_SX300.jpg" /></p>
        
        <p>Binge-watched this in one sitting. Very powerful, all its 4 episodes are shot on a single take and that really brings you into the room with the characters.</p>
<p>The final montage is beautiful, too. Bonus points for having one of my favorite songs.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://m.media-amazon.com/images/M/MV5BNGY1YjBiNzMtYWZhNC00OWViLWE0MzItNjc4YzczOGNiM2I0XkEyXkFqcGc@._V1_SX300.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://m.media-amazon.com/images/M/MV5BNGY1YjBiNzMtYWZhNC00OWViLWE0MzItNjc4YzczOGNiM2I0XkEyXkFqcGc@._V1_SX300.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/quick-reviews/anora</guid>
      <title>Quick Review: Anora</title>
      <link>https://fantinel.dev/quick-reviews/anora</link>
      <pubDate>Sat, 15 Mar 2025 12:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>Anora <br> 2024, Sean Baker</p>
        <p>My rating: I like it</p>
        <p><img src="https://m.media-amazon.com/images/M/MV5BYThiN2M0NTItODRmNC00NDhlLWFiYTgtMWM2YTEyYzI3ZTY1XkEyXkFqcGc@._V1_SX300.jpg" /></p>
        
        <p>An overall decent movie until the Armenians come in. Then it gets amazing! For a drama film, the comedy is what steals the show.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://m.media-amazon.com/images/M/MV5BYThiN2M0NTItODRmNC00NDhlLWFiYTgtMWM2YTEyYzI3ZTY1XkEyXkFqcGc@._V1_SX300.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://m.media-amazon.com/images/M/MV5BYThiN2M0NTItODRmNC00NDhlLWFiYTgtMWM2YTEyYzI3ZTY1XkEyXkFqcGc@._V1_SX300.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/quick-reviews/captain-fantastic</guid>
      <title>Quick Review: Captain Fantastic</title>
      <link>https://fantinel.dev/quick-reviews/captain-fantastic</link>
      <pubDate>Sun, 09 Mar 2025 12:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>Captain Fantastic <br> 2016, Matt Ross</p>
        <p>My rating: Loved it!</p>
        <p><img src="https://m.media-amazon.com/images/M/MV5BMjE5OTM0OTY5NF5BMl5BanBnXkFtZTgwMDcxOTQ3ODE@._V1_SX300.jpg" /></p>
        
        <p>Such a good movie! It touches on very sensitive topics in a very heartwarming way. It’s fun, serious and uplifting at the same time.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://m.media-amazon.com/images/M/MV5BMjE5OTM0OTY5NF5BMl5BanBnXkFtZTgwMDcxOTQ3ODE@._V1_SX300.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://m.media-amazon.com/images/M/MV5BMjE5OTM0OTY5NF5BMl5BanBnXkFtZTgwMDcxOTQ3ODE@._V1_SX300.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/cool-links-february-2025</guid>
      <title>Cool Links Vol. 8: February, 2025</title>
      <description>Links to the best stuff I&apos;ve read or watched during the month of February, 2025</description>
      <link>https://fantinel.dev/blog/cool-links-february-2025</link>
      <pubDate>Fri, 28 Feb 2025 12:00:00 +0000</pubDate>
      <category>Cool Links</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/cool-links-february-2025">
              read on the site!
            </a>
          </strong>
        </div>

        <p>Hey there! This month I brought <em>a ton</em> of links, probably a result of me trying to make a habit of checking my RSS reader every morning during breakfast. Let’s get started!</p>
<h2 id="fun">Fun</h2>
<p><strong><a href="https://www.jwz.org/xscreensaver/google.html">XScreenSaver’s Privacy Policy</a>, by Jamie Zawinski</strong></p>
<p>Apparently when your indie app does not collect any amount of data, the data reapers get confused.</p>
<p><strong><a href="https://weeks.ginatrapani.org/">My Life in Weeks</a>, by Gina Trapani</strong></p>
<p>This is such a cool idea that I definitely want to copy in the future. I’m a bit wary of making so much private information public (especially dates), so I might not ever make this public anywhere. But still, a nice personal exercise and a perfect <em>memento mori</em>.</p>
<p><strong><a href="https://noclip.website/">noclip.website</a></strong></p>
<p>This is so cool! This website allows you to explore the 3D models of maps from a variety of old-ish games from the Wii, GameCube, DS and PS2 eras. If you’ve played any of them, it might be worth having a look. My favorite ones to explore like this were the maps from Pokémon HeartGold/SoulSilver and Platinum.</p>
<p>I recommend opening the site on a computer though. The touch controls aren’t great.</p>
<p><strong><a href="https://tabloid.vercel.app/">How To Lose Brain Fat With This Programming Language!</a></strong></p>
<p>I love those joke programming languages. They’re perfect examples that sometimes the only reason you need to build something is that you can.</p>
<h2 id="dev">Dev</h2>
<p><strong><a href="https://jakearchibald.com/2024/view-transitions-handling-aspect-ratio-changes/">View transitions: Handling aspect ratio changes</a>, by Jake Archibald</strong></p>
<p>Jake gives a really thorough explanation on view transitions, showing some of its shortcomings when animating some specific elements and how to fix them. View transitions are so nice 🤩</p>
<p><strong><a href="https://www.smashingmagazine.com/2017/10/naming-things-css-grid-layout/#naming-lines">Faux Containers in CSS Grids</a>, by Tyler Sticka</strong></p>
<p>A fun exercise about making elements “pop out” of their containers with CSS without altering markup. Might not be useful if you can change the markup (as there are easier ways to do that), but learning this kind of approach is always good for expanding your repertoire.</p>
<p><strong><a href="https://www.smashingmagazine.com/2017/10/naming-things-css-grid-layout/#naming-lines">Naming Things In CSS Grid Layout</a>, by Rachel Andrew</strong></p>
<p>That article about the faux containers lead me to this one. I knew about naming CSS grid areas, but I had no idea about the [area-start/end] pattern! You can set those explicitly or have them be automatically added by CSS. This is pretty cool!</p>
<p><strong><a href="https://unplannedobsolescence.com/blog/hard-page-load/">Who’s Afraid of a Hard Page Load?</a>, by Unplanned Obsolescence</strong></p>
<blockquote>
<p>The smoothness of a web application is an anti-indicator of its reliability and predictability as a web page.</p>
</blockquote>
<blockquote>
<p>your team almost certainly doesn’t have what it takes to out-engineer the browser. The browser will continuously improve the experience of plain HTML, at no cost to you, using a rendering engine that is orders of magnitude more efficient than JavaScript.</p>
</blockquote>
<p>I remember when I first learned about SPAs and how amazing it seemed like to be able to have smooth transitions between pages. Then, as I started building and using them, it became apparent that those benefits also brought a lot of issues that took a lot of dev work to fix.</p>
<p>Luckily, with View Transitions, lazy loading, and predictive pre-rendering (start loading a page before you click on its link) that a lot of frameworks have now, we can have most of the SPA benefits without having to reinvent the wheel.</p>
<p><strong><a href="https://www.joshwcomeau.com/css/container-queries-unleashed/">Container Queries Unleashed</a>, by Josh Comeau</strong></p>
<p><a href="https://fantinel.dev/container-queries">I’ve written about Container Queries before</a>, but this article by Josh Comeau is great at giving even more examples of its utility. It’s always nice to see what use cases other developers find for it.</p>
<p><strong><a href="https://christianheilmann.com/2025/01/15/learning-html-is-the-best-investment-i-ever-did/">Learning HTML is the best investment I ever did</a>, by Chris Heilmann</strong></p>
<p>HTML and CSS are my favorite parts of web development, and this article gives some great examples of why. Learning HTML is one of the best things you can do for your (web development) career, as it’s the most foundational block of a website and by itself can do most of the functionality you need. From HTML, you can <a href="https://fantinel.dev/progressive-enhancement">progressively enhance</a> the rest.</p>
<p><strong><a href="https://polypane.app/blog/getting-stuck-all-the-ways-position-sticky-can-fail/">Getting stuck: all the ways position:sticky can fail</a>, by Polypane</strong></p>
<p>position: sticky is incredibly useful but I’ve had issues implementing it more than once. This article goes over some of the most common issues we can face with it and how we can fix them.</p>
<p><strong><a href="https://notadesigner.io/p/how-to-pick-font">How to pick a font (or is it a typeface?)</a>, by Saron from Not A Designer</strong></p>
<p>Cool article explaining a little bit about fonts and choosing them for your website/app. I’m still overwhelmed by the options, but I found the info in there to be interesting.</p>
<h2 id="deep-reads">Deep Reads</h2>
<p><strong><a href="https://www.newcartographies.com/p/in-the-kingdom-of-the-bored-the-one">In the Kingdom of the Bored, the One-Armed Bandit Is King</a>, by Nicholas Carr</strong></p>
<blockquote>
<p>Abundance breeds boredom. When there’s no end of choices, each choice feels disappointing.</p>
</blockquote>
<blockquote>
<p>It was once assumed that digitization would liberate cultural artifacts from their physical containers. We’d be able to enjoy the wine without the bottles. What’s actually happened is different. We’ve come, as Goldsmith says, “to prefer the bottles to the wine.”</p>
</blockquote>
<p>Worth a read if you use any kind of computer technology, really. It’s so weird how our ancient brain has adapted to the digital world - or maybe hasn’t adapted at all?</p>
<p><strong><a href="https://gkeenan.co/avgb/ive-missed-sam-for-a-very-long-time-or-pick-your-battles/">I’ve missed Sam for a long time (or: Pick Your Battles)</a>, by Keenan</strong></p>
<p>What a powerful read. Not really tech-related, but as someone who’s seen loved ones go down the same route as Sam did, it’s a very relatable, sad, albeit weirdly comforting, read.</p>
<h2 id="wrapping-up">Wrapping up</h2>
<p>Phew, that was a lot! Some of them were in my “Read later” queue for a while. I’ll keep working on clearing up that queue and let’s see how next month goes.</p>
<p>See you then!</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/opengraph/cool-links/Q29vbCUyMExpbmtzJTIwVm9sLiUyMDg=/MjAyNS0wMi0yOFQxMiUzQTAwJTNBMDAuMDAwWg==/image.png"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/opengraph/cool-links/Q29vbCUyMExpbmtzJTIwVm9sLiUyMDg=/MjAyNS0wMi0yOFQxMiUzQTAwJTNBMDAuMDAwWg==/image.png"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/quick-reviews/conclave</guid>
      <title>Quick Review: Conclave</title>
      <link>https://fantinel.dev/quick-reviews/conclave</link>
      <pubDate>Fri, 28 Feb 2025 12:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>Conclave <br> 2024, Edward Berger</p>
        <p>My rating: I like it</p>
        <p><img src="https://m.media-amazon.com/images/M/MV5BYjgxMDI5NmMtNTU3OS00ZDQxLTgxZmEtNzY1ZTBmMDY4NDRkXkEyXkFqcGc@._V1_SX300.jpg" /></p>
        
        <p>For a movie with pretty much no action, it is incredibly good at keeping you hooked. Everything is well made, and I loved the ending too!</p>
<p>Bonus points for being the first time I see my favorite dish on a movie 😋</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://m.media-amazon.com/images/M/MV5BYjgxMDI5NmMtNTU3OS00ZDQxLTgxZmEtNzY1ZTBmMDY4NDRkXkEyXkFqcGc@._V1_SX300.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://m.media-amazon.com/images/M/MV5BYjgxMDI5NmMtNTU3OS00ZDQxLTgxZmEtNzY1ZTBmMDY4NDRkXkEyXkFqcGc@._V1_SX300.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/special-interests-online-negativity</guid>
      <title>“Special Interests” and Negativity</title>
      <description>Finding a community that loves something is getting increasingly harder.</description>
      <link>https://fantinel.dev/blog/special-interests-online-negativity</link>
      <pubDate>Tue, 04 Feb 2025 20:18:07 +0000</pubDate>
      <category>Personal</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/special-interests-online-negativity">
              read on the site!
            </a>
          </strong>
        </div>

        <p>Easily one of the best parts of my atypical brain is its ability to dive deeply into subjects I find interesting. Throughout my life these subjects have been quite varied, with music, movies, art, history, and games all sucking me in at some point in my life.</p>
<p>With the internet it’s always been quite easy for me to find stuff to read or watch about what I like. This allowed me to have a pretty in-depth knowledge about a lot of things.</p>
<p>Then, something changed. A change that started happening subtly but that’s now pretty hard to ignore. People used to gather online in communities centered about <em>things they liked</em>. They’d be similarly excited about similar things, and discussing those things would usually be a joy.</p>
<p>Now it seems like people only go out of their way if it’s to talk about <em>things they hate</em>. Communities centered around something remotely popular are gonna be filled with people complaining and pointing out every single flaw about it.</p>
<p>Why is that? I’m not sure. Maybe because people are so angry all the time? <a href="https://fantinel.dev/jomo-joy-of-missing-out">Digital drugs (a.k.a. social media) monetize your anger</a>, so we’re naturally more angry if we don’t actively fight against it.</p>
<p>It’s just something I realized and that made me stop and think about how I approach my interests now. If something brings me joy and I want to keep things that way, then it’s just better to avoid online communities about that interest altogether. It just sucks that enjoying something with others is getting increasingly hard.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/cms/media/articles/special-interests-online-negativity.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/cms/media/articles/special-interests-online-negativity.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/quick-reviews/the-whale</guid>
      <title>Quick Review: The Whale</title>
      <link>https://fantinel.dev/quick-reviews/the-whale</link>
      <pubDate>Sun, 02 Feb 2025 12:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>The Whale <br> 2022, Darren Aronofsky</p>
        <p>My rating: I like it</p>
        <p><img src="https://m.media-amazon.com/images/M/MV5BYmNhOWMyNTYtNTljNC00NTU3LWFiYmQtMDBhOGU5NWFhNGU5XkEyXkFqcGc@._V1_SX300.jpg" /></p>
        
        <p>Amazing drama with great acting by Brendan Fraser and some good thought-provoking messages. Got really emotional by the end.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://m.media-amazon.com/images/M/MV5BYmNhOWMyNTYtNTljNC00NTU3LWFiYmQtMDBhOGU5NWFhNGU5XkEyXkFqcGc@._V1_SX300.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://m.media-amazon.com/images/M/MV5BYmNhOWMyNTYtNTljNC00NTU3LWFiYmQtMDBhOGU5NWFhNGU5XkEyXkFqcGc@._V1_SX300.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/cool-links-january-2025</guid>
      <title>Cool Links Vol. 7: January, 2025</title>
      <description>Links to the best stuff I&apos;ve read or watched during the month of January, 2025</description>
      <link>https://fantinel.dev/blog/cool-links-january-2025</link>
      <pubDate>Fri, 31 Jan 2025 12:00:00 +0000</pubDate>
      <category>Cool Links</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/cool-links-january-2025">
              read on the site!
            </a>
          </strong>
        </div>

        <p>Hey there! I hope your 2025 has started on a high note. What about some cool links to read during this weekend?</p>
<h2 id="fun">Fun</h2>
<p><strong><a href="https://henry.codes/">Henry Desroches’ Website</a></strong></p>
<p>Henry’s personal website is absolutely stunning! It has an unique design that is, above all, <strong>fun to explore</strong>. I miss exploring websites, instead of being guided through them.</p>
<p><strong><a href="https://neal.fun/stimulation-clicker/">Stimulation Clicker</a>, by Neal Agarwal</strong></p>
<p>Most of what Neal makes is pure gold and this is no exception. This game is no different than most of what you can find on your App Store, it’s just more honest about it…</p>
<h2 id="dev">Dev</h2>
<p><strong><a href="https://ishadeed.com/article/balancing-text-css">Balancing Text In CSS</a>, by Ahmad Shadeed</strong></p>
<p>Incredible article that not only explains the new-ish <code>text-wrap: balance</code> and <code>text-wrap: pretty</code> CSS properties in-depth, it also goes into the caveats those properties have. It’s well-written, well illustrated, and interactive. What else could you want?</p>
<p><strong><a href="https://ishadeed.com/article/overflow-clip">Overflow Clip</a>, by Ahmad Shadeed</strong></p>
<p>Ahmad once again writing the articles I wish I did. Another well-written and interactive article going in-depth on the also new-ish <code>overflow: clip</code> CSS property. <code>clip</code> has helped me implement designs more than once and it’s so nice to have something that works just like I always expected <code>overflow: hidden</code> to work.</p>
<h2 id="wrapping-up">Wrapping up</h2>
<p>This month was also a bit short on links as I have  been mostly focused on work rather than learning. Here’s hoping I can get back to finding more good links soon! 🤞</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/opengraph/cool-links/Q29vbCUyMExpbmtzJTIwVm9sLiUyMDc=/MjAyNS0wMS0zMVQxMiUzQTAwJTNBMDAuMDAwWg==/image.png"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/opengraph/cool-links/Q29vbCUyMExpbmtzJTIwVm9sLiUyMDc=/MjAyNS0wMS0zMVQxMiUzQTAwJTNBMDAuMDAwWg==/image.png"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/quick-reviews/supernatural-season-6</guid>
      <title>Quick Review: Supernatural (Season 6)</title>
      <link>https://fantinel.dev/quick-reviews/supernatural-season-6</link>
      <pubDate>Fri, 31 Jan 2025 12:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>Supernatural (Season 6) <br> 2010, Sera Gamble</p>
        <p>My rating: I like it</p>
        <p><img src="https://fantinel.dev/cms/media/quick-reviews/supernatural-season-6.jpg" /></p>
        
        <p>I had watched this one many years ago and remember not liking it very much. After rewatching it, I was surprised… it’s actually pretty good!</p>
<p>It’s kind of a soft reboot after the series’ story ending in Season 5, so it’s understandable that most people didn’t like it back then. But when you’re not expecting a continuation and instead understand it’s a reboot, it’s actually pretty enjoyable.</p>
<p>Fun episodes and interesting arcs (that honestly could have lasted a bit longer). I might even say it’s better than seasons 2 and 3.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/cms/media/quick-reviews/supernatural-season-6.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/cms/media/quick-reviews/supernatural-season-6.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/quick-reviews/nosferatu</guid>
      <title>Quick Review: Nosferatu</title>
      <link>https://fantinel.dev/quick-reviews/nosferatu</link>
      <pubDate>Sat, 25 Jan 2025 12:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>Nosferatu <br> 2024, Robert Eggers</p>
        <p>My rating: Loved it!</p>
        <p><img src="https://m.media-amazon.com/images/M/MV5BY2FhZGE3NmEtNWJjOC00NDI1LWFhMTQtMjcxNmQzZmEwNGIzXkEyXkFqcGc@._V1_SX300.jpg" /></p>
        
        <p>Holy shit, what an incredible movie.</p>
<p>Tense, intense, beautiful, terrifying.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://m.media-amazon.com/images/M/MV5BY2FhZGE3NmEtNWJjOC00NDI1LWFhMTQtMjcxNmQzZmEwNGIzXkEyXkFqcGc@._V1_SX300.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://m.media-amazon.com/images/M/MV5BY2FhZGE3NmEtNWJjOC00NDI1LWFhMTQtMjcxNmQzZmEwNGIzXkEyXkFqcGc@._V1_SX300.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/quick-reviews/young-sheldon</guid>
      <title>Quick Review: Young Sheldon</title>
      <link>https://fantinel.dev/quick-reviews/young-sheldon</link>
      <pubDate>Mon, 20 Jan 2025 12:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>Young Sheldon <br> 2017–2024, Chuck Lorre</p>
        <p>My rating: I like it</p>
        <p><img src="https://m.media-amazon.com/images/M/MV5BODU3M2FmMDctNGViYi00OGUxLThkZDAtZmRkZDBhNWJhNDIzXkEyXkFqcGc@._V1_SX300.jpg" /></p>
        
        <p>A surprisingly good show, a far cry from The Big Bang Theory. Sheldon is in the title, but the supporting characters are what really steal the show. They’re varied, complex, and above all, funny. It ended at the right time and in a beautiful way.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://m.media-amazon.com/images/M/MV5BODU3M2FmMDctNGViYi00OGUxLThkZDAtZmRkZDBhNWJhNDIzXkEyXkFqcGc@._V1_SX300.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://m.media-amazon.com/images/M/MV5BODU3M2FmMDctNGViYi00OGUxLThkZDAtZmRkZDBhNWJhNDIzXkEyXkFqcGc@._V1_SX300.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/quick-reviews/balatro</guid>
      <title>Quick Review: Balatro</title>
      <link>https://fantinel.dev/quick-reviews/balatro</link>
      <pubDate>Tue, 14 Jan 2025 12:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>Balatro <br> 2024, LocalThunk</p>
        <p>My rating: I like it</p>
        <p><img src="https://fantinel.dev/cms/media/quick-reviews/balatro.jpg" /></p>
        
        <p>The idea of this being a “Poker Roguelike”, two things I don’t really like, kept me off the game for a while. Luckily, it being included on Apple Arcade gave me the nudge to try it out. And I’m glad I did!</p>
<p>It’s so easy to start, and hard to stop. Addictive, satisfying, and overall so fun to play.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/cms/media/quick-reviews/balatro.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/cms/media/quick-reviews/balatro.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/cool-links-december-2024</guid>
      <title>Cool Links Vol. 6: December, 2024</title>
      <description>Links to the best stuff I&apos;ve read or watched during the month of December, 2024</description>
      <link>https://fantinel.dev/blog/cool-links-december-2024</link>
      <pubDate>Tue, 31 Dec 2024 12:00:00 +0000</pubDate>
      <category>Cool Links</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/cool-links-december-2024">
              read on the site!
            </a>
          </strong>
        </div>

        <p>Happy Holidays! I have four new links to read while you recharge your batteries from dealing with end-of-year stress or recovering from overeating.</p>
<p>They’re either <em>introspective</em> or <em>retrospective</em>, as is the theme this time of year.</p>
<h2 id="deep-reads">Deep Reads</h2>
<p><strong><a href="https://stevenscrawls.com/care-doesnt-scale/">Care Doesn’t Scale</a>, by Steven Scrawls</strong></p>
<p>This is such a good article that resonated very deeply with me. As someone who wished could do more to help the world and as a software developer who thinks about scalability, it’s a hard realization when you get older and don’t see yourself as someone who’s made a big difference.</p>
<p>But turns out that caring for someone or something doesn’t scale. It can’t, otherwise it’s not <em>care</em> anymore.</p>
<p><strong><a href="https://stephango.com/self-guarantee">Self-guaranteeing promises</a>, by Steph Ango</strong></p>
<blockquote>
<p>A self-guaranteeing promise does not require you to trust anyone.</p>
</blockquote>
<p>Steph Ango is the lead developer behind Obsidian, and I’ve mentioned him before on my <a href="https://fantinel.dev/owning-your-stuff">Owning your stuff is pretty cool, actually</a> post earlier this year. On this article he talks about how the only way to guarantee ownership of your data is if the service can never access it in the first place. Terms of service guarantees are based on trust that the company’s priorities will never change, and that trust has been broken again and again.</p>
<h2 id="dev">Dev</h2>
<p><strong><a href="https://2024.stateofjs.com/en-US/">State of JavaScript 2024</a></strong></p>
<p>It’s always fun to see these “State of…” surveys. Noteworthy thing being Astro completely dominating the framework numbers (except for usage, but I can totally see it becoming #1 soon).</p>
<p><strong><a href="https://chrome.dev/css-wrapped-2024/">CSS Wrapped 2024</a></strong></p>
<p>A really well-designed post from the Chrome team showing the coolest new things that were introduced to CSS in 2024. A lot of the things in there are really cool! The sad part is that, unless you’re running an up-to-date Chromium browser, you might not be able to see them in action. I initially saw this blog post on my iPhone (which only has access to Safari) and almost none of them worked.</p>
<h2 id="wrapping-up">Wrapping up</h2>
<p>With 2024 over and done, I want to thank <strong>you</strong> for following me along over here, and I hope you stick around for 2025.</p>
<p>See you next year!</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/opengraph/cool-links/Q29vbCUyMExpbmtzJTIwVm9sLiUyMDY=/MjAyNC0xMi0zMVQxMiUzQTAwJTNBMDAuMDAwWg==/image.png"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/opengraph/cool-links/Q29vbCUyMExpbmtzJTIwVm9sLiUyMDY=/MjAyNC0xMi0zMVQxMiUzQTAwJTNBMDAuMDAwWg==/image.png"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/quick-reviews/the-platform-2</guid>
      <title>Quick Review: The Platform 2</title>
      <link>https://fantinel.dev/quick-reviews/the-platform-2</link>
      <pubDate>Tue, 31 Dec 2024 12:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>The Platform 2 <br> 2024, Galder Gaztelu-Urrutia</p>
        <p>My rating: Didn't like it</p>
        <p><img src="https://m.media-amazon.com/images/M/MV5BNzI4YjkyODctMGJmNC00YjBjLTllNjAtNDkxNTJmZjg4MGZkXkEyXkFqcGc@._V1_SX300.jpg" /></p>
        
        <p>Whatever this movie had working on its favor on the first half is completely lost on the latter half. A lot of disjointed narratives that don’t seem to go anywhere.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://m.media-amazon.com/images/M/MV5BNzI4YjkyODctMGJmNC00YjBjLTllNjAtNDkxNTJmZjg4MGZkXkEyXkFqcGc@._V1_SX300.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://m.media-amazon.com/images/M/MV5BNzI4YjkyODctMGJmNC00YjBjLTllNjAtNDkxNTJmZjg4MGZkXkEyXkFqcGc@._V1_SX300.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/default-apps-2024</guid>
      <title>My Default Apps: 2024</title>
      <description>My apps of choice for most of my daily activities.</description>
      <link>https://fantinel.dev/blog/default-apps-2024</link>
      <pubDate>Fri, 27 Dec 2024 02:16:57 +0000</pubDate>
      <category>Apps</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/default-apps-2024">
              read on the site!
            </a>
          </strong>
        </div>

        <p><a href="https://fantinel.dev/default-apps-2023">Last year</a>, I jumped on the trend back then of posting my default apps i.e. the apps I use on a daily basis for most of my activities, both on the computer and phone.</p>
<p>I had kinda forgotten about it this year, until I saw some people posting their updated list of apps before the end of the year. So I decided to do the same! It’ll be nice to look back at this list in ~5 years and see how much I’ve changed.</p>
<p>But, as I updated the list, it looks like… almost nothing changed. I think my time of hopping between apps all the time has ended (or is slowly fading away). I don’t see many reasons to change something that works well, so thankfully most of them stayed the same.</p>
<p>I highlighted the services/apps that are new with the ✨ emoji.</p>
<ul>
<li>📧 Mail service: <a href="https://fastmail.com">Fastmail</a></li>
<li>📬 Mail client: Apple Mail</li>
<li>📆 Calendar: Apple Calendar</li>
<li>✅ Tasks: <a href="https://ticktick.com/">TickTick</a> (<em>in 2023, I used Notion as well</em>)</li>
<li>📝 Notes: <a href="https://obsidian.md">Obsidian</a> ✨
<ul>
<li>I wrote some of my reasoning for using Obsidian <a href="https://fantinel.dev/owning-your-stuff">on this post</a>.</li>
</ul>
</li>
<li>🌐 Web browser: <a href="https://arc.net/">Arc</a> on Mac, Arc Search ✨ on iPhone (<em>in 2023, I used Safari on iPhone</em>)</li>
<li>📰 RSS service: <a href="https://readwise.io/read">Readwise Reader</a></li>
<li>📖 Reading: <a href="https://readwise.io/read">Readwise Reader</a></li>
<li>🧮 Code Editor: <a href="https://code.visualstudio.com/">Visual Studio Code</a></li>
<li>👨🏻‍💻 Terminal: <a href="https://iterm2.com/">iTerm 2</a></li>
<li>🔎 Search: <a href="https://kagi.com">Kagi</a></li>
<li>⌨️ Launcher: <a href="https://www.raycast.com/">Raycast</a></li>
<li>☁️ Cloud storage: iCloud</li>
<li>🌅 Photo library: iCloud</li>
<li>🌤️ Weather: Apple Weather</li>
<li>🎙️ Podcasts: Apple Podcasts / YouTube</li>
<li>🎶 Music: Apple Music</li>
<li>🛹 Clipboard manager: <a href="https://maccy.app/">Maccy</a></li>
<li>🔐 Passwords: <a href="https://1password.com/">1Password</a></li>
<li>💸 Budgeting: <a href="https://www.ynab.com/">YNAB</a> ✨</li>
<li>💁🏻‍♂️ Social: <a href="https://joinmastodon.org/">Mastodon</a></li>
<li>🐘 Mastodon: <a href="https://tapbots.com/ivory/">Ivory</a> (Mac and iPhone)</li>
<li>🎮 Gaming: PS5</li>
<li>🖼️ Screenshots: <a href="https://cleanshot.com/">CleanShot X</a></li>
</ul>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/cms/media/articles/default-apps-2024.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/cms/media/articles/default-apps-2024.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/2024-year-in-review</guid>
      <title>2024 In The Rearview Mirror</title>
      <description>What was good and what wasn&apos;t in 2024.</description>
      <link>https://fantinel.dev/blog/2024-year-in-review</link>
      <pubDate>Mon, 23 Dec 2024 15:16:57 +0000</pubDate>
      <category>Retrospective</category><category>Meta</category><category>Personal</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/2024-year-in-review">
              read on the site!
            </a>
          </strong>
        </div>

        <p>At the end of every year, I write a retrospective of what I did, learned, felt, watched, played or listened to. It’s the perfect way to reflect on everything, figure out what was good, bad and what could be better!</p>
<p>If you’d like, you can read my posts for <a href="https://fantinel.dev/2019-year-in-review">2019</a>, <a href="https://fantinel.dev/2020-year-in-review">2020</a>, <a href="https://fantinel.dev/2021-year-in-review">2021</a>, <a href="https://fantinel.dev/2022-year-in-review">2022</a>, and <a href="https://fantinel.dev/2023-year-in-review">2023</a>.</p>
<h2 id="last-years-predictions">Last Year’s Predictions</h2>
<p>Last year I made some predictions (or hopes) of what would happen this year. Did I get anything right?</p>
<blockquote>
<p>I expect to take big steps towards owning my own house. I really want to stop paying rent and hopefully get a house instead of an apartment. It’s more work, but it’s more fulfilling and freeing.</p>
</blockquote>
<p>Eh, partially, I guess? I did save money towards that end, but a change of plans made me not look into buying anything for now. More on that later.</p>
<blockquote>
<p>I expect to write even more on this blog, and continue to expand on the topics I post about. Dev articles aren’t going anywhere, but I don’t always have something to post about in that regard.</p>
</blockquote>
<p>✅ I got this one right! I did write more this year, and while the topics didn’t get as varied as I’d liked, I’m still happy with what came out of the blog in 2024.</p>
<blockquote>
<p>Also, I want to add proper support for categories and filtering, as well as search. As I post more, even I am starting to struggle finding something I wrote in the past to reference. Additionally, some code refactoring is always welcome.</p>
</blockquote>
<p>❌ Nope, I did not do that. Not this year at least. I wrote about my desire to <a href="https://fantinel.dev/thinking-of-a-redesign">redesign my website</a> and all that work has been moved to that redesign. It did not get ready (<em>or started</em>) in time, so that was a wrong prediction.</p>
<blockquote>
<p>I also want to travel a bit next year too - either to the US to meet my work colleagues or to Italy to see my brother. And of course, do some sightseeing!</p>
</blockquote>
<p>✅ I did travel this year! I traveled to New York and Chicago. More on those trips later.</p>
<h2 id="personal-life">Personal Life</h2>
<p>Not much happened on my personal life this year, now that I think about it. In a good way. I feel like I’ve been able to mature a lot in the past couple years, after the autism diagnosis, therapy, and coming to terms with who I am and who I can be. Turns out everything gets easier when you learn not only that you <em>should</em> be kind to yourself, but <em>how</em> to be kind to yourself.</p>
<h3 id="travel">Travel</h3>
<p>Last year, I said I was hoping to either going to the US to meet my work colleagues or to go to Italy to see my brother. Since I already had been to Italy, I chose to go to the US, and it was pretty fun! My wife and I traveled there in August, near the end of summer. We spent ~5 days in New York and ~5 in the Chicago area.</p>
<h4 id="new-york">New York</h4>
<p>New York City is, no other way of saying it, complete and utter chaos. But in a (mostly) good way! Being so big and having so many people from everywhere in the world, there’s just no shortage of things to do and to see.</p>
<p>We went with a mentality that we’d rather do less and have a great time than to try and do everything and get stressed about it. But turns out we were able to do pretty much everything we wanted! We got a lot of tips of the best times to go to each place and were able to plan some of our days accordingly.</p>
<p>We went to some of the most famous places like Rockefeller Center (and went to the Top of the Rock at sunset), the Empire State Building, the Statue of Liberty and Times Square. Also the Natural History museum and The MET.</p>
<p>Some of the nicer surprises were the <em>experiences</em> based on pop culture stuff. We went to The Friends Experience, an entire place dedicated to the Friends sitcom, with original items used in the show and really faithful recreations of Monica’s and Joey’s apartments, as well as the Central Perk coffee shop. It was a weird feeling being in that place that I’d never been before, but knew every corner. I was worried it’d be just a shameless cash grab, but it was actually pretty cool.</p>
<p>Being a big Nintendo nerd, we also went to the Nintendo Store. It was, honestly, a bit underwhelming, but still great to see so many things based on games I love.</p>
<p><img src="https://fantinel.dev/cms/media/articles/2024-year-in-review/broadway.png" alt="Picture of the Broadway Theater from inside. In the background the stage is illuminated by a blue glow, with the picture of the aurora in the skies in the backdrop. The theater seats are empty in front of me, and the playbill of the play is being held in the foreground." title="Waiting for The Great Gatsby to start on the Broadway Theater in NYC."></p>
<p>But nothing we did there can even compare to the experience of going to a Broadway show! We went to see The Great Gatsby, and wow… just wow. It was honestly the most incredible thing I’ve ever seen, and I don’t say that lightly. The music, the scenarios, the performances, and how well all that transitioned in front of your eyes, it’s just mind-blowing. I’d never watched a musical play (only movies, which I hated), but it’s now one of my favorite things. Too bad there’s nothing like that where I live, but whenever I get back to NYC, I’m definitely watching more musicals.</p>
<h4 id="chicago-and-aurora">Chicago and Aurora</h4>
<p>On my birthday, we took a plane to Chicago. We celebrated by having a famous deep-dish pizza at Gino’s East. It’s pretty good, but I’d kinda classify it as a pie and not pizza 😅</p>
<p>The main reason we went to Chicago was because I wanted to finally get to meet my work colleagues in person. Most of them live in the city of Aurora, which is around an hour from Chicago by train.</p>
<p>Meeting them was one of the highlights of the whole trip. It’s nice (and weird) to finally meet people you’ve only spoken to online for the first time. They were really lovely as usual and made us feel very welcome there.</p>
<p>Since we were in Chicago anyway, we did some sightseeing there too. We went to the Field Museum, Shedd Aquarium, the Bean and even went to the top of Willis Tower, which was literally shaking from the wind.</p>
<p>Downtown Chicago is really pretty, way prettier than NY, but understandably there’s a lot less going on. Oh, and it was <em>hot</em> in there. I’ve never felt so hot in my life. It was around 38ºC during the day, but felt like it was over 40ºC. 🥵</p>
<p><img src="https://fantinel.dev/cms/media/articles/2024-year-in-review/chicago.png" alt="Picture of downtown Chicago, with ridiculously tall buildings surrounding a river." title="It&#x27;s so pretty that it doesn&#x27;t even look like the sun was functioning as a death ray that day."></p>
<h3 id="tragedy">Tragedy</h3>
<p>2024 unfortunately was the year where the state I live in got hit by a big tragedy, with a lot of consequences to everyone who lives here.</p>
<p>At the beginning of May, the state of Rio Grande do Sul was <a href="https://edition.cnn.com/2024/05/08/weather/brazil-flooding-satellite-imagery-intl/index.html">hit by absurd amounts of rain in the matter of days</a>, which put almost the entire state quite literally underwater. So many people lost their houses, belongings, and even their lives. Entire cities were lost, and so much of our infrastructure was damaged and is still far from being rebuilt.</p>
<p>Luckily, I wasn’t personally affected by it (besides the comparatively trivial shipping delays and/or being unable to travel to neighboring cities). My city stands in a mountain range and even though we had a lot of rain, the water naturally flows away down the mountains (and sadly flooded the cities in the valleys or in the river deltas). But it was still heartbreaking to see people suffer so much because of it. And it does hit much harder when these things happen so close to you.</p>
<p>The bright side of it all is how many people came together to help the ones in need, from all over the country. Brazil is enormous and it felt like everyone did their best to help us pull ourselves around. It does help you restore a bit of faith in humanity.</p>
<h2 id="blog">Blog</h2>
<p>Another good year for the blog! I’ve published a total of <strong>18</strong> posts so far, not counting this one. It’s my record, though 5 of them were the new <em>Cool Links</em> series, which is not really original content. Even if we don’t count those, we still have <strong>13</strong>, one more than last year!</p>
<p>The big thing this year was the creation of the <em>Cool Links</em> series, a monthly blog post where I put all the best links I’ve saved during each month. It’s a natural evolution of the <em>Reading Recs</em> I started posting last year, and I’m happy with how they’re turning out. It’s always good to have a chance to link to other websites and comment on them, without having to spin up a blog post for each one of them.</p>
<p>Another new thing is my newsletter, which, to be fair, is more of an email feed of the blog instead of a newsletter. I’ve started it in October, and it’s not being a huge hit but I didn’t expect it to. It’s meant to be an additional way to <a href="https://fantinel.dev/subscribe">subscribe</a> to what I write.</p>
<p>My favorite posts of the year were, once again, the more personal ones: <a href="https://fantinel.dev/longterm-goals">Best longterm goal? Not having one</a> and <a href="https://fantinel.dev/jomo-joy-of-missing-out">JOMO is a Beautiful Thing</a>.</p>
<h2 id="work">Work</h2>
<p>Since last year I’ve been working at <a href="https://usefulgroup.com">Useful Group</a>, a lovely agency in Aurora, Illinois, USA. I’ve been pretty happy with my job there, working on nice projects and most importantly with amazing people. It’s so refreshing to find people in the software development industry that are nice and actually care about other people and about doing good work.</p>
<p>On the side projects front, I didn’t do much this year. I worked on the redesign of my website in Figma, but didn’t get to coding it yet. I learned a lot of cool CSS stuff and leaned into Progressive Enhancement as much as possible at work. Wanting to improve our sliders at work, I ended up developing a web component called <a href="https://github.com/matfantinel/enhanced-css-slider">enhanced-css-slider</a>, which I’ve been using at work for almost a year.</p>
<h2 id="fun">Fun</h2>
<p>The best part of the year – the fun things I’ve watched, listened to or played this year.</p>
<h3 id="tv-and-movies">TV and Movies</h3>
<p>This year I haven’t watched any “serious” TV shows. I haven’t really felt like starting to watch something new and instead just re-watched some sitcoms as my “lunch show”. What I did watch was a bunch of movies, for which I started writing some quick reviews using the <a href="https://quickreviews.app">QuickReviews</a> web app. I plan on putting all those quick reviews up on my website eventually, but for now, here’s some of the best ones:</p>
<ul>
<li><strong>The Office US</strong> was the only new (for me) show that I watched in 2024. It took my wife and I a few tries before it actually <em>clicked</em> for us, but once it did, it’s so much fun! The dead-pan, slightly awkward (or <em>very awkward</em>) humor is my jam. We watched the entire 9 seasons once, then re-watched it up to season 7 right after;</li>
<li><strong>Inside Out 2:</strong> You can feel all sorts of emotions on this one (hah!), but the main one is joy. Amazing movie, funny, relatable, touches on very important topics with good humor and a lot of sensibility. Props to the Brazilian dubbing and localization which was amazing and probably made the movie even better than it originally was!</li>
<li><strong>Bacurau:</strong> It took me way too long to watch this movie, but I’m so glad I was finally able to watch it. Absolutely incredible, raw, weird and full of symbolisms that will keep me thinking about the movie for days. Another masterpiece in Brazilian cinema.</li>
<li><strong>Grave of the Fireflies:</strong> Just as beautiful as it is sad. What a movie. 🥺</li>
<li><strong>I’m Still Here (Ainda Estou Aqui):</strong> This one hits hard. It’s a brilliantly told true story of just one of the thousands of families destroyed the military dictatorship in Brazil. Its depiction of Rio de Janeiro in the 70s is very on point. The details make everything much more relatable. It’s a powerful reminder of why we must celebrate and fight to keep the rights we have today.</li>
</ul>
<h3 id="games">Games</h3>
<ul>
<li><strong>World of Warcraft</strong> is undoubtedly the game I’ve played the most in my life, being a big part of my teenage years. I had been on and off the game since 2013 and then off since 2015, but this year I started playing it again. Its new expansion, The War Within, has been a blast. It’s just a world I love being in and I’ve finally had the time to play it again. Where else can I be a humanoid cow that shoots lightning bolts?</li>
</ul>
<p><img src="https://fantinel.dev/cms/media/articles/2024-year-in-review/wow.png" alt="Screenshot of World of Warcraft. In it, an Orc mounted on a moose stands still in a grassfield. In the background, a giant crystal emanating light, coming out of the sky. The marks around the crystal makes it apparent that the sky is actually the top of a giant cave. Passing between the Orc and the crystal is an airship and some birds. To the right, the silhouette of a cathedral points to the crystal." title="This is an underground zone, and that crystal up there is called Beledar. It lights up the cavern in a way that makes it feel like it&#x27;s on the surface. Sometimes it goes dark, and the atmosphere of the entire zone changes."></p>
<ul>
<li>Last year I played <strong>Disco Elysium</strong> for the first time and it was genuinely one of the best artistic experiences I’ve ever had in my life. So, this year I watched my wife play it for the first time, to try and have the experience of playing through it all over again. Seeing everything again just made the game even better for me. It’s so powerful, funny and sad, with so many glimpses of hope. It’s an emotional rollercoaster and I get emotional just thinking about it.</li>
</ul>
<h3 id="music">Music</h3>
<ul>
<li><strong>What Happened to the Heart?</strong> is the new album by Aurora and my most-listened to album of the year. I’ll admit I didn’t like it on first listen, but after the second one it just grew on me and it’s one of my favorites ever now. It starts by being what you expect from her, then it gets weirder and weirder in the best way possible. I’m just sad I wasn’t able to see her live again this year.</li>
</ul>
<p><img src="https://fantinel.dev/cms/media/articles/2024-year-in-review/whtth.jpg" alt="Cover of What Happened to the Heart by Aurora. A blue background, with two hands holding a metal human heart." title="Album cover of What Happened to the Heart"></p>
<ul>
<li><strong>The Mandrake Project</strong> was the first solo album by Bruce Dickinson since 2005. It’s full of great songs and is amongst the best of his career (Iron Maiden included). I was lucky enough to be able to see him live in Porto Alegre in April of this year. The concert was amazing, his singing is still fantastic, his band was great and the crowd just makes everything better.</li>
<li><strong>Ordinary Corrupt Human Love</strong> is my new favorite album from Deafheaven. Last year I mentioned how it was growing up on me and this year I listened to it even more. I just love the mix of the melodic parts and the heavier ones.</li>
</ul>
<h2 id="what-awaits-in-2025">What Awaits in 2025</h2>
<p>Well, first of all the plan for 2025 is to move. We’re moving out of Brazil to give living abroad another chance. After the bad experience in 2022 we want to give it a fair try by going to an actual city with actual things to do. The idea is to try Italy and Austria, but we’ll see, nothing is set in stone.</p>
<p>Another not-as-big-but-still-big goal is to actually go ahead with my website’s redesign. The new design has grown up on me and so I think it is the right choice. The only thing I’m still undecided about is if I should go ahead and do a full rewrite (possibly with <a href="https://astro.build/">Astro</a>) or just update the visuals. 2025 Matt will be wiser and more capable of making that decision.</p>
<p>As for writing more on the blog, I’m not sure this time. Of course I want to, but I think I had a good cadence this year. I definitely want to continue with the monthly links and adding some non-blog content to the site (like the movie quick reviews).</p>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>Overall, 2024 was a great year for me. After the chaotic 2022, its two following years were definitely <em>calmer</em>. I think now that 2025 will have a lot <em>more</em> going on, but hopefully I’m better prepared to handle all that now.</p>
<p>See you next year!</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/cms/media/articles/2024-year-in-review/cover.png"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/cms/media/articles/2024-year-in-review/cover.png"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/text-overflow-multi-line</guid>
      <title>text-overflow: ellipsis on multi-line text</title>
      <description>Adding an automatic ellipsis to some text if it overflows its content is simple enough. But what if you want your text to have multiple lines?</description>
      <link>https://fantinel.dev/blog/text-overflow-multi-line</link>
      <pubDate>Mon, 02 Dec 2024 21:26:58 +0000</pubDate>
      <category>Front-End</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/text-overflow-multi-line">
              read on the site!
            </a>
          </strong>
        </div>

        <p>Adding an automatic ellipsis to some text if it overflows its content is simple enough. You just set <code>overflow: hidden</code> to make sure the container doesn’t scroll, and then <code>text-overflow: ellipsis</code> to add the ellipsis to the edge of the text.</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="css"><code><span class="line"><span style="color:#B392F0">.example</span><span style="color:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#79B8FF">	text-overflow</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">ellipsis</span><span style="color:#E1E4E8">;</span></span>
<span class="line"><span style="color:#79B8FF">	overflow</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">hidden</span><span style="color:#E1E4E8">;</span></span>
<span class="line"><span style="color:#E1E4E8">}</span></span></code></pre>
<p>But what if you want your text to have multiple lines, but still cut off if it’s too big? For example, you want to show the excerpt of a blog post on a blog post card (just like the ones on this blog). You want it to go up to 4 lines, but if the excerpt is bigger than that, you want to add the ellipsis at the end of the 4th line.</p>
<p>Turns out there’s a neat little trick, using some <code>-webkit</code> prefixed properties. <strong>Don’t worry, it works on all modern browsers, including Firefox and Safari.</strong></p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="css"><code><span class="line"><span style="color:#B392F0">.example</span><span style="color:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#79B8FF">	text-overflow</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">ellipsis</span><span style="color:#E1E4E8">;</span></span>
<span class="line"><span style="color:#79B8FF">	overflow</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">hidden</span><span style="color:#E1E4E8">;</span></span>
<span class="line"><span style="color:#79B8FF">	display</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">-webkit-box</span><span style="color:#E1E4E8">;</span></span>
<span class="line"><span style="color:#6A737D">	/* Set this to the max number of lines you want */</span></span>
<span class="line"><span style="color:#79B8FF">	-webkit-line-clamp</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">4</span><span style="color:#E1E4E8">;</span></span>
<span class="line"><span style="color:#79B8FF">	-webkit-box-orient</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">vertical</span><span style="color:#E1E4E8">;</span></span>
<span class="line"><span style="color:#E1E4E8">}</span></span></code></pre>
<p>Here’s how it looks like:</p>
<p><img src="https://fantinel.dev/cms/media/articles/text-overflow-multi-line/example.png" alt="Screenshot of a card element in which a paragraph is cut off at the end of the 4th line with an ellipsis."></p>
<p>Taken from <a href="https://stackoverflow.com/a/34559614/6604030">this StackOverflow answer</a>.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/cms/media/articles/text-overflow-multi-line.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/cms/media/articles/text-overflow-multi-line.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/cool-links-november-2024</guid>
      <title>Cool Links Vol. 5: November, 2024</title>
      <description>Links to the best stuff I&apos;ve read or watched during the month of November, 2024</description>
      <link>https://fantinel.dev/blog/cool-links-november-2024</link>
      <pubDate>Sat, 30 Nov 2024 12:00:00 +0000</pubDate>
      <category>Cool Links</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/cool-links-november-2024">
              read on the site!
            </a>
          </strong>
        </div>

        <p>November has come and gone, and another year is nearing its end. Temperatures are getting higher and higher here, and my city’s started to get empty during the weekends, with everyone taking a trip to the coast to enjoy the ocean.</p>
<p>How about some cool links to kick off this holiday season?</p>
<h2 id="apps-and-fun">Apps and Fun</h2>
<p><strong><a href="https://browser.horse/">Horse Browser</a></strong></p>
<p>(No, it’s not something you use to browse horses. Unless that’s what you want to use an internet browser for.)</p>
<p>I love highly-specialized software that tries to solve a problem without worrying about the “regular” use cases. The Horse Browser seems like a pretty neat thing for people doing researches - it remembers, organizes and allows you to export all the links you click when doing research (or just going down rabbit holes).</p>
<p>I can see it being useful when writing papers or even when trying to look for a solution to a nasty bug. It’s definitely not something you’d want for regular browsing, but that’s fine. There are plenty of other browsers to use for that end.</p>
<p>There’s a <a href="https://www.macstories.net/reviews/horse-browser-tries-its-hooves-at-a-new-take-on-tabs/">neat review of this browser up on MacStories</a> if you’re interested.</p>
<p><strong><a href="https://sill.social/">Sill</a></strong></p>
<p>The best part about Mastodon is the lack of a “For You” algorithm. That means nobody controls what you see (except for Time, I guess, since it’s chronological).</p>
<p>That is also the worst part of it. Sometimes I just can’t keep up with all the posts in there.</p>
<p>Sill is a neat little service that checks your timeline daily and sends you an email with the most shared links in your timeline. It works really well and it’s a neat way to make sure you didn’t miss a really cool project or article from your timeline even if you have skipped social media for a day (which <a href="https://fantinel.dev/jomo-joy-of-missing-out">you should really try</a>). And, at least for now, it’s free!</p>
<p>Oh, it also works with BlueSky, though I haven’t tried it with that.</p>
<p><strong><a href="https://mgx.me/kirby-vs-this-blog-post">kirby vs. this blog post</a></strong></p>
<p>Just a cute, fun and short blog post. What’s not to love?</p>
<h2 id="tech">Tech</h2>
<p><strong><a href="https://notes.ghed.in/posts/2024/selfishness-in-ai/">Selfishness in AI</a>, by Rodrigo Ghedin</strong></p>
<p>Great analysis of how most uses of generative AIs (or at least what companies are trying to sell as use cases) are primarily selfish.</p>
<p>If you can’t bother to do something yourself and instead ask a computer to do it, why should you expect someone to bother reading/watching it?</p>
<p>The corporate use cases for this are somewhat understandable - most content on the web is written for robots, not for people, for example (I know, sad). But Apple has been recently trying to sell it as a way to have a complete emotional detachment from your family as well. We truly live in the worst timeline.</p>
<p><strong><a href="https://pluralistic.net/2024/11/02/ulysses-pact/#tie-yourself-to-a-federated-mast">Bluesky and enshittification</a>, by Cory Doctorow</strong></p>
<p>As Bluesky is gaining millions of users, this is an important reminder that, while it sells itself as something different, it hasn’t proven itself to be yet, and is at a high risk of having the same issues every other commercial platform has - just like the one its new users are coming from.</p>
<h2 id="dev">Dev</h2>
<p><strong><a href="https://www.joshwcomeau.com/css/browser-support/">A Framework for Evaluating Browser Support</a>, by Josh Comeau</strong></p>
<p>Josh has always been one of my favorite bloggers, and this blog post is awesome. You’ve probably seen me talking about <a href="https://fantinel.dev/progressive-enhancement">Progressive Enhancement</a> before, and this article talks about browser support and figuring out how and where to progressively enhance things.</p>
<p><strong><a href="https://youtube.com/watch?v=DNXEORSk4GU">CSS Popover + Anchor Positioning is Magical</a> (video), by Kevin Powell</strong></p>
<p>This is the best explanation of the new HTML/CSS popover API that I’ve seen. It still looks overly complex, mind you, and I’m not sure I like that API. But if you wanna find out about what it is and possible use cases, this video is a nice start!</p>
<p><strong><a href="https://web.dev/articles/fetch-priority">Optimize resource loading with the Fetch Priority API</a></strong></p>
<p>Optimizing the resources your website loads is the best way of making sure it loads faster for everyone. <a href="https://fantinel.dev/web-images-modern-formats">I’ve talked before about ways of doing that</a>, but turns out there’s a new, better way of telling browsers what they should load first!</p>
<p>The article has many examples of use cases, but these are my favorites:</p>
<ul>
<li><strong>Hero images</strong>: “Images inside the viewport typically start at a Low priority. After the layout is complete, Chrome discovers that they’re in the viewport and boosts their priority. This usually adds a significant delay to loading the critical images, like hero images.”</li>
<li><strong>Image Carousels</strong>: “For example, in an image carousel, only the first visible image needs a higher priority, and the others, typically offscreen initially can be set to have lower priority.”</li>
</ul>
<p><strong><a href="https://jvns.ca/blog/2024/11/18/how-to-import-a-javascript-library/">Importing a frontend Javascript library without a build system</a>, by Julian Evans</strong></p>
<p>Nowadays it seems most packages and developers expect you to use a build system like Vite, Webpack, or anything with NodeJS to build websites.</p>
<p>That shouldn’t be true, but if you’re ever building a simple buildless website and want to use packages, Julia Evans explain how to do that and also a bit of how the different kinds of JS modules work.</p>
<p><strong><a href="https://heather-buchel.com/blog/2024/11/carving-space/">Carving your space</a>, by Heather Buchel</strong></p>
<p>Great article reflecting on how job descriptions usually suck and how hard it is to find a job working with the things you want to work on, especially if your expertise is in the <em>gap</em> between two different job descriptions. Turns out the easiest way is to try and carve out a way into the work you do best.</p>
<p><strong><a href="https://github.com/web-platform-dx/baseline-status">baseline-status web component</a></strong></p>
<p>A neat little web component that displays Baseline status of any web feature, that you can quickly add to any web page you want.</p>
<p>I might use this in future blog posts here!</p>
<h2 id="wrapping-up">Wrapping up</h2>
<p>Phew, that was a bunch of links for this month! I’m assuming December won’t have many, since I’m winding down a bit for the end of the year.</p>
<p>I’m starting to prep up my year-in-review post for 2024. These are always my favorite ones to write, as they make me reflect on everything I’ve seen and done this year. It was a good year, so hopefully that will turn into a good post!</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/opengraph/cool-links/Q29vbCUyMExpbmtzJTIwVm9sLiUyMDU=/MjAyNC0xMS0zMFQxMiUzQTAwJTNBMDAuMDAwWg==/image.png"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/opengraph/cool-links/Q29vbCUyMExpbmtzJTIwVm9sLiUyMDU=/MjAyNC0xMS0zMFQxMiUzQTAwJTNBMDAuMDAwWg==/image.png"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/quick-reviews/wicked---part-i</guid>
      <title>Quick Review: Wicked - Part I</title>
      <link>https://fantinel.dev/quick-reviews/wicked---part-i</link>
      <pubDate>Sat, 30 Nov 2024 12:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>Wicked - Part I <br> 2024, Jon M. Chu</p>
        <p>My rating: I like it</p>
        <p><img src="https://m.media-amazon.com/images/M/MV5BOWMwYjYzYmMtMWQ2Ni00NWUwLTg2MzAtYzkzMDBiZDIwOTMwXkEyXkFqcGc@._V1_SX300.jpg" /></p>
        
        <p>Despite being a musical, this is pretty good! Interesting story, right amount of nostalgia, and honestly l even enjoyed some of the musical parts around the halfway point of the movie.</p>
<p>I just thought the final sequence was a bit drawn out so the landing wasn’t perfect. But overall, way better than I expected.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://m.media-amazon.com/images/M/MV5BOWMwYjYzYmMtMWQ2Ni00NWUwLTg2MzAtYzkzMDBiZDIwOTMwXkEyXkFqcGc@._V1_SX300.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://m.media-amazon.com/images/M/MV5BOWMwYjYzYmMtMWQ2Ni00NWUwLTg2MzAtYzkzMDBiZDIwOTMwXkEyXkFqcGc@._V1_SX300.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/quick-reviews/im-still-here-ainda-estou-aqui</guid>
      <title>Quick Review: I&apos;m Still Here (Ainda Estou Aqui)</title>
      <link>https://fantinel.dev/quick-reviews/im-still-here-ainda-estou-aqui</link>
      <pubDate>Mon, 18 Nov 2024 12:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>I'm Still Here (Ainda Estou Aqui) <br> 2024, Walter Salles</p>
        <p>My rating: Loved it!</p>
        <p><img src="https://m.media-amazon.com/images/M/MV5BM2FjMjBiZjgtZDkyYy00YTRlLTk5N2QtODE2ZWIyYWE0Yzg0XkEyXkFqcGc@._V1_SX300.jpg" /></p>
        
        <p>This one hits hard. It’s a brilliantly told true story of just one of the thousands of families destroyed the military dictatorship in Brazil.</p>
<p>Its depiction of Rio de Janeiro in the 70s is very on point. The details make everything much more relatable. It’s a powerful reminder of why we must celebrate and fight to keep the rights we have today.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://m.media-amazon.com/images/M/MV5BM2FjMjBiZjgtZDkyYy00YTRlLTk5N2QtODE2ZWIyYWE0Yzg0XkEyXkFqcGc@._V1_SX300.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://m.media-amazon.com/images/M/MV5BM2FjMjBiZjgtZDkyYy00YTRlLTk5N2QtODE2ZWIyYWE0Yzg0XkEyXkFqcGc@._V1_SX300.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/jomo-joy-of-missing-out</guid>
      <title>JOMO is a Beautiful Thing</title>
      <description>JOMO is the opposite of FOMO. It&apos;s the Joy of Missing Out on stuff that&apos;s bad for you.</description>
      <link>https://fantinel.dev/blog/jomo-joy-of-missing-out</link>
      <pubDate>Wed, 13 Nov 2024 13:58:08 +0000</pubDate>
      <category>Personal</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/jomo-joy-of-missing-out">
              read on the site!
            </a>
          </strong>
        </div>

        <p>One of the greatest weapons modern social platforms have against us is something called FOMO - the Fear of Missing Out. We are inherently social creatures and we want to belong to a group, and to do so we feel like we need to be aware of everything that is going on. We fear we’ll miss out on something important and will be left out of a social context because of that.</p>
<p>That instinct makes sense in an evolutionary context. Our ability to form groups and a society is what made us survive. But our ancient reptilian brain was never ready for the sheer amount of context we have access to on the internet. We can’t keep up, realistically. So that Fear turns into Anxiety.</p>
<p>If our brains can’t handle it, what can we do about it? Well, we can try to be rational.</p>
<p>How much of what you see in social media everyday is actually <em>good</em> for you? How much of it actually helps you build relationships with others? I don’t know about you, but to me it seems like the vast majority of it is actually <em>harmful</em> to both you individually and to your relationships.</p>
<p>There’s no shortage of people suffering from anxiety caused by seeing so many bad news all the time. And no shortage of stories of people saying they stopped talking to their relatives or friends because of something dumb someone posted online. Everyone is so angry all the time and there’s just no way of building a healthy relationship when you’re in that state.</p>
<blockquote>
<p>[!info] <strong>To them, your anger is the goal</strong></p>
<p>It’s always worth remembering that, for commercial social platforms like Facebook, Instagram or Twitter, making you angry is the main goal. Angry people are much more likely to engage with content, which translates to bigger numbers for them, which means more ads and more money.</p>
<p>That means that if you’re fuming with anger at the end of a 15 minute session of scrolling through your feed, you’re not using it wrong. That is exactly what’s intended.</p>
</blockquote>
<p>If bad things are most of what you see, and that erodes both your trust in humanity and your personal relationships, then shouldn’t you be happy if you miss out on it?</p>
<h2 id="the-joy-of-missing-out">The Joy of Missing Out</h2>
<p>What if we try to exercise the opposite emotion when thinking about all that? I’ll give some personal examples.</p>
<p>Recently, in Brazil, there’s been a lot of public discussion around labor laws because the congress is discussing prohibiting making people work on a 6:1 schedule. There’s been so many awful, borderline criminal takes being posted online that it can truly make you lose all your hope in people (I do think most of them are <em>rage baiting</em>, saying something so absurd to make people engage more).</p>
<p>I’m not in any commercial social media platform, and only got exposed to some of the bad takes that burst out of their bubbles. My mind could definitely steer towards the thought that I’m missing out on that discussion, and I could be there to tell these people they’re stupid. But wait… those people are awful and are saying some awful things, and I’m missing out on that! I’m glad I’m missing out, because I really don’t wanna see it.</p>
<p>Even if you’re part of a bubble somewhere that aligns with your own thinking, sometimes it’s good to be able to identify when something’s not good for you too. After the US presidential elections, my Mastodon feed got overwhelmingly negative. I totally empathize with the feeling, but at the same time it was dragging my mood down, and there’s not really anything I could do about it. So I just… muted the topic and tuned down my usage of Mastodon. I chose to miss out on it and my brain was glad I did.</p>
<p>I recognize that ==being able to miss out and just turning off from news and social media like this is a privilege. My right to be myself is not under constant threat.== A lot of the people I interact with can’t say the same, and staying ahead of the news or even just being able to vent is a necessity for them.</p>
<p>But, if you can, I highly recommend trying it out. You don’t have to stay on top of everything that’s happening. In fact, it’s better if you don’t. Keep some space in your mind for things you can learn from other people in an anger-free context. It’s much easier to understand their point of view and have healthy discussions without all that noise.</p>
      ]]></content:encoded>
      
                
    </item>
  
    <item>
      <guid>https://fantinel.dev/quick-reviews/supernatural-seasons-1-5</guid>
      <title>Quick Review: Supernatural (Seasons 1-5)</title>
      <link>https://fantinel.dev/quick-reviews/supernatural-seasons-1-5</link>
      <pubDate>Sun, 03 Nov 2024 12:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>Supernatural (Seasons 1-5) <br> 2005 ~ 2010, Eric Kripke</p>
        <p>My rating: I like it</p>
        <p><img src="https://m.media-amazon.com/images/M/MV5BMDFmMGZmMGItNGRjNC00NjVjLWI5ODEtNzhjMTE5MmJhN2FkXkEyXkFqcGc@._V1_SX300.jpg" /></p>
        
        <p>Probably my favorite show from my childhood, I was wondering if itwould survive a rewatch.</p>
<p>Honestly, it’s not amazing, but it’s fun. It is at its peak in the first two seasons, when the stakes were lower. It gets worse once the story gets too big for its budget. Still, that ending was amazing.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://m.media-amazon.com/images/M/MV5BMDFmMGZmMGItNGRjNC00NjVjLWI5ODEtNzhjMTE5MmJhN2FkXkEyXkFqcGc@._V1_SX300.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://m.media-amazon.com/images/M/MV5BMDFmMGZmMGItNGRjNC00NjVjLWI5ODEtNzhjMTE5MmJhN2FkXkEyXkFqcGc@._V1_SX300.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/cool-links-october-2024</guid>
      <title>Cool Links Vol. 4: October, 2024</title>
      <description>Links to the best stuff I&apos;ve read or watched during the month of October, 2024</description>
      <link>https://fantinel.dev/blog/cool-links-october-2024</link>
      <pubDate>Thu, 31 Oct 2024 12:00:00 +0000</pubDate>
      <category>Cool Links</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/cool-links-october-2024">
              read on the site!
            </a>
          </strong>
        </div>

        <p>🎃 Happy Halloween! 🎃 As the spirits prepare to walk among the living, I bring you some Cool Links to read while enjoying your candy. Or, if you’re in the Southern Hemisphere like me, to read while taking your allergy pills because Spring is in full throttle.</p>
<p>This month’s links are pretty much all web-related, but some are not specific to devs. It’s been a busy month, so I haven’t really had the time to read about more varied topics 😅. Maybe next month!</p>
<h2 id="tech">Tech</h2>
<p><strong><a href="https://pluralistic.net/2024/10/16/keep-it-really-simple-stupid/#read-receipts-are-you-kidding-me-seriously-fuck-that-noise">You should be using an RSS reader</a>, by Cory Doctorow</strong></p>
<p>There’s really nothing else I can add to this argument that Cory hasn’t already said. But really, RSS is the best way to follow a publication or a person there can be. It’s the best way for you to actually have control of what you read and decide when to do so.</p>
<p><strong><a href="https://lmnt.me/blog/nothing-left-to-solve.html">Nothing Left to Solve</a>, by Louie Mantia</strong></p>
<p>I don’t know if you’ve heard, but The Browser Company, the company behind the Arc Browser (which I’ve been using for a couple years as my main browser) has announced that they’ve pretty much given up on monetizing it and will just keep it working as-is in the future, with no new development.</p>
<p>This article reflects a bit on how it seems that tech companies seem to be running out of problems to solve, which results in great products that can’t find their footing (outside of small niches) due to not really changing much about their user’s lives.</p>
<h2 id="dev">Dev</h2>
<p><strong><a href="https://kristoff.it/blog/static-site-paradox/">The Static Site Paradox</a></strong></p>
<p>The web is, at its core, just links to HTML files with some CSS and JS on top. That’s how it was years ago and how it technically still is.</p>
<p>Over the years, things became more complex and many layers of abstraction were introduced. A lot of web developers have never had any real contact with the core of the web.</p>
<p>So we ended up on a place where it’s much easier to find and use an overly-complex, bloated solution for websites than just having the bare minimum (that usually works better).</p>
<p><em>Isn’t that wild?</em> In the author’s words:</p>
<blockquote>
<p>“the web doesn’t belong just to software engineers. The more we make the web complex, the more we push normal users into the enclosures that we like to call social networks.”</p>
</blockquote>
<p>I’ve been a web developer for over 10 years now and I can afford the luxury of building my own website - a static, simple, lean one. People with less or no coding knowledge can’t have that. They need to use tooling that ships so much code and complexity if they want to have their own. Or, even worse - they’ll stick to social media platforms instead. 😱</p>
<p><strong><a href="https://htmlforpeople.com/">HTML for People</a>, by Blake Watson</strong></p>
<p>Building on the previous paradox: did you know that HTML was originally meant to be understandable and writable by anyone?</p>
<p>Before us web devs came along and scared people off, HTML was actually seen as a very simple way of structuring and linking data.</p>
<p>And it still is! This course aims to teach HTML to people with no technical knowledge, so they’re able to build their own simple web pages (or just mess around with existing ones, which is really fun).</p>
<p><strong><a href="https://github.com/1111mp/nvm-desktop">nvm Desktop</a></strong></p>
<p><a href="https://github.com/nvm-sh/nvm">nvm</a> (Node Version Manager) is one of the best tools in my web dev toolkit. It allows you to keep multiple versions of NodeJS installed, which is usually a must if you swap between different projects all day. I’d even say it’s worth it even if you only have one version, since installing NodeJS via nvm is <em>way</em> easier than the official way.</p>
<p>This is a way of using nvm with a graphical interface, in case you’re not too fond of CLIs (or simply prefer a simple GUI instead).</p>
<p><strong><a href="https://2024.stateofcss.com/en-US">State of CSS 2024</a></strong></p>
<p>These “State of” results are always fun, if only for the graphics alone. This one has pretty good data though, as 2024 was one of CSS’s best years, with <a href="https://fantinel.dev/container-queries">Container Queries</a>, :has() (which allows for <a href="https://fantinel.dev/quantity-queries">Quantity Queries</a>), native nesting, native page transitions, and more.</p>
<p><strong><a href="https://svelte.dev/blog/svelte-5-is-alive">Svelte 5 is alive</a></strong></p>
<p>I’ve been growing increasingly unhappy with JavaScript frameworks as of late, but I still have a soft spot for Svelte. Svelte is <a href="blog-development-sveltekit">what my blog is built with</a>, and it’s always been a pleasure to use. Version 5 significantly changes some of its core features, apparently for the better. I’m excited to upgrade eventually (and <em>really glad</em> that they kept backwards compatibility).</p>
<h2 id="wrapping-up">Wrapping up</h2>
<p>This post is going to be on the first edition of my new newsletter! I set it up as a mirror of this blog, at least initially, so people can get notified whenever I write something new.</p>
<p>If you’re interested, you can <a href="https://fantinel.dev/subscribe">subscribe to it</a>. There’s not gonna be any exclusive content in there, so you’re not missing anything if you don’t. The blog is still going to be my main way of publishing.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/opengraph/cool-links/Q29vbCUyMExpbmtzJTIwVm9sLiUyMDQ=/MjAyNC0xMC0zMVQxMiUzQTAwJTNBMDAuMDAwWg==/image.png"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/opengraph/cool-links/Q29vbCUyMExpbmtzJTIwVm9sLiUyMDQ=/MjAyNC0xMC0zMVQxMiUzQTAwJTNBMDAuMDAwWg==/image.png"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/quick-reviews/the-substance</guid>
      <title>Quick Review: The Substance</title>
      <link>https://fantinel.dev/quick-reviews/the-substance</link>
      <pubDate>Fri, 25 Oct 2024 12:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>The Substance <br> 2024, Coralie Fargeat</p>
        <p>My rating: I like it</p>
        <p><img src="https://m.media-amazon.com/images/M/MV5BZDQ1NGE5MGMtYzdlZC00ODExLWJlMDMtNWU4NjA5OWYwMDEwXkEyXkFqcGc@._V1_SX300.jpg" /></p>
        
        <p>What the fuck?</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://m.media-amazon.com/images/M/MV5BZDQ1NGE5MGMtYzdlZC00ODExLWJlMDMtNWU4NjA5OWYwMDEwXkEyXkFqcGc@._V1_SX300.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://m.media-amazon.com/images/M/MV5BZDQ1NGE5MGMtYzdlZC00ODExLWJlMDMtNWU4NjA5OWYwMDEwXkEyXkFqcGc@._V1_SX300.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/quick-reviews/late-night-with-the-devil</guid>
      <title>Quick Review: Late Night with the Devil</title>
      <link>https://fantinel.dev/quick-reviews/late-night-with-the-devil</link>
      <pubDate>Sun, 20 Oct 2024 12:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>Late Night with the Devil <br> 2023, Cameron & Colin Cairnes</p>
        <p>My rating: I like it</p>
        <p><img src="https://m.media-amazon.com/images/M/MV5BYTRiNWZlNGMtOTUwZi00ZjE4LWE1ZjEtNWE4MGQ2ZGU5NDliXkEyXkFqcGc@._V1_SX300.jpg" /></p>
        
        <p>One of the best horror movie setups I’ve ever seen. Its premise of being footage of a 70s talk show works really well and it draws you in really quickly.</p>
<p>No ending could have lived up to the quality of that setup, though. Which made me feel a bit confused and disappointed when it ended. Still a great watch though!</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://m.media-amazon.com/images/M/MV5BYTRiNWZlNGMtOTUwZi00ZjE4LWE1ZjEtNWE4MGQ2ZGU5NDliXkEyXkFqcGc@._V1_SX300.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://m.media-amazon.com/images/M/MV5BYTRiNWZlNGMtOTUwZi00ZjE4LWE1ZjEtNWE4MGQ2ZGU5NDliXkEyXkFqcGc@._V1_SX300.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/cool-links-september-2024</guid>
      <title>Cool Links Vol. 3: September, 2024</title>
      <description>Links to the best stuff I&apos;ve read or watched during the month of September, 2024</description>
      <link>https://fantinel.dev/blog/cool-links-september-2024</link>
      <pubDate>Mon, 30 Sep 2024 12:00:00 +0000</pubDate>
      <category>Cool Links</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/cool-links-september-2024">
              read on the site!
            </a>
          </strong>
        </div>

        <p>Hi there! As September ends, it’s time to wake up for a new edition of Cool Links!</p>
<p>This month’s links are mostly web development related, with a couple ideas on how to build some fun components, a brilliant jab at the current state of commercial web, a jab at React and a jab at Safari. Honestly, it’s a pack of somewhat negative opinions, but presented in a fun way. 😅</p>
<h2 id="fun">Fun</h2>
<p><strong><a href="https://ismy.blue/">Is my blue your blue?</a></strong></p>
<p>I don’t know about you, but I’ve argued with a lot of people in the past over if a specific color was blue or green. This quick test makes you categorize some colors into blue or green, and then how “green” you are compared to the rest of population. I’m not sure if there’s any scientific data to back any of that up, but it’s still a fun test to take.</p>
<p>For the record, my boundary is at hue 171, which makes me greener than 75% of the population.</p>
<p><strong><a href="https://modem.io/blog/blog-monetization/">How to monetize a blog</a>, by Modem.io</strong></p>
<p>This one is absolutely brilliant and nothing I can say about it will make it justice. Open it on desktop for the full effect.</p>
<h2 id="dev">Dev</h2>
<p><strong><a href="https://webventures.rejh.nl/blog/2024/history-of-safari-show-stoppers/">An Abridged History of Safari Showstoppers</a>, by Roderick E.J.H. Gadellaa</strong></p>
<p>I’ve complained about Safari multiple times in my posts and social media, and I’m not alone. This document pretty much aggregates all the issues Safari has been causing for <em>years</em> and how users (and devs) often have no choice of browser to run to when Safari decides it doesn’t want something to work.</p>
<p>I am 100% in favor of browser engine diversity, and definitely don’t want Chromium to be the only option out there. But Safari (and WebKit, its “heart”) is not a fair competitor because the majority of its users (on iOS and iPadOS) don’t have the option of using something else. And when Safari has time and time again had issues with the implementation of new APIs and, even worse, had issues with having old APIs working in a completely different way from other browsers, we have a problem.</p>
<p>And to make it all worse, Apple clearly has financial incentives to make Safari lag behind. A limited web experience means the only way to provide your service to users on iOS is by releasing an app… on Apple’s App Store. They can’t monetize web browsing, so why make it work well?</p>
<p><strong><a href="https://briefs.video/videos/what-is-react/">What is React.JS?</a>, by Heydon Pickering</strong></p>
<p>A semi-serious, semi-satire, but fully incredible short video that talks about ReactJS and how and why it became the most popular web framework. Definitely worth the watch, especially if you don’t like React. Make sure to read the transcript for some added Director’s notes.</p>
<p><strong><a href="https://www.stefanjudis.com/blog/on-being-a-javascript-framework-developer/">On being a “Javascript Framework Developer”…</a> by Stefan Judis</strong></p>
<p>I feel like more and more web developers are becoming “Framework Developers”, which means they only really develop with one specific (or maybe two) JS frameworks. This is a problem because these frameworks often change how you interact with web technology. In the end, everything gets compiled to HTML, CSS and JS, but when your dev environment is so different from that, it’s hard to understand exactly what you’re building.</p>
<p>If you already know HTML, CSS and JS, then you can make that association while you’re learning the framework, and things happen naturally. When the opposite happens, though, the path is more difficult.</p>
<p>A HTML, CSS and JS developer can learn any framework (and jump between them) with relative ease, while a React developer might have a hard time working with Vanilla JS or Svelte, for example, because they only learned the React way of thinking instead of how browsers interpret the code.</p>
<p>I do use a JavaScript framework (Svelte) on my website and personal projects, but I’m glad I learned the underlying technologies before diving into any framework. Having that foundation has allowed me to work professionally with a lot of different stacks (Vanilla, React, Vue, Angular, Svelte) while still keeping my foundational skills intact.</p>
<p><strong><a href="https://codepen.io/argyleink/pen/MWMQJQy">Responsive app switcher or carousel UI with CSS view()</a></strong></p>
<p>This CodePen uses pure CSS and its new-ish features to create a really neat card stacking effect! It uses <a href="https://fantinel.dev/css-scroll-snapping">Scroll Snapping</a> (widely available) and Scroll-based animation (Chromium only for now) to both style the card stack and make sure they behave correctly.</p>
<p><strong><a href="https://9elements.com/blog/building-the-perfect-logo-strip/">Building the Perfect Logo Strip</a>, by Nils Binder</strong></p>
<p>This article dives into how the author built a logo strip (i.e. a grid of company logos with varying widths and heights) and the techniques they used to make their weight nicely distributed, way beyond what most people have done in the past (including me). I’ll definitely be using that next time!</p>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>I hope you enjoyed this month’s selection of Cool Links. See you next month!</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/opengraph/cool-links/Q29vbCUyMExpbmtzJTIwVm9sLiUyMDM=/MjAyNC0wOS0zMFQxMiUzQTAwJTNBMDAuMDAwWg==/image.png"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/opengraph/cool-links/Q29vbCUyMExpbmtzJTIwVm9sLiUyMDM=/MjAyNC0wOS0zMFQxMiUzQTAwJTNBMDAuMDAwWg==/image.png"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/quick-reviews/tár</guid>
      <title>Quick Review: Tár</title>
      <link>https://fantinel.dev/quick-reviews/tár</link>
      <pubDate>Sat, 28 Sep 2024 12:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>Tár <br> 2022, Todd Field</p>
        <p>My rating: I like it</p>
        <p><img src="https://m.media-amazon.com/images/M/MV5BYWY5YThhOGUtNDU4OS00NTk3LWI0ODQtNmRiYTk0ZjVkZWU2XkEyXkFqcGc@._V1_SX300.jpg" /></p>
        
        <p>It’s one of those movies where you spend a good chunk of its duration wondering if it’s going anywhere. But you can’t look away, it draws you in. And then things click.</p>
<p>Amazing acting, photography, and sound. Not easy to understand but worth the effort.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://m.media-amazon.com/images/M/MV5BYWY5YThhOGUtNDU4OS00NTk3LWI0ODQtNmRiYTk0ZjVkZWU2XkEyXkFqcGc@._V1_SX300.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://m.media-amazon.com/images/M/MV5BYWY5YThhOGUtNDU4OS00NTk3LWI0ODQtNmRiYTk0ZjVkZWU2XkEyXkFqcGc@._V1_SX300.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/quick-reviews/beetlejuice-beetlejuice</guid>
      <title>Quick Review: Beetlejuice Beetlejuice</title>
      <link>https://fantinel.dev/quick-reviews/beetlejuice-beetlejuice</link>
      <pubDate>Fri, 20 Sep 2024 12:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>Beetlejuice Beetlejuice <br> 2024, Tim Burton</p>
        <p>My rating: I like it</p>
        <p><img src="https://m.media-amazon.com/images/M/MV5BYmRlYzE3ZTYtNjIyZi00MWYyLWEzZDItOWI5NzM1Yjc0NmI5XkEyXkFqcGc@._V1_SX300.jpg" /></p>
        
        <p>I never watched the first one, but this sequel is a very good time! It’s never afraid to be fun and never takes itself seriously, which is a breath of fresh air nowadays.</p>
<p>It’s far from being a cinema masterpiece, but it’s 105 minutes of pure entertainment. Great soundtrack too!</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://m.media-amazon.com/images/M/MV5BYmRlYzE3ZTYtNjIyZi00MWYyLWEzZDItOWI5NzM1Yjc0NmI5XkEyXkFqcGc@._V1_SX300.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://m.media-amazon.com/images/M/MV5BYmRlYzE3ZTYtNjIyZi00MWYyLWEzZDItOWI5NzM1Yjc0NmI5XkEyXkFqcGc@._V1_SX300.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/quick-reviews/grave-of-the-fireflies</guid>
      <title>Quick Review: Grave of the Fireflies</title>
      <link>https://fantinel.dev/quick-reviews/grave-of-the-fireflies</link>
      <pubDate>Sat, 14 Sep 2024 12:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>Grave of the Fireflies <br> 1988, Isao Takahata</p>
        <p>My rating: Loved it!</p>
        <p><img src="https://m.media-amazon.com/images/M/MV5BNTY5MmE2OGMtN2IxNC00MDY4LTkwMGEtZDUzOTYyNWE0ZTNjXkEyXkFqcGc@._V1_SX300.jpg" /></p>
        
        <p>Just as beautiful as it is sad. What a movie. 🥺</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://m.media-amazon.com/images/M/MV5BNTY5MmE2OGMtN2IxNC00MDY4LTkwMGEtZDUzOTYyNWE0ZTNjXkEyXkFqcGc@._V1_SX300.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://m.media-amazon.com/images/M/MV5BNTY5MmE2OGMtN2IxNC00MDY4LTkwMGEtZDUzOTYyNWE0ZTNjXkEyXkFqcGc@._V1_SX300.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/quick-reviews/oddity</guid>
      <title>Quick Review: Oddity</title>
      <link>https://fantinel.dev/quick-reviews/oddity</link>
      <pubDate>Sat, 07 Sep 2024 12:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>Oddity <br> 2024, Damian Mc Carthy</p>
        <p>My rating: I like it</p>
        <p><img src="https://m.media-amazon.com/images/M/MV5BMWU5Yjc0N2MtZjhlYS00MTM3LWI5YWUtZDg5MzcxZjEyZWJmXkEyXkFqcGc@._V1_SX300.jpg" /></p>
        
        <p>Wow, a horror movie that’s actually good! Really knows how to build up tension, keeping you on the edge of your seat for most of it.</p>
<p>Story is good enough to keep you interested, and it doesn’t feel like it needs to explain everything, which to me is really important to make horror work.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://m.media-amazon.com/images/M/MV5BMWU5Yjc0N2MtZjhlYS00MTM3LWI5YWUtZDg5MzcxZjEyZWJmXkEyXkFqcGc@._V1_SX300.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://m.media-amazon.com/images/M/MV5BMWU5Yjc0N2MtZjhlYS00MTM3LWI5YWUtZDg5MzcxZjEyZWJmXkEyXkFqcGc@._V1_SX300.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/cool-links-august-2024</guid>
      <title>Cool Links Vol. 2: August, 2024</title>
      <description>Links to the best stuff I&apos;ve read or watched during the month of August, 2024</description>
      <link>https://fantinel.dev/blog/cool-links-august-2024</link>
      <pubDate>Sat, 31 Aug 2024 18:10:00 +0000</pubDate>
      <category>Cool Links</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/cool-links-august-2024">
              read on the site!
            </a>
          </strong>
        </div>

        <p>Welcome to the second ever edition of Cool Links!</p>
<p>This was kind of a half-month for me, as I went on vacation on the 18th and didn’t really read anything worthwhile after that. Still, I was able to find some cool reads below.</p>
<p>As a reminder, Cool Links contains the best things that I’ve read during the month, not things that were published in the current month. Some of these articles were actually in my “Read Later” list for a while, actually, and I finally got to them.</p>
<p>So, with no further ado, here they are:</p>
<h2 id="dev">Dev</h2>
<p><strong><a href="https://brr.fyi/posts/engineering-for-slow-internet">Engineering for slow internet</a>, by brr</strong></p>
<p>In case you don’t know this blog, I highly recommend checking other posts out. “brr” is a blog made by an IT worker that worked for a few months in Antarctica, and regularly blogged about some of the unique situations and challenges faced there. They are back home now, and published a post about the challenges faced due to Antarctica’s <em>very</em> limited internet access, and how a lot of stuff we take for granted just wasn’t usable there.</p>
<p>Makes us think about how much code we ship on our apps and websites. Surely there are ways to make our websites work with less and require less resources to run? We never know who, how and where our users might be.</p>
<p><strong><a href="https://miunau.com/posts/dynamic-text-contrast-in-css/">Dynamic text color contrast based on background lightness with CSS/SVG filters</a>, by miunau</strong></p>
<p>If you’ve ever worked with CSS variables on a project, you know one of the main challenges is getting color contrast to work correctly. When the background color changes, the text color (which should have high contrast) might not work as well. Often we handle this by having a <code>--color-contrast</code> variable, but this added complexity builds up quickly.</p>
<p>This article goes into an alternative way that uses CSS and SVG filters to let the browser apply the contrast automatically. The CSS-only alternative has a problem with fringing, but the SVG one seems straightforward enough and gets rid of that issue. That’s pretty clever!</p>
<p>I assume the main drawback of that approach is that you don’t have full control over the text color, so if having an exact color is crucial to you, then you’re still stuck with hardcoding color variables.</p>
<p><strong><a href="https://github-roast.pages.dev/">GitHub Profile Roaster</a></strong></p>
<p>Generative AI has a lot of problems, but it’s really good at creating some fun stuff. This tool asks for a GitHub profile handle and <em>roasts</em> it, using public data from your profile and your public repos. It’s surprisingly good at it… and it might get your feelings hurt.</p>
<p>Here’s a bit of what it says about mine:</p>
<blockquote>
<p>Your website might be “lightweight,” but it seems like the only thing lighter is the impact of your contributions. If I wanted to stumble upon such “high-quality” work, I’d check out my toddler’s finger painting. Keep swinging that magic wand, maybe one day you’ll code something that doesn’t make people cringe!</p>
</blockquote>
<p>I’m okay, I promise 🥲 …</p>
<h2 id="tech">Tech</h2>
<p><strong><a href="https://birchtree.me/blog/is-this-the-slow-decline-of-the-apple-cult/">Is this the slow decline of the Apple “cult”?</a>, by Matt Birchler</strong></p>
<p>This great article goes into how Apple went from being kind of an underdog to becoming the biggest company in the world, and how that made its loyal fans from back in the 2000s kinda loathe the company nowadays, with its greedy actions and overall abuse of market power.</p>
<p>I never had access to any Apple products until ~4 years ago, so I never really got caught in the Apple hype train. Still, that’s what I mostly use nowadays and while I like their products, it’s really hard to have any kind of sympathy for them (which is honestly okay; companies should never be your friends). So, it’s nice reading what someone that was in that hype train feels about all the negative Apple news that are all around us all the time.</p>
<h2 id="mental-health">Mental Health</h2>
<p><strong><a href="https://jason.energy/baseline/">The Baseline: How to Create Long-Term Happiness</a>, by Jason Lengstorf</strong></p>
<p>This fantastic article talks about how we’re often chasing down big goals as a form of reaching happiness, but those big highs often come with big lows right after. What if we aim to make the baseline higher instead of reaching higher highs all the time?</p>
<p>It kinda goes along with <a href="https://fantinel.dev/longterm-goals">what I wrote about longterm goals a few months ago</a>. Not to brag, but I think that article came out really great ;)</p>
<p><strong><a href="https://www.bbc.co.uk/programmes/articles/2T3H8CZHJYjqmkQQP3Jt54N/why-we-should-embrace-being-average">Why we should embrace being average</a>, by Matthew Syed</strong></p>
<p>I read the <a href="https://www.bbc.com/portuguese/articles/ckd090zv40po">Brazilian Portuguese version</a> of this article, and it kind of goes into the same idea as the last one. It talks about how perfectionism can be harmful to one’s self-esteem and how accepting being average allows us to try more things and to live life to the fullest. Perfectionism has nothing to do with achieving success, only with avoiding doing things we’re afraid of not being good at.</p>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>I hope you enjoyed this month’s selection of Cool Links. See you next month!</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/opengraph/cool-links/Q29vbCUyMExpbmtzJTIwVm9sLiUyMDI=/MjAyNC0wOC0zMVQxOCUzQTEwJTNBMDAuMDAwWg==/image.png"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/opengraph/cool-links/Q29vbCUyMExpbmtzJTIwVm9sLiUyMDI=/MjAyNC0wOC0zMVQxOCUzQTEwJTNBMDAuMDAwWg==/image.png"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/vision-pro-first-impressions</guid>
      <title>Apple Vision Pro First Impressions</title>
      <description>Decided to jot down my first impressions of this weird device after trying it out for the first time.</description>
      <link>https://fantinel.dev/blog/vision-pro-first-impressions</link>
      <pubDate>Thu, 29 Aug 2024 08:23:00 +0000</pubDate>
      <category>Misc</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/vision-pro-first-impressions">
              read on the site!
            </a>
          </strong>
        </div>

        <p>While in Chicago and during a massive heat wave, I ended up going inside an Apple Store, mostly looking at a place with A/C to chill for a bit.</p>
<p>In there, a guy was giving out a quick presentation on the Vision Pro and mentioned there being demo slots available, so I thought why not? The demos usually take 30 minutes and it’d be cool to try out this device I’ve heard about but never had the chance to try.</p>
<p>For the record, the Apple Vision Pro is not currently available where I live (Brazil), and its price point ($3500) is a major turn off anyway, so I was never interested in buying it. Still, I like gadgets and was really interested in checking it out.</p>
<p>And… yeah, the wow factor is definitely there! The demo is fully guided and of course they take you through its most impressive features. The immersive videos and “interactive” experiences are really something - it really felt like things were right in front of me, in a not-3D-glasses way. The brain was really struggling to understand some of the things were not real.</p>
<h2 id="controls">Controls</h2>
<p>The Vision Pro has no controller or anything similar, so you basically control and navigate the UI just using your eyes and your hands. You look at something and it will get highlighted (kinda like hovering a mouse cursor over something), and then you pinch 👌with any hand to “click” on stuff.</p>
<p>For me, it worked really well! I’ve seen some people saying it can be hit and miss sometimes, but I felt it quite responsive in my short time using it.</p>
<p>One thing that would probably be an issue though: you have to be looking <em>exactly</em> at where you’re clicking. Which means your eyes have to stay in sync with your hands all the time. This seems natural at first, but once you start trying to do things more quickly, you realize that’s not how you usually do things in other devices.</p>
<p>Try, for example, resizing or moving a window on your computer. Your eyes go to the corner of the window as you drag the mouse cursor there, but before you click, the eyes are already moving to where you want the window to be dragged to. That’s natural, as our eyes are looking for the next thing to do while our hands are still doing the action.</p>
<p>With the Vision Pro, that doesn’t work. Your eyes can’t be faster than your hand, they have to be the exact same speed, otherwise you will be interacting with the wrong thing.</p>
<h2 id="hardware">Hardware</h2>
<p>The headset itself is pretty well made and is built with premium materials, as you’d expect from Apple. Nothing new there. But the first thing you notice when putting it on is that it is <em>heavy</em>. Not heavy enough to stop your head from staying straight, of course, but heavy enough for it to start to weigh down after a while.</p>
<p>After 30 minutes wearing it, I started to feel the weight on my neck, and I had a pretty clear “headset mark” on my face. I didn’t get any problem with my eyes, though I imagine they would get irritated more easily than with regular screens, since they’re pretty much glued to your face.</p>
<p>Which means, I don’t seriously think you can wear the Vision Pro for long periods of time. And I think Apple knows this, since they went for a 2h battery only. Good for a movie maybe, bad for a work session. And speaking of work…</p>
<h2 id="use-cases">Use Cases</h2>
<p>Apple did showcase some hypothetical work situations in their Vision Pro videos, but it’s very hard to imagine someone using it for work seriously. It is technically impressive, sure, and some “what ifs” are really exciting to think about.</p>
<p>But when doing actual work, you want reliability, speed and flexibility. I don’t think it provides any of those.</p>
<p>It’s not reliable or fast because of the control issues I mentioned before. It feels like your mind has to slow down to catch up with the Vision Pro, whereas it feels like computers have caught up in speed with our minds (or did we just get used to them?).</p>
<p>It’s not flexible because, in the end, it runs another closed-down platform, VisionOS, which faces the same issues the iPadOS has. Great if all you do is use App Store stuff, but whenever you need something else that Apple doesn’t allow for any reason (like a clipboard manager or any tweaks to the OS itself), then you’re out of luck. There’s a reason the Mac is still <em>the</em> work OS for most.</p>
<p>Speaking of Mac, you can actually sync it with the Vision Pro and use it as an external monitor, in which you can make the screen as big as you want. I wasn’t able to see that on the demo, but honestly, even if it’s good I doubt it’s $3500 good.</p>
<h2 id="wrapping-up">Wrapping up</h2>
<p>To sum up my first impressions, I think it’s technically amazing, and the nerd side of me was pretty excited using it. It’s a great entertainment device (as long as you’re ok only watching what’s available), although a lonely one.</p>
<p>But yeah, that price point just makes it hard to be positive about it 😬</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/cms/media/articles/vision-pro-first-impressions.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/cms/media/articles/vision-pro-first-impressions.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/roleplaying-in-orgrimmar</guid>
      <title>Roleplaying in Orgrimmar</title>
      <description>Who would have thought that interacting with people on a multiplayer game could be fun?</description>
      <link>https://fantinel.dev/blog/roleplaying-in-orgrimmar</link>
      <pubDate>Sun, 11 Aug 2024 22:39:00 +0000</pubDate>
      <category>Games</category><category>Personal</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/roleplaying-in-orgrimmar">
              read on the site!
            </a>
          </strong>
        </div>

        <p>Last night, I spent a few hours playing World of Warcraft with my wife. It’s an old game, so it has a lot of things to do and content to experience.</p>
<p>Still, we spent a lot of time just sitting around Orgrimmar, chatting with other players. We play on a Roleplaying server, which means that in some specific spots of the world, people actually do the <em>roleplaying</em> part of a <em>roleplaying game</em>.</p>
<p>And it’s so fun! While not as flashy as the big combat encounters and epic story bits, it feels like these little social interactions while pretending to be your character are way more fun and personal.</p>
<p>In the 20 years since the game’s release, the social aspects of this social game have been reduced considerably, and you can pretty much do everything without having to talk to a single soul in the game.</p>
<p>That makes sense if you consider the game’s audience has grown up and doesn’t have a lot of time to play and interact, and/or our increasingly shorter attention spans can’t handle spending hours looking for people to do something with us.</p>
<p>Still, I’m glad there’s still opportunity for players that want to dive into the game a bit more to do that and find each other. I’m a huge lore nerd and love any opportunity to feel like I’m a part of that world. Thanks to Braldov, the Tauren, and Moralus, the Zandalari, for a fun night!</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/cms/media/articles/roleplaying-in-orgrimmar.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/cms/media/articles/roleplaying-in-orgrimmar.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/quick-reviews/warcraft</guid>
      <title>Quick Review: Warcraft</title>
      <link>https://fantinel.dev/quick-reviews/warcraft</link>
      <pubDate>Sun, 11 Aug 2024 12:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>Warcraft <br> 2016, Duncan Jones</p>
        <p>My rating: Decent</p>
        <p><img src="https://m.media-amazon.com/images/M/MV5BMjIwNTM0Mzc5MV5BMl5BanBnXkFtZTgwMDk5NDU1ODE@._V1_SX300.jpg" /></p>
        
        <p>As a longtime Warcraft fan, I watched this back when it came out, and finally rewatched it now. I remember being fascinated by seeing the Azeroth I knew so well on the big screen, but once that feeling goes away, it’s just an okay film.</p>
<p>It feels rushed and gives the feeling that whoever was involved in making it wasn’t a fan of the games.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://m.media-amazon.com/images/M/MV5BMjIwNTM0Mzc5MV5BMl5BanBnXkFtZTgwMDk5NDU1ODE@._V1_SX300.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://m.media-amazon.com/images/M/MV5BMjIwNTM0Mzc5MV5BMl5BanBnXkFtZTgwMDk5NDU1ODE@._V1_SX300.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/quick-reviews/the-zone-of-interest</guid>
      <title>Quick Review: The Zone of Interest</title>
      <link>https://fantinel.dev/quick-reviews/the-zone-of-interest</link>
      <pubDate>Sun, 04 Aug 2024 12:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>The Zone of Interest <br> 2023, Jonathan Glazer</p>
        <p>My rating: Didn't like it</p>
        <p><img src="https://m.media-amazon.com/images/M/MV5BNjk4MTQ3MGEtZmI4OS00N2NhLWI5ZjgtYWE5ZWVkZGExYTQ1XkEyXkFqcGc@._V1_SX300.jpg" /></p>
        
        <p>Everything about this movie is great. Atmosphere, acting, sounds, and how it plays with historical context. It’s meant to make you uncomfortable, and it succeeds in doing so.</p>
<p>Too bad it just didn’t hit me the way I think it should have. I get why it should have been great, it just wasn’t for me.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://m.media-amazon.com/images/M/MV5BNjk4MTQ3MGEtZmI4OS00N2NhLWI5ZjgtYWE5ZWVkZGExYTQ1XkEyXkFqcGc@._V1_SX300.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://m.media-amazon.com/images/M/MV5BNjk4MTQ3MGEtZmI4OS00N2NhLWI5ZjgtYWE5ZWVkZGExYTQ1XkEyXkFqcGc@._V1_SX300.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/cool-links-july-2024</guid>
      <title>Cool Links Vol. 1: July, 2024</title>
      <description>Links to the best stuff I&apos;ve read or watched during the month of July, 2024</description>
      <link>https://fantinel.dev/blog/cool-links-july-2024</link>
      <pubDate>Wed, 31 Jul 2024 21:36:32 +0000</pubDate>
      <category>Cool Links</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/cool-links-july-2024">
              read on the site!
            </a>
          </strong>
        </div>

        <p>Welcome to the first edition of Cool Links™! (not really trademarked)</p>
<p>The idea here is to post links to cool stuff I’ve read or watched every month. It is part of an effort I’m making into writing more and more often. Posting these links gives me the opportunity to comment on them, which feels less intimidating than writing a full blog post. Plus, it’s an opportunity to link to the work of other writers and keep the spirit of the IndieWeb going.</p>
<p>This year I’ve also started posting some articles with the “Reading Recs” category to be able to comment on them. These Cool Links will replace some of those, but might still show up in cases where I have a lot to say about them.</p>
<p>So, let’s go to this month’s Cool Links:</p>
<h2 id="design">Design</h2>
<p><strong><a href="https://www.lukew.com/ff/entry.asp?2066=">Different Gets Ignored</a>, by Luke Wroblewski</strong></p>
<p>This article talks about how users are growing increasingly indifferent to elements that “pop” on websites and apps. Because “⁠if a pop-up or any element of a user interface for that matter looks too different from the rest of the design, people will often perceive it as something that doesn’t belong (like an ad) and dismiss it.”.</p>
<p>Instead, making the important elements feel like the core flow tends to yield better results.</p>
<p><strong><a href="https://builtformars.com/case-studies/monzo-perks">Why users are ignoring your features</a>, by Built for Mars</strong></p>
<p>This case study (in the form of a slide presentation) has amazing insight into how having too many features can backfire. It goes through many design concepts and practical examples onto how you can make users take notice and actually use the features you work so hard to build.</p>
<p><strong><a href="https://growth.design/case-studies/been-onboarding">How small UI delighters have a huge impact on UX</a>, by Growth Design</strong></p>
<p>Another case study; this one shows how apps that are great ideas can become a disappointment when they don’t know how to display those good ideas correctly. And how good (and fun) design can make all the difference on getting users on board.</p>
<h2 id="ai">AI</h2>
<p><strong><a href="https://www.macstories.net/stories/ai-companies-need-to-be-regulated-an-open-letter-to-the-u-s-congress-and-european-parliament/">AI Companies Need to Be Regulated: An Open Letter to the U.S. Congress and European Parliament</a>, by MacStories</strong></p>
<p>The MacStories team was able to put into words what a lot of people (including me) are feeling. AI companies that scrape content on the web (ignoring its licenses) pose a big threat to websites that need the pageviews to keep the lights on. Right now, that is all done under the claim that they’re using the data to train their models and not reproducing the content directly, which would fit into “fair use”. But there are good arguments that it’s not true.</p>
<p><strong><a href="https://www.nature.com/articles/s41586-024-07566-y">AI models collapse when trained on recursively generated data</a></strong></p>
<p>This study shows that, predictably, generative AIs or LLMs tend to decline in quality as they start feeding on content that was generated by other LLMs instead of humans.</p>
<p>Considering much of the web is now getting polluted by this LLM-generated slop and the web is a big source of data for their training, it seems that future models will likely regress in quality. Doesn’t seem like a very sustainable model, does it?</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/opengraph/cool-links/Q29vbCUyMExpbmtzJTIwVm9sLiUyMDE=/MjAyNC0wNy0zMVQyMSUzQTM2JTNBMzIuMzk4Wg==/image.png"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/opengraph/cool-links/Q29vbCUyMExpbmtzJTIwVm9sLiUyMDE=/MjAyNC0wNy0zMVQyMSUzQTM2JTNBMzIuMzk4Wg==/image.png"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/quick-reviews/maxxxine</guid>
      <title>Quick Review: MaXXXine</title>
      <link>https://fantinel.dev/quick-reviews/maxxxine</link>
      <pubDate>Fri, 19 Jul 2024 12:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>MaXXXine <br> 2024, Ti West</p>
        <p>My rating: Decent</p>
        <p><img src="https://m.media-amazon.com/images/M/MV5BNjBmYzFmODktNDIyZC00NWFmLTk2NTctYmZiY2E2OTA2OTc0XkEyXkFqcGc@._V1_SX300.jpg" /></p>
        
        <p>The conclusion to the trilogy that started with ‘X’, this one now takes place in the 80s and has a lot of the tropes of the movies of that era.</p>
<p>The ambiance, references and (again) Mia Goth’s acting really bring you into the movie. It’s a shame the climax is unable to match the same level of quality, leading to a quite confusing ending.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://m.media-amazon.com/images/M/MV5BNjBmYzFmODktNDIyZC00NWFmLTk2NTctYmZiY2E2OTA2OTc0XkEyXkFqcGc@._V1_SX300.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://m.media-amazon.com/images/M/MV5BNjBmYzFmODktNDIyZC00NWFmLTk2NTctYmZiY2E2OTA2OTc0XkEyXkFqcGc@._V1_SX300.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/quick-reviews/pearl</guid>
      <title>Quick Review: Pearl</title>
      <link>https://fantinel.dev/quick-reviews/pearl</link>
      <pubDate>Thu, 18 Jul 2024 12:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>Pearl <br> 2022, Ti West</p>
        <p>My rating: I like it</p>
        <p><img src="https://m.media-amazon.com/images/M/MV5BNzk0Y2RlZjItZDUzYi00Y2JhLTk5MzMtNjgzYWFmZmUxN2FmXkEyXkFqcGc@._V1_SX300.jpg" /></p>
        
        <p>Amazing movie from start to finish. Pearl is an incredible character and even though you already have an idea of what’s going to happen (because this is a prequel to ‘X’), the way the story is told just keeps your eyes glued to the screen.</p>
<p>Mia Goth’s acting is nothing short of amazing, too. It might actually be the best part of the movie!</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://m.media-amazon.com/images/M/MV5BNzk0Y2RlZjItZDUzYi00Y2JhLTk5MzMtNjgzYWFmZmUxN2FmXkEyXkFqcGc@._V1_SX300.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://m.media-amazon.com/images/M/MV5BNzk0Y2RlZjItZDUzYi00Y2JhLTk5MzMtNjgzYWFmZmUxN2FmXkEyXkFqcGc@._V1_SX300.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/quick-reviews/x</guid>
      <title>Quick Review: X</title>
      <link>https://fantinel.dev/quick-reviews/x</link>
      <pubDate>Wed, 17 Jul 2024 12:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>X <br> 2022, Ti West</p>
        <p>My rating: Decent</p>
        <p><img src="https://m.media-amazon.com/images/M/MV5BNzQ2MzI5NjQtNmZjZC00ODFhLWE2MjQtNWE0NjljODc0ZGM3XkEyXkFqcGc@._V1_SX300.jpg" /></p>
        
        <p>I think this movie might be much more enjoyable for people who love cinema history, based on the amount of references in it and how much it goes for the 1970s slasher horror movie vibes.</p>
<p>For me, it was entertaining enough to keep me watching, but nothing really memorable.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://m.media-amazon.com/images/M/MV5BNzQ2MzI5NjQtNmZjZC00ODFhLWE2MjQtNWE0NjljODc0ZGM3XkEyXkFqcGc@._V1_SX300.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://m.media-amazon.com/images/M/MV5BNzQ2MzI5NjQtNmZjZC00ODFhLWE2MjQtNWE0NjljODc0ZGM3XkEyXkFqcGc@._V1_SX300.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/quick-reviews/bacurau</guid>
      <title>Quick Review: Bacurau</title>
      <link>https://fantinel.dev/quick-reviews/bacurau</link>
      <pubDate>Sat, 06 Jul 2024 12:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>Bacurau <br> 2019, Kleber Mendonça Filho</p>
        <p>My rating: Loved it!</p>
        <p><img src="https://m.media-amazon.com/images/M/MV5BNDE4OTMyOGQtODNiNy00YjIzLWFjNTAtMmY2Yjg3ZWJiY2YxXkEyXkFqcGc@._V1_SX300.jpg" /></p>
        
        <p>It took me way too long to watch this movie, but I’m so glad I was finally able to watch it.</p>
<p>Absolutely incredible, raw, weird and full of symbolisms that will keep me thinking about the movie for days. Another masterpiece in Brazilian cinema.
Have you watched Bacurau?</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://m.media-amazon.com/images/M/MV5BNDE4OTMyOGQtODNiNy00YjIzLWFjNTAtMmY2Yjg3ZWJiY2YxXkEyXkFqcGc@._V1_SX300.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://m.media-amazon.com/images/M/MV5BNDE4OTMyOGQtODNiNy00YjIzLWFjNTAtMmY2Yjg3ZWJiY2YxXkEyXkFqcGc@._V1_SX300.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/quick-reviews/inside-out-2</guid>
      <title>Quick Review: Inside Out 2</title>
      <link>https://fantinel.dev/quick-reviews/inside-out-2</link>
      <pubDate>Mon, 01 Jul 2024 12:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>Inside Out 2 <br> 2024, Kelsey Mann</p>
        <p>My rating: Loved it!</p>
        <p><img src="https://m.media-amazon.com/images/M/MV5BYWY3MDE2Y2UtOTE3Zi00MGUzLTg2MTItZjE1ZWVkMGVlODRmXkEyXkFqcGc@._V1_SX300.jpg" /></p>
        
        <p>You can feel all sorts of emotions on this one
(hah!), but the main one is joy. Amazing movie, funny, relatable, touches on very important topics with good humor and a lot of sensibility.</p>
<p>Props to the Brazilian dubbing and localization which was amazing and probably made the movie even better than it originally was! 😉</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://m.media-amazon.com/images/M/MV5BYWY3MDE2Y2UtOTE3Zi00MGUzLTg2MTItZjE1ZWVkMGVlODRmXkEyXkFqcGc@._V1_SX300.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://m.media-amazon.com/images/M/MV5BYWY3MDE2Y2UtOTE3Zi00MGUzLTg2MTItZjE1ZWVkMGVlODRmXkEyXkFqcGc@._V1_SX300.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/quantity-queries</guid>
      <title>CSS “Quantity Queries” are a thing now</title>
      <description>With the :has selector available everywhere now, there&apos;s a neat way of styling elements depending on how many elements are inside them.</description>
      <link>https://fantinel.dev/blog/quantity-queries</link>
      <pubDate>Tue, 18 Jun 2024 21:45:00 +0000</pubDate>
      <category>Front-End</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/quantity-queries">
              read on the site!
            </a>
          </strong>
        </div>

        <p>I was recently working on some form styling for a project and the design had this example of a group of checkboxes that spread out in 3 columns:</p>
<p><img src="https://fantinel.dev/cms/media/articles/quantity-queries/checkboxes-3-columns.png" alt="Screenshot of a grid of checkboxes with 3 columns and 3 rows"></p>
<p>That’s pretty simple to do, right? Just add <code>display: grid</code> to the parent and set <code>grid-template-columns: 1fr 1fr 1fr</code>.</p>
<p>However, things don’t look so good when there’s only 3 checkboxes:</p>
<p><img src="https://fantinel.dev/cms/media/articles/quantity-queries/checkboxes-3-columns-2.png" alt="Screenshot of a grid of checkboxes with 3 columns and a single row"></p>
<p>When there’s only a few choices, they look better and are easier to read if displayed vertically. So, if there are only 3 items, ideally <code>grid-template-columns</code> would only define one column. But how can we hot-swap between these rules without doing some hacky server-side/JS tweak?</p>
<h2 id="has-to-the-rescue">:has() to the rescue</h2>
<p>The <code>:has()</code> CSS selector has been available on all major browsers since December 2023 (Firefox was the last to implement it), and is often called the “Parent selector”. With it, you’re able to select an element based on what it contains. For example, you can select every <code>p</code> element with some bold text (<code>strong</code>) inside by doing <code>p:has(strong)</code>. That’s already quite useful, but the neat thing is that you can use more complex selectors inside it as well.</p>
<p>We have the <code>:nth-child</code> selector that usually allows us to select every nth element and then do something with it. So, let’s say that in our checkbox example above, we want to make the color of the fifth checkbox red. We could do something like:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="css"><code><span class="line"><span style="color:#B392F0">.group</span><span style="color:#B392F0"> .checkbox:nth-child</span><span style="color:#E1E4E8">(</span><span style="color:#79B8FF">5</span><span style="color:#E1E4E8">) {</span></span>
<span class="line"><span style="color:#79B8FF">	color</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">red</span><span style="color:#E1E4E8">;</span></span>
<span class="line"><span style="color:#E1E4E8">}</span></span></code></pre>
<p>But what if we could mix both? That <code>:nth-child</code> selector would only return something if there is an element at index 5… so, if we put that inside a <code>:has</code> selector, we could select the parent of that 5th item instead… Eureka!</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="css"><code><span class="line"><span style="color:#B392F0">.group:has</span><span style="color:#E1E4E8">(</span><span style="color:#B392F0">.checkbox:nth-child</span><span style="color:#E1E4E8">(</span><span style="color:#79B8FF">5</span><span style="color:#E1E4E8">)) {</span></span>
<span class="line"><span style="color:#6A737D">	/*</span></span>
<span class="line"><span style="color:#6A737D">		Styles here will only apply if a 5th checkbox exists inside .group</span></span>
<span class="line"><span style="color:#6A737D">		i.e. there are > 5 checkboxes</span></span>
<span class="line"><span style="color:#6A737D">	*/</span></span>
<span class="line"><span style="color:#E1E4E8">}</span></span></code></pre>
<p>Amazing! So, with that, we’re able to do something like:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="css"><code><span class="line"><span style="color:#B392F0">.group</span><span style="color:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#79B8FF">	display</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">grid</span><span style="color:#E1E4E8">;</span></span>
<span class="line"><span style="color:#79B8FF">	grid-template-columns</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">1</span><span style="color:#F97583">fr</span></span>
<span class="line"><span style="color:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#B392F0">.group:has</span><span style="color:#E1E4E8">(</span><span style="color:#B392F0">.checkbox:nth-child</span><span style="color:#E1E4E8">(</span><span style="color:#79B8FF">5</span><span style="color:#E1E4E8">)) {</span></span>
<span class="line"><span style="color:#6A737D">	/*</span></span>
<span class="line"><span style="color:#6A737D">		If it has at least 5 checkboxes, make it 2 columns</span></span>
<span class="line"><span style="color:#6A737D">	*/</span></span>
<span class="line"><span style="color:#79B8FF">	grid-template-columns</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">1</span><span style="color:#F97583">fr</span><span style="color:#79B8FF"> 1</span><span style="color:#F97583">fr</span><span style="color:#E1E4E8">;</span></span>
<span class="line"><span style="color:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#B392F0">.group:has</span><span style="color:#E1E4E8">(</span><span style="color:#B392F0">.checkbox:nth-child</span><span style="color:#E1E4E8">(</span><span style="color:#79B8FF">7</span><span style="color:#E1E4E8">)) {</span></span>
<span class="line"><span style="color:#6A737D">	/*</span></span>
<span class="line"><span style="color:#6A737D">		And 3 columns if there's at least 7 checkboxes</span></span>
<span class="line"><span style="color:#6A737D">	*/</span></span>
<span class="line"><span style="color:#79B8FF">	grid-template-columns</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">1</span><span style="color:#F97583">fr</span><span style="color:#79B8FF"> 1</span><span style="color:#F97583">fr</span><span style="color:#79B8FF"> 1</span><span style="color:#F97583">fr</span><span style="color:#E1E4E8">;</span></span>
<span class="line"><span style="color:#E1E4E8">}</span></span></code></pre>
<h2 id="progressive-enhancement">Progressive Enhancement</h2>
<p><em>(I really like talking about <a href="https://fantinel.dev/progressive-enhancement">progressive enhancement</a>)</em></p>
<p>So, there’s a very real possibility that some of your users won’t be able to see it as you intended because their browser does not support <code>:has</code> yet. It’s sad, but it’s true. This is where you have to make a decision. In those cases where <code>:has</code> isn’t supported, what do you want to happen?</p>
<ul>
<li><strong>Do things look broken? Is the UX greatly affected?</strong> If yes, then you will have to figure out an alternative for those specific users. You can use <code>@supports selector(:has(*))</code> (for when browser supports it) and <code>@supports not selector(:has(*))</code> (for when it doesn’t), and apply fallback styles accordingly. In that checkbox columns example, perhaps a good fallback would be just default to 2 columns instead.</li>
<li><strong>If nothing looks broken, just not ideal</strong>, then you can safely leave it as is. No errors will be thrown if you use <code>:has</code> and the browser doesn’t know what it is. In that checkbox example, I believe nothing would look broken if I just kept it at 1 column. Then, if a user’s browser supports it, the website gets <em>enhanced</em>.</li>
</ul>
<h2 id="wrapping-up">Wrapping up</h2>
<p>So, just to wrap up that checkboxes example, you can also sprinkle some <a href="https://fantinel.dev/container-queries">container queries</a> in there to make everything responsive! Here was my end result (you can <a href="https://codepen.io/matfantinel/pen/gOJvxyE">check it out on CodePen</a> as well:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="css"><code><span class="line"><span style="color:#B392F0">.group</span><span style="color:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#79B8FF">	display</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">grid</span><span style="color:#E1E4E8">;</span></span>
<span class="line"><span style="color:#79B8FF">	grid-template-columns</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">1</span><span style="color:#F97583">fr</span></span>
<span class="line"><span style="color:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#B392F0">.group:has</span><span style="color:#E1E4E8">(</span><span style="color:#B392F0">.checkbox:nth-child</span><span style="color:#E1E4E8">(</span><span style="color:#79B8FF">5</span><span style="color:#E1E4E8">)) {</span></span>
<span class="line"><span style="color:#6A737D">	/*</span></span>
<span class="line"><span style="color:#6A737D">		If it has at least 5 checkboxes, make it 2 columns</span></span>
<span class="line"><span style="color:#6A737D">  </span></span>
<span class="line"><span style="color:#6A737D">	    But only if there's enough space for it</span></span>
<span class="line"><span style="color:#6A737D">	*/</span></span>
<span class="line"><span style="color:#E1E4E8">  @</span><span style="color:#79B8FF">container</span><span style="color:#E1E4E8"> (</span><span style="color:#79B8FF">min-width</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">350</span><span style="color:#F97583">px</span><span style="color:#E1E4E8">) {</span></span>
<span class="line"><span style="color:#79B8FF">	  grid-template-columns</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">1</span><span style="color:#F97583">fr</span><span style="color:#79B8FF"> 1</span><span style="color:#F97583">fr</span><span style="color:#E1E4E8">;</span></span>
<span class="line"><span style="color:#E1E4E8">  }</span></span>
<span class="line"><span style="color:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#B392F0">.group:has</span><span style="color:#E1E4E8">(</span><span style="color:#B392F0">.checkbox:nth-child</span><span style="color:#E1E4E8">(</span><span style="color:#79B8FF">7</span><span style="color:#E1E4E8">)) {</span></span>
<span class="line"><span style="color:#6A737D">	/*</span></span>
<span class="line"><span style="color:#6A737D">		And 3 columns if there's at least 7 checkboxes</span></span>
<span class="line"><span style="color:#6A737D">	*/</span></span>
<span class="line"><span style="color:#E1E4E8">  </span></span>
<span class="line"><span style="color:#E1E4E8">    @</span><span style="color:#79B8FF">container</span><span style="color:#E1E4E8"> (</span><span style="color:#79B8FF">min-width</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">600</span><span style="color:#F97583">px</span><span style="color:#E1E4E8">) {</span></span>
<span class="line"><span style="color:#79B8FF">    	grid-template-columns</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">1</span><span style="color:#F97583">fr</span><span style="color:#79B8FF"> 1</span><span style="color:#F97583">fr</span><span style="color:#79B8FF"> 1</span><span style="color:#F97583">fr</span><span style="color:#E1E4E8">;</span></span>
<span class="line"><span style="color:#E1E4E8">    }</span></span>
<span class="line"><span style="color:#E1E4E8">}</span></span></code></pre>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/cms/media/articles/quantity-queries/cover.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/cms/media/articles/quantity-queries/cover.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/separating-content-from-code</guid>
      <title>Separating my website&apos;s content from its code</title>
      <description>Having an open source website is great, but having the content stored in the same spot as the code has some issues. On this post I walk through what I did to keep them separated.</description>
      <link>https://fantinel.dev/blog/separating-content-from-code</link>
      <pubDate>Sun, 16 Jun 2024 22:17:00 +0000</pubDate>
      <category>Meta</category><category>Dev</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/separating-content-from-code">
              read on the site!
            </a>
          </strong>
        </div>

        <p>My website has been open source since the beginning. Being open was the most important goal for me back when I built it (and still is now), for many reasons:</p>
<ul>
<li>Open source websites and projects were/are crucial for me when learning how to build things;</li>
<li>I wanted my website to do the same for other devs (it has!);</li>
<li>It’s good as a personal project for a portfolio; You can see its code to see how I work and maybe understand a bit of how my coding brain works.</li>
</ul>
<p>Pretty much since its inception some people have taken interest on the website, forked it, made changes to it, or maybe just dug through the code to see how I did things. That’s great!</p>
<p>Unfortunately, there are some people that clone the website and then just remove my name, while keeping all the content on it. That’s not very nice.</p>
<p>I’m not particularly knowledgeable (or interested, to be honest) in how all that works legally. Technically that content was part of the code, right? So it’s probably not illegal or anything, just not nice.</p>
<p>Which is why I decided to remove the content (blog posts, work experience, resume) out of the main website repo and into a different (and private) one.</p>
<h2 id="git-submodules">Git submodules</h2>
<p>To do that, I figured the simplest way would be to make use of <a href="https://github.blog/2016-02-01-working-with-submodules/">Git submodules</a>, so that the main repo would point to this separate repo, and then use some custom CI code that would automatically fetch updates in that content repo and update my website whenever something new was published.</p>
<p>I started by creating a new <em>private</em> repo that I aptly called <em>FantinelCMS</em>, with just an empty README.</p>
<p>Then, on the main website repo, I ran this git command to add a new submodule in the “static/cms” folder:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="shell"><code><span class="line"><span style="color:#B392F0">git</span><span style="color:#9ECBFF"> submodule</span><span style="color:#9ECBFF"> add</span><span style="color:#9ECBFF"> git@github.com/org-name/repo-name</span><span style="color:#9ECBFF"> static/cms</span><span style="color:#79B8FF"> -b</span><span style="color:#9ECBFF"> main</span></span></code></pre>
<blockquote>
<p>[!info]
I use SvelteKit, and I am putting the “cms” folder inside the “static” folder because it will contain images (the ones used on blog posts), which need to be inside the “static” folder in order to be publicly accessible.</p>
</blockquote>
<p>This set up a new git repo inside that folder and pulled down that empty README. So far, so good. Then, I copied over the content that was in the website repo into that newly created folder and committed it. I ran the project locally, and it was finding all the posts and stuff. Neat!</p>
<h2 id="deploying-somewhere">Deploying somewhere</h2>
<p>Now, this part might vary depending on where you host your website. I host it on Vercel, and I unfortunately found out that <a href="https://github.com/orgs/vercel/discussions/44">it does not support private git submodules by default</a>. Bummer, but fortunately I found <a href="https://github.com/junhoyeo/vercel-submodules">a 3rd-party project</a> that provides a quick command to fix for that. You just need to call that script on the “<strong>Install Command</strong>” field in the settings for your project in Vercel, and it does the magic for you.</p>
<h2 id="automating-updates">Automating updates</h2>
<p>Today, whenever I commit something to my website repo, it automatically gets deployed to Vercel in less than 2 minutes. However, with the content on a separate repo, that’s no longer the case. Since the updates themselves are on a different repo, the website one does not know that it should update the content.</p>
<p>I solved this by setting up <a href="https://stackoverflow.com/a/68213855/6604030">a new GitHub Action</a> on my content repo that runs whenever the content gets updated. It will then update the website repo and commit changes to it, firing the previously-existing CI process. It worked pretty well!</p>
<h2 id="how-well-does-it-work">How well does it work?</h2>
<p>Well, if you’re seeing this post, it means @@it worked well!@@ I actually wrote this post on Obsidian, a separate app that only handles Markdown (.md) files, and is totally detached from the website code. (I intend to write about this setup in the near future).</p>
<p>Hopefully this makes it easier for people to fork the website (though, if you do, I highly recommend <a href="https://github.com/matfantinel/sveltekit-static-blog-template">using this separate template instead</a>) without having the work to take out my stuff from it. There’s still some content here and there that’s not being fed from the cms folder (like the home page), but I also plan on tackling that soon.</p>
      ]]></content:encoded>
      
                
    </item>
  
    <item>
      <guid>https://fantinel.dev/quick-reviews/anatomy-of-a-fall</guid>
      <title>Quick Review: Anatomy of a Fall</title>
      <link>https://fantinel.dev/quick-reviews/anatomy-of-a-fall</link>
      <pubDate>Sun, 16 Jun 2024 12:00:00 +0000</pubDate>
      <content:encoded><![CDATA[
        <p>Anatomy of a Fall <br> 2023, Justine Triet</p>
        <p>My rating: Loved it!</p>
        <p><img src="https://m.media-amazon.com/images/M/MV5BYTk3MjFkZmQtMThiYi00N2JhLTk2YzItZTE5ZGUxZTIyZDVkXkEyXkFqcGc@._V1_SX300.jpg" /></p>
        
        <p>Mesmerizing movie that hooked me from start to finish. Might not be for everyone, but I just love when movies don’t give you all the answers and make you think about what happened. It makes you part of the story in away.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://m.media-amazon.com/images/M/MV5BYTk3MjFkZmQtMThiYi00N2JhLTk2YzItZTE5ZGUxZTIyZDVkXkEyXkFqcGc@._V1_SX300.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://m.media-amazon.com/images/M/MV5BYTk3MjFkZmQtMThiYi00N2JhLTk2YzItZTE5ZGUxZTIyZDVkXkEyXkFqcGc@._V1_SX300.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/longterm-goals</guid>
      <title>Best longterm goal? Not having one</title>
      <description>I consider myself a pretty happy person. I do think a lot of it comes from me never really expecting much from myself. That&apos;s not as bad as it sounds.</description>
      <link>https://fantinel.dev/blog/longterm-goals</link>
      <pubDate>Wed, 29 May 2024 21:12:41 +0000</pubDate>
      <category>Personal</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/longterm-goals">
              read on the site!
            </a>
          </strong>
        </div>

        <p>I have recently watched <a href="https://www.youtube.com/watch?v=mRd_1Bt9qrs">this video by Jason Lengstorf</a> where he chats with Maggie Appleton and she talks a lot about how she never had any clear longterm goals in her career, and how that actually was a good thing and possibly the key to her happiness. That got me thinking…</p>
<p>I’ve never been someone who plans ahead a lot. I had no idea what to do for a living when I finished high school. I never really made any plans of “getting married before I’m 25” or “owning a house before I’m 30”. After I began working as a developer, I never really followed a roadmap or knew what kind of developer I wanted to be 5 years down the road. I mostly went with my gut and focused on the things I enjoyed doing.</p>
<p>I started my career mainly as a backend developer, and everything was fun to me back then. As I had to touch the frontend more and more during my work, I realized I really liked that. So I decided to get into it! Not long after I was a primarily frontend developer, working on big web apps and making important architectural decisions. Fast-forward a few years, I got really into the “front of the frontend” side of things, and now I build websites (not web apps) for a living! I even do some designing every once in a while.</p>
<p>Looking from outside, that may look like I wasted the first years of my career learning things I don’t use anymore. If I had <em>planned ahead</em> and had <em>a set goal in mind</em>, surely I could have achieved so much more by now, <em>right</em>?</p>
<p>The thing is, back then I had very little idea of what I liked. Backend was fun to me because it was new, but quickly it became clear that what I enjoyed was something else. And I knew next to nothing about frontend development because every other developer I knew was backend-focused. So, how was I supposed to know I’d enjoy working on the frontend more? Right, I wouldn’t.</p>
<p>So, maybe, probably, I’d have spent years focusing on my goal of becoming the best backend developer, while not really enjoying it that much. And knowing myself, I just know that I could never do my best work if I didn’t enjoy what I was doing. I’d have sacrificed all the fun I had along the way, and probably still feel not good enough by now.</p>
<p>This anecdote was focused on web development, but it really could apply to most longterm goals in life.</p>
<hr>
<p>My wife and I always wanted to live abroad. We thought of Canada, but eventually decided we were going to live in Germany instead. The first step was getting our EU citizenship, which would then be followed by us leaving everything in Brazil behind and diving into the unknown in a different continent. It was our longterm goal for many years.</p>
<p>Eventually we figured out that we weren’t living our life in Brazil to the fullest because of that. We weren’t trying to find a better apartment because we’d move abroad. We didn’t get better furniture because we’d move abroad. We didn’t start studying what we wanted because we’d move abroad. Essentially we were making some of the best years of our lives worse just because we had a set idea in mind since many years before.</p>
<p>We changed that, and started actually doing what we could to enjoy our lives here more. The end result? We realized we really liked living here and that longterm goal of moving to another country was not that important, or at least not that <em>urgent</em>. It was such a relief to get that pressure off our shoulders. It even played a huge part in giving me a <a href="https://fantinel.dev/belonging-somewhere">feeling of belonging somewhere.</a></p>
<hr>
<p>I consider myself a pretty happy person. I do think a lot of it comes from me never really expecting much from myself, and from internalizing the phrase “the journey is the reward” pretty early in life. Not expecting much from myself is not necessarily a lack of self-confidence, it might actually be the opposite: ==I trust myself to become a better, wiser person with time.== And that wiser Matt is way more well-informed to make life decisions than dumb old me.</p>
<p>So much is expected of us by other people already that I feel that often the kindest thing you can do to yourself is to not expect anything. Future you has a way better idea of what they want than present you does. The best thing you can do for future you is to keep chasing what you like. Make your brain happy. ==It usually makes good life decisions when you’re happy.==</p>
<p>Goals are not inherently a bad thing. They keep us moving forward. But while dreaming of the treasure at the end of the rainbow, don’t miss the breathtaking beauty of the rainbow itself.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/cms/media/articles/longterm-goals.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/cms/media/articles/longterm-goals.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/automating-social-media-preview-images-satori</guid>
      <title>Automating Social Media Preview Images</title>
      <description>Social media preview images are very useful if you want to attract people to your website. They&apos;re sometimes a pain to create, though. Let&apos;s automate it!</description>
      <link>https://fantinel.dev/blog/automating-social-media-preview-images-satori</link>
      <pubDate>Sun, 05 May 2024 22:15:42 +0000</pubDate>
      <category>Front-End</category><category>Svelte</category><category>Dev</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/automating-social-media-preview-images-satori">
              read on the site!
            </a>
          </strong>
        </div>

        <p>Social media preview images (or open graph images, or og images) are very useful if you want to attract people to your website. Displaying that neat blog post featured image on the link preview might be the difference between someone scrolling past your link or clicking on it. Plus, it looks neat.</p>
<p>Problem is, not all blog posts have an image. Maybe it’s just a quick thought, or something so abstract you just can’t figure out a good image to put in there. Your blog layout is fine if you don’t add an image, but what happens when that link is shared? Maybe nothing will show up and people will miss it? Or maybe the more generic fall-back image of your website will show up in there. And how will people mindlessly scrolling on social media know what your link is about?</p>
<p>Luckily for us lazy people, there’s a way of automating them!</p>
<h2 id="generating-images-from-html-using-satori">Generating images from HTML using Satori</h2>
<p>This is kind of an old problem that has had a few solutions in the past, but none of them were really great. Depending on how long you’ve been coding, you might have used packages like <code>html2png</code> or something similar, and if you did, you probably remember it never really worked well and had plenty of limitations.</p>
<p>Enter <a href="https://github.com/vercel/satori">Satori</a>. It’s a library developed by Vercel that actually converts HTML and CSS to SVG. SVGs are way easier to convert to images than HTML, so that’s why they decided to go down this route. They then provide their own solution for converting to a PNG, but honestly, that’s not a big deal and you can roll your own quite easily (I did!).</p>
<h2 id="why-not-just-screenshot-a-page-some-sites-do-that">Why not just screenshot a page? Some sites do that!</h2>
<p>If you’re searching for this topic, you’ve probably found <a href="https://www.zachleat.com/web/automatic-opengraph/">some articles</a> talking about a method of generating these images by creating a page on your website that looks the way you want, and then using a serverless function to spin up an instance of Chromium, load your site, and take a screenshot of it.</p>
<p>That approach makes a lot of sense - I even used it briefly! - but it has three main problems:</p>
<ol>
<li>It’s <strong>slow!</strong> Spinning up Chromium on a server, waiting for the page to load, taking the screenshot and downloading it are a lot of considerably large steps.</li>
<li>It’s <strong>inefficient!</strong> Spinning up an entire browser just to grab a screenshot sounds really wasteful. If you’re in a paid plan, it’s expensive too.</li>
<li>It <strong>probably won’t  work.</strong> Which might actually be the #1 reason not to use it. ==Most of those articles were written years ago==, when the headless version of Chromium that’s used on those serverless functions was smaller. Nowadays, ==it takes up more than 50MB==, which is the maximum limit of serverless functions! There are technically still <a href="https://www.stefanjudis.com/blog/how-to-use-headless-chrome-in-serverless-functions/">some ways of working around that limit</a>, but those break often and are not guaranteed to work in the long run. Chromium is likely to keep growing.</li>
</ol>
<p>So, with that out of the way, we can now talk about how Satori works.</p>
<h2 id="the-more-efficient-albeit-slightly-limited-way">The more efficient, albeit slightly limited way</h2>
<p><a href="https://vercel.com/blog/introducing-vercel-og-image-generation-fast-dynamic-social-card-images">In their announcement blog post</a>, Vercel explains that they built Satori to counter the problems of the previous approach. In order to avoid running into the same speed and inefficiency problems, they wanted to avoid using any browser engine. So, they use the same layout engine as React Native, which, while not a complete CSS implementation, still has good parity in results for most cases.</p>
<p>The nice thing about that is that it’s lightweight and reliable. The bad things is that it has some caveats:</p>
<ul>
<li>It only supports a subset of CSS. So, no Grid support, for example. But considering you’re using this for generating preview images and not really something complex, it’s a fair tradeoff. <a href="https://github.com/vercel/satori?tab=readme-ov-file#css">Their GitHub page</a> details what it does and does not support;</li>
<li>It expects JSX syntax for elements. If you’re working on a project that doesn’t use it, that’d require another dependency and a build step. Additionally, you can also pass JS objects that have <code>type</code>, <code>props.children</code> and <code>props.style</code>. It’s good for not needing another dependency, but it’s <em>really</em> hard to read. Or you can use <a href="https://github.com/natemoo-re/satori-html">satori-html</a>, a package that translates HTML and CSS into JSX so you can just write good ol’ web code.</li>
</ul>
<h2 id="where-to-run-it">Where to run it</h2>
<p>In case it’s not yet clear, you need to have some sort of backend in order to run this. That way, you can make your backend return an image file instead of a page or something else.</p>
<p>Luckily, a bunch of frameworks include that nowadays. SvelteKit, Next JS, Astro, and others, all allow you to create some endpoints inside your web app. And, if you host things on platforms like Vercel or Netlify, you can probably have all that working for free (or included in the plan you already have).</p>
<p>On SvelteKit, you can just create a route with a server file (<code>+server.js</code>). On Next JS, you can add a new route to the <code>/api</code> folder. Other frameworks may vary, but it shouldn’t be that hard!</p>
<p>If you have a static site or something else and can’t run it, I haven’t really done much research into how it can be done. But I believe you can still use the free tier of <a href="https://vercel.com/docs/functions/quickstart">Vercel Functions</a>, for example, to get that working.</p>
<h2 id="my-implementation">My implementation</h2>
<blockquote>
<p>[!info]
In order to set it up on my site, I followed <a href="https://geoffrich.net/posts/svelte-social-image/">this post from Geoff Rich</a> that goes deep into how things work and how to build things. I’m not gonna try to rewrite his post because there’s really no need - his is already amazing, so if you’re looking into a tutorial, definitely follow his instead of mine.</p>
</blockquote>
<p>Since I use SvelteKit, I created a new <code>/opengraph</code> route with a <code>+server.js</code> file that does the magic. It receives some params via query string (like the text of the image) and then returns back the generated image.</p>
<p>Since I don’t use JSX and didn’t wanna add that to my project, I used <a href="https://github.com/natemoo-re/satori-html">satori-html</a> to convert HTML and CSS to it. Additionally, I used <a href="https://github.com/yisibl/resvg-js">resvg-js</a> to convert the SVG that Satori generates into a PNG.</p>
<p>To make things even nicer, I moved the HTML and CSS code into a Svelte component, added the <code>text</code> variable, and then imported that on the server file. In the end, the code goes through this entire flow: Svelte -> HTML -> SVG -> PNG! It looks roughly like this:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="javascript"><code><span class="line"><span style="color:#F97583">~</span><span style="color:#E1E4E8">filename </span><span style="color:#F97583">+</span><span style="color:#E1E4E8">server.js</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> componentResult</span><span style="color:#F97583"> =</span><span style="color:#E1E4E8"> component.</span><span style="color:#B392F0">render</span><span style="color:#E1E4E8">();  </span></span>
<span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> htmlString</span><span style="color:#F97583"> =</span><span style="color:#B392F0"> satoriHtml</span><span style="color:#E1E4E8">(componentResult.html);  </span></span>
<span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> svg</span><span style="color:#F97583"> =</span><span style="color:#F97583"> await</span><span style="color:#B392F0"> satori</span><span style="color:#E1E4E8">(htmlString, satoriConfig);  </span></span>
<span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> image</span><span style="color:#F97583"> =</span><span style="color:#F97583"> new</span><span style="color:#B392F0"> Resvg</span><span style="color:#E1E4E8">(svg, resvgConfig).</span><span style="color:#B392F0">render</span><span style="color:#E1E4E8">();  </span></span>
<span class="line"><span style="color:#F97583">return</span><span style="color:#F97583"> new</span><span style="color:#B392F0"> Response</span><span style="color:#E1E4E8">(image.</span><span style="color:#B392F0">asPng</span><span style="color:#E1E4E8">(), {  </span></span>
<span class="line"><span style="color:#E1E4E8">	headers: {  </span></span>
<span class="line"><span style="color:#9ECBFF">		'content-type'</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">'image/png'</span><span style="color:#E1E4E8">  </span></span>
<span class="line"><span style="color:#E1E4E8">	}  </span></span>
<span class="line"><span style="color:#E1E4E8">};</span></span></code></pre>
<p>After that, I can just make my <code>og:image</code> meta tag point to that endpoint:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="svelte"><code><span class="line"><span style="color:#E1E4E8">&#x3C;</span><span style="color:#85E89D">meta</span><span style="color:#B392F0"> property</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"og:image"</span><span style="color:#B392F0"> content</span><span style="color:#E1E4E8">={</span><span style="color:#9ECBFF">`https://fantinel.dev/opengraph?text={encodeURIComponent(post.title)}`</span><span style="color:#E1E4E8">} /></span></span></code></pre>
<p>And… that’s it! All my posts that don’t have an explicitly set cover image will now have at least a good looking related image that shows up on social media.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/cms/media/articles/automating-social-media-preview-images-satori.png"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/cms/media/articles/automating-social-media-preview-images-satori.png"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/thinking-of-a-redesign</guid>
      <title>Thinking of a redesign</title>
      <description>I want my personal website to reflect a bit of who I am. Am I the same person I was 3-4 years ago?</description>
      <link>https://fantinel.dev/blog/thinking-of-a-redesign</link>
      <pubDate>Fri, 29 Mar 2024 04:02:11 +0000</pubDate>
      <category>Meta</category><category>Personal</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/thinking-of-a-redesign">
              read on the site!
            </a>
          </strong>
        </div>

        <p>I recently wrote about <a href="https://fantinel.dev/5-year-blog-anniversary">my website’s 5th anniversary</a>, and made a little retrospective of its past layouts. Which made me realize it has looked pretty much the same for the last 3 or 4 years.</p>
<p>Which is… really not that long? In the grand scheme of things it’s still quite recent. You can also argue that this consistency is good for my “personal brand”. You see green waves, you’re on my website (or in <a href="https://github.com/matfantinel/fantinel.dev/forks">some of the many forks</a> around the web). And people seem to like it. A lot of people forked, copied, or rebuilt something similar for their websites. I even got hired to branch it out into <a href="https://github.com/matfantinel/sveltekit-static-blog-template">a separate template</a>. That makes me really happy, not only because it shows that people like what I built, but because my work is helping them like many other people’s work helped me (and still do).</p>
<p>On the other hand, I’m a person, not a brand. I want my personal website to reflect a bit of who I am. Am I the same person I was 3-4 years ago? Yes, but not really. I changed a lot, hopefully for the better, and maybe it’s time for my website to do the same.</p>
<p>There’s this fear of ending up with something I don’t like as much as I like the current one. Fear that I’ll waste time or that I’ll regret the changes a year from now.</p>
<p>But I think that if it does actually end up objectively worse, it just means I haven’t changed enough to express myself differently, which I guess will make me happier with what it is right now. Plus, I’ll have the experience of knowing I tried. Worst case scenario, it’ll end up exactly like it is now, but with me a little wiser.</p>
<p>Best case scenario, the 10-years retrospective will be more interesting ;)</p>
      ]]></content:encoded>
      
                
    </item>
  
    <item>
      <guid>https://fantinel.dev/5-year-blog-anniversary</guid>
      <title>5 Years of Fantinel.dev!</title>
      <description>My website has existed for half of my professional life now. In this post I look back at some snapshots of its history and talk about what&apos;s next.</description>
      <link>https://fantinel.dev/blog/5-year-blog-anniversary</link>
      <pubDate>Fri, 08 Mar 2024 12:05:36 +0000</pubDate>
      <category>Meta</category><category>Personal</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/5-year-blog-anniversary">
              read on the site!
            </a>
          </strong>
        </div>

        <p>On this day, 5 years ago, I launched my blog alongside <a href="https://fantinel.dev/purpose-of-this-blog">its first post</a>! Back then, I had little idea if I was gonna keep writing or maintaining it. Well… turns out I did!</p>
<h2 id="the-first-days">The First Days</h2>
<p>This blog actually began as a <a href="https://ghost.org">Ghost</a> blog, using its default theme. Ghost was great as a starting point as it provided a quick way to get up and running, and most importantly, a quick way to start <em>writing</em>. Back then I had a couple ideas for blog posts related to stuff I was doing at work - I believe they were the two posts about PWAs (<a href="https://fantinel.dev/angular-pwa-how-to">How to transform your Angular app into a PWA</a> and <a href="https://fantinel.dev/what-are-pwas-and-why-should-i-care-about-them">What are PWAs</a>). So, back then, to me, it was more important to get started with writing than to have the perfect blog setup.</p>
<p>Unfortunately, I don’t have a screenshot of what it looked like back then, but since it used the default Ghost theme (Casper), I guess it doesn’t matter much.</p>
<p>Five months later (October, 2019), I released the first version of this blog built by my own hands. It was built with <a href="https://jekyllrb.com/">Jekyll</a>, a Ruby-based static site generator that I chose because it was fully supported by GitHub Pages (GitHub’s own static website hosting service - it’s free!). All posts were stored in Markdown files (that’s still true to this day!), and building it was quite fun!</p>
<p>I didn’t have the skills or courage to design something from scratch yet, so I opted to almost completely clone Ghost’s default theme. Here’s what it looked like:</p>
<p><img src="https://fantinel.dev/cms/media/articles/5-year-blog-anniversary/v1.png" alt="Screenshot of the first version of my website, in light and dark themes. It contains a big section saying &#x22;Hey, Matheus Fantinel here.&#x22;. Below, a card with a picture of me and some text that&#x27;s too small to read describing myself." title="Fantinel.dev v1, October 2019"></p>
<p>Although I think it’s definitely evolved since then, it still has its charm. The big hero with my name had a typing effect that thankfully <a href="https://web.archive.org/web/20200105151225/http://fantinel.dev/">you can still check out thanks to the Wayback Machine</a>.</p>
<p>Even back then, I had some main goals for the website, that I had written on the project’s README:</p>
<blockquote>
<p>It was built with a few goals in mind:</p>
<p>• Responsive design: the website looks and behaves well on screens of all sizes;
• Fast: it only loads what’s needed for it to work. No external JS or CSS libraries:
• Adaptive: it supports dark mode from most operating systems by default (desktop and mobile);
• Pretty: use a simple and organized layout with simple animations to provide a pleasant experience to all visitors.</p>
</blockquote>
<p>That’s still what I strive for nowadays with the website, so I’m glad 2019 me set the path to where it is right now 😊.</p>
<h2 id="version-20">Version 2.0</h2>
<p>For Version 2.0, I decided to do a big redesign of the website, and add a bit more of <em>me</em> into it, instead of it being a carbon copy of a theme. It’s the first time I added the “waves” effect on the homepage, and since I wanted to design everything myself BUT don’t have much design skills, I relied heavily on cards to organize content (I still rely on cards often). Unfortunately, there’s no (working) snapshot of this version of the website on the Wayback Machine, so unless I run it locally (I’m too lazy to do it), all we have is this screenshot:</p>
<p><img src="https://fantinel.dev/cms/media/articles/5-year-blog-anniversary/v2.png" alt="Screenshot of the second version of my website, in light and dark themes. There is a green background with a &#x22;wavy&#x22; effect behind a big card with my picture and information about me. || no-shadow" title="Fantinel.dev v2, November 2020"></p>
<p>Overall, I think this is my least favorite version of it. I really like the waves effect (I still have it today), but I feel like the content didn’t have enough weight. Still, it set the precedent for what the website would look like today, with the colors and overall layout.</p>
<h2 id="version-30">Version 3.0</h2>
<p>This was a big rewrite, and brought it really close to what it’s like today. It was a complete rebuild from the ground up, now using Svelte and SvelteKit, replacing Jekyll. <a href="https://fantinel.dev/blog-development-sveltekit">I wrote about how I built it</a>, and it became my most read post of all time!</p>
<p>If you open it <a href="https://web.archive.org/web/20211006092452/https://fantinel.dev/">on the Wayback Machine</a> and compare to the current site, it mostly looks the same. I still think the layout is not perfect, but I haven’t found a better way of displaying information about me yet.</p>
<p><img src="https://fantinel.dev/cms/media/articles/5-year-blog-anniversary/v3.png" alt="Screenshot of the third version of my website. It has a background that resembles waves, and a greeting &#x22;Hello, I&#x27;m Matt!&#x22; front and center. There&#x27;s an illustrated version of me wearing glasses. || no-shadow" title="Fantinel.dev v3, September 2021"></p>
<p>I guess the main difference is that I lost the glasses.</p>
<h2 id="version-40-current">Version 4.0 (current)</h2>
<p>On February, 2023, I released the current version of the website, aptly named v4. Visually, it really wasn’t much of a change, but it included big changes under the hood, with me adopting atomic design for components, TypeScript, and some interactive stuff like the theme toggle. There were still some visual tweaks, like better positioning of some elements in the home page, but nothing major.</p>
<p><img src="https://fantinel.dev/cms/media/articles/5-year-blog-anniversary/v4.png" alt="Screenshot of the fourth version of my website. It still has a wavy background, but the content is positioned in more readable ways. || no-shadow" title="Fantinel.dev v4, February 2023"></p>
<h2 id="looking-back-at-it-all">Looking back at it all</h2>
<p>On my first ever post, I wrote that the purpose of this blog was to help myself learn about the topics I write here. And it certainly helped with that. A lot of posts here feel like guides because they are - initially a guide to myself that I decided to publish for everyone. And I’m glad I did! I don’t have the numbers since the beginning, but at least since January 2021, ==this site was visited by 43.4k people==! 🤯 That’s a lot. I never expected so many people to drop in and read something I wrote. And considering most of the visitors got here from Google and into one of the guide/help articles, I hope I helped them somehow. Blog posts have been a huge source of help to me over the past years, and I’m happy to be giving back to the dev community.</p>
<p>(by the way, all my blog’s analytics data is fully anonymized and public. <a href="https://plausible.io/fantinel.dev?period=all">You can check it out on Plausible</a>)</p>
<p>But most of all, this website has been by itself a source of learning for me. More than writing, I think I learned more from building and experimenting on it, and I really don’t plan on stopping. It’s my testing grounds, a way to reflect a bit of who I am, and most of all, have fun.</p>
<h2 id="next-steps">Next steps</h2>
<p>On the dev side, I want to keep experimenting. I have some small redesigns planned that I just need time to work on. More importantly, I’m trying really hard to simplify the writing process so I can write more, and more often, from anywhere.</p>
<p>And on the writing side, I really wanna expand the topics I write about. The “blogging renaissance” that’s been going on lately has really inspired me to have this blog be less of a content repository and more of… Matt’s blog. Last year I already started with some purely personal posts and I’m thinking of doubling down on that. But, that’s only good if it comes naturally, I won’t force myself to write stuff I don’t want to. We’ll see.</p>
<p>The only thing I’m certain of is that I want to keep it going.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/cms/media/articles/5-year-blog-anniversary/cover.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/cms/media/articles/5-year-blog-anniversary/cover.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/owning-your-stuff</guid>
      <title>Owning your stuff is pretty cool, actually</title>
      <description>Let&apos;s talk a bit about Obsidian, VC-funded apps, the appocalypse and how awesome Markdown life can be.</description>
      <link>https://fantinel.dev/blog/owning-your-stuff</link>
      <pubDate>Thu, 29 Feb 2024 00:00:00 +0000</pubDate>
      <category>Reading Recs</category><category>Personal</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/owning-your-stuff">
              read on the site!
            </a>
          </strong>
        </div>

        <blockquote>
<p>If you want to create digital artifacts that last, they must be files you can control, in formats that are easy to retrieve and read. Use tools that give you this freedom.
 
– Steph Ango, in <a href="https://stephango.com/file-over-app">Files over apps</a></p>
</blockquote>
<p>This week I re-read Steph Ango’s post about why we should prefer files over apps. The context over which it was reposted was because Notion bought Skiff, an email client, which was quickly followed by an announcement that the service is being sunset in 6 months (and its source code got moved to another repo with no warning; which made many people think it got pulled completely).</p>
<p>There’s a ton of things to like about this idea of using open format files for everything. It’s pretty much the most guaranteed way to make sure your data belongs to you, because it’s as close as you can get to physically owning something digital.</p>
<p>Nowadays, most of what we use has databases hosted somewhere we don’t own, and that data can only be read by that service. Some integrate with others, some allow you to export your data to other services (or even to files), but the truth is that you don’t have the same level of control. If that service runs out of money and goes down suddenly, or some stakeholder decides letting people export their data is bad for business, you’re at their mercy. Especially if it’s a VC-funded company, which are encouraged to either go big or go broke.</p>
<p>Sure, there’s a reason those apps exist and we use them: dealing with files can be hard. It becomes our responsibility to manage them, keep them safe, and if you want to access them on multiple devices (your laptop and your phone, for example), then it gets a lot more complicated. So, is there a happy medium somewhere? I think so.</p>
<h2 id="file-based-apps">File-based apps</h2>
<p>Steph Ango is one of the people behind <a href="https://obsidian.md/">Obsidian</a>, a note-taking app that writes everything into Markdown (.md) files. It is a pretty powerful app, capable of linking and organizing notes in pretty complex ways. But still, all the data in there is stored in files that can be read by any program in any system. Even though Obsidian has come unique features (and even plugins that add even more features), you can use it with the peace of mind that, if anything ever happens to Obsidian, you’ll still have 100% of your data with you.</p>
<p>Obsidian is far from the only app that works like this, which is great. It means that if you find another app that better suits your needs, you can easily take your data in there too. Off the top of my head, you could easily migrate to Logseq, Notable, or even VS Code without having to do anything.</p>
<p>And what conveniences do you lose with this kind of approach?</p>
<p>Well, syncing files between devices is not as straightforward (or at least not free). In Obsidian’s case, you can pay for Obsidian Sync, which is actually what funds development. If you don’t want to pay, it can still be done in a lot of ways. Some people use iCloud or Dropbox folders, others use Git, or other solutions. But yeah, definitely not as easy, especially nowadays where we expect these things to be free. Which leads us to another topic…</p>
<h2 id="sustainable-apps">Sustainable apps</h2>
<p>Software costs money to build. A lot. Even if it doesn’t cost money to keep it up (i.e. there are no servers or support), there’s still all of the dev work that went into it.</p>
<p>What we’ve been seeing happen a lot in the past decade (or even further back) is that startups receive huge investments, with the goal of growing as much as possible, as quickly as possible, to grab a good market share. So these startups operate at huge losses in order to gather a large user base. Then, eventually, the investors want their money back, and now startups need to figure out ways of generating revenue from all these users.</p>
<p>What happens then is… a lot of the things that brought users into those apps start being taken away. The things that caused financial losses to the startup aren’t really feasible anymore, and they need to either start charging for stuff that was free or hike up prices that were low. Or even worse, start making money off user data. The term “<a href="https://en.wikipedia.org/wiki/Enshittification">enshittification</a>” was coined the word of the year in 2023 for a reason.</p>
<p>This is because, as I’ve written before, VC-funded companies are encouraged to either go big or go broke. There’s no middle ground. They can’t grow steadily at a sustainable rate because they got a big chunk of money that they’ve gotta pay back in a few years, and they gotta hurry. So, there’s even more reason to <strong>really</strong> consider not having all your important data on a server they own, with no easy way of backing it up somewhere else.</p>
<p>Which is why I have a big appreciation for companies that aim to build software (or anything, really) in a sustainable way. They start with products that are good and appealing, yes, but also with well defined monetization strategies. Companies like <a href="https://plausible.io/blog/open-source-saas">Plausible</a>, the brazilian news blog <a href="https://manualdousuario.net/">Manual do Usuário</a> and the previously mentioned Obsidian come to mind. I’m sure there are more.</p>
<h2 id="my-next-steps">My next steps</h2>
<p>I have a confession to make: I talked a lot about Obsidian here, but I don’t actually use it. I use Notion, a VC-funded note taking app that keeps all my notes on a database on their server somewhere. I also use some other VC-funded apps. So this post was written for myself, too. Often as I read some tech news I get a bit worried that I might lose some things. Eventually something will happen with Notion that will force me to change my workflow or make me migrate somewhere else (this migration, again, isn’t easy, because my notes are not just files).</p>
<p>So, I’m planning on changing some things. All my blog posts here are already just Markdown files, and I’m testing editing them in Obsidian for kicks (and maybe to eventually be able to post from a phone or tablet). This will be my first step into getting used to it and bringing over my important personal notes to Markdown files too.</p>
<p>Beyond the peace of mind of actually owning my stuff, it’ll be nice to be able to admire everyone who builds the software I use, instead of being slightly concerned that they’ll turn on me someday.</p>
      ]]></content:encoded>
      
                
    </item>
  
    <item>
      <guid>https://fantinel.dev/stripping-web-humanity</guid>
      <title>Stripping the web of its humanity</title>
      <description>A less human web is no good for anyone.</description>
      <link>https://fantinel.dev/blog/stripping-web-humanity</link>
      <pubDate>Tue, 30 Jan 2024 23:43:46 +0000</pubDate>
      <category>Reading Recs</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/stripping-web-humanity">
              read on the site!
            </a>
          </strong>
        </div>

        <p>Talking about the newly released <a href="https://apps.apple.com/us/app/arc-search/id6472513080?pt=120729021&#x26;ct=arcnet-homepage">Arc Search</a>, Ben Werdmuller says:</p>
<blockquote>
<p>A world where everyone uses an app like this is a death spiral to an information desert.</p>
<p>We built the world’s most incredible communication and knowledge-sharing medium, rich with diverse perspectives and alternative ideas; let’s not sanitize it through a banal filter that is designed to strip it of its humanity.</p>
<p>– Ben Werdmuller, in <a href="https://werd.io/2024/stripping-the-web-of-its-humanity">Stripping the web of its humanity</a></p>
</blockquote>
<p>I agree with Ben’s sentiment here. As flashy and interesting on surface as these AI tools are, they usually bring their own bunch of problems. And they’re also symptoms of even bigger problems: the apparent need to summarize web content is a result of how much filler content there is, usually just as a manner of improving SEO and being able to show more ads to more people. It’s content written by robots for robots (search engine bots), now being read by robots and summarized in a few bullet points. Where’s the <em>human</em> in that?</p>
<p>I am a user of GitHub Copilot and love the tool, so I’m not totally against this latest wave of AI tools. But as of now, there are only a few use cases where I see a net positive for us in the end.</p>
<p>Today, I limited a bunch of AI bots’ access to my website’s content. Probably won’t do any difference, as there’s a lot of similar content to scrap out there. And the thing that makes my content <em>mine</em>, the human part of this, wasn’t going to be used by them anyway. I guess it’s just a way of me yelling at the clouds that the internet I want to see is not the one they’re trying to build.</p>
      ]]></content:encoded>
      
                
    </item>
  
    <item>
      <guid>https://fantinel.dev/progressive-enhancement</guid>
      <title>Progressive Enhancement (and why it matters)</title>
      <description>Progressive Enhancement isn&apos;t just another web jargon; it&apos;s a guiding principle shaping modern web development.</description>
      <link>https://fantinel.dev/blog/progressive-enhancement</link>
      <pubDate>Tue, 23 Jan 2024 01:50:25 +0000</pubDate>
      <category>Front-End</category><category>Dev</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/progressive-enhancement">
              read on the site!
            </a>
          </strong>
        </div>

        <p>I’ve mentioned Progressive Enhancement on this blog <a href="https://fantinel.dev/blog-development-sveltekit">once before</a>, when explaining how my blog was built and how Svelte has that concept as one of its main appeals. Since then, it’s something that has stuck with me on everything I’ve built, and as such has shaped the way I work both on personal projects and professionally as well.</p>
<p>This article talks about Progressive Enhancement focusing mainly on websites and web apps, but it’s a concept that may carry over to other areas of development (client-side, at least), though the specifics might differ.</p>
<h2 id="what-is-progressive-enhancement">What is Progressive Enhancement?</h2>
<p>If you really think about it, Progressive Enhancement is all about accessibility. It’s about making sure people of all kinds and with all kinds of devices and connections are able to access your software in at least some capacity.</p>
<p>Just like you can’t expect all users to have perfect eyesight and motor skills, you can’t expect them to have the latest hardware and software, or to have perfect network connections to load everything as intended. Of course that the experience of someone whose spotty 3G connection wasn’t able to load your JS files to be as good as someone in a fast stable Wi-Fi, but what if both could use your website/app either way?</p>
<p>Sounds hard, right? But it really isn’t.</p>
<h2 id="why-and-when-it-matters">Why and when it matters</h2>
<p>When talking about Progressive Enhancement, there’s three main reasons a user may be facing a non-ideal version of your website:</p>
<ol>
<li>The JavaScript files couldn’t be downloaded;</li>
<li>The CSS couldn’t be downloaded;</li>
<li>The user’s browser/device doesn’t support a feature your code uses;</li>
</ol>
<p>For number 3, this can be dealt with with some clever feature-checking (more on this below) and using websites like [<a href="http://caniuse.com">caniuse.com</a>](<a href="https://caniuse.com">https://caniuse.com</a>) before using newer features.</p>
<p>As for 1 and 2, there are numerous reasons it can happen. I like to refer to <a href="https://www.kryogenix.org/code/browser/everyonehasjs.html">this website</a> whenever I want to reference why someone might not have JavaScript loaded (and the same reasons apply for CSS), but to summarize:</p>
<ul>
<li>A faulty connection may make the JS/CSS file requests fail, even though the page itself loaded;</li>
<li>A lot of corporate firewalls and ISPs block domains, which might affect requests, especially 3rd-party files your code might depend on;</li>
<li>The user might have disabled JavaScript (a lot of people do);</li>
<li>Browser extensions (especially adblockers) might block your scripts from running;</li>
</ul>
<p>It’s hard to know exactly how many people this affects, and the number will greatly vary depending on who your audience is. <a href="https://gds.blog.gov.uk/2013/10/21/how-many-people-are-missing-out-on-javascript-enhancement/">A 2013 study by the UK Government</a> showed that 1 in 93 people won’t get your JavaScript code running correctly. 11 years later, while in general connections have gotten faster and more stable, users have also become more likely to use ad-blockers or similar tools. So I’d wager the rate hasn’t changed much.</p>
<h2 id="how-to-progressively-enhance-your-website">How to Progressively Enhance Your Website</h2>
<p>I’m gonna be honest: Progressive Enhancement is easy, as long as you’re building something new. There’s no quick way of going back to old JS-dependent features and make them work without it, as it will require a refactor. Beyond that, while Progressive Enhancement is a always good thing to keep in mind when developing, a fully working website with JS disabled might not be achievable if you have an app that uses JS to build its entire UI - a SPA (Single Page Application).</p>
<p>With that out of the way, these are my main tips for building websites that get progressively better the more resources the user has:</p>
<h3 id="start-with-the-basics">Start with the basics</h3>
<p>Instead of building a fully-featured UI and then stripping it down, we’re doing the opposite: start with the bare minimum. It helps to disable JS for your website (can be done in the browser’s DevTools), so you’re 100% sure everything will work without it. It doesn’t need to be pretty or smooth, it just needs to function. Once that’s done, you can re-enable JavaScript and start adding the charming bits.</p>
<p>There’s not much you can do to prepare for a case where your CSS didn’t load, but you can mitigate one of the main issues when that happens: <strong>add height/width to your images and SVGs.</strong> That way, they won’t be absurdly big and won’t break the layout as much. And, if you want to style your images with more complex rules, you can add the styles in CSS and they’ll overwrite the height/width attributes. For example:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="html"><code><span class="line"><span style="color:#E1E4E8">&#x3C;</span><span style="color:#85E89D">img</span><span style="color:#B392F0"> src</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"example.jpg"</span><span style="color:#B392F0"> width</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"500"</span><span style="color:#B392F0"> height</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"500"</span><span style="color:#E1E4E8"> /></span></span></code></pre>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="css"><code><span class="line"><span style="color:#F97583">~</span><span style="color:#E1E4E8">filename styles</span><span style="color:#B392F0">.css</span></span>
<span class="line"><span style="color:#6A737D">/* These will only be applied if </span></span>
<span class="line"><span style="color:#6A737D">the CSS file loads successfully */</span></span>
<span class="line"><span style="color:#85E89D">img</span><span style="color:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#79B8FF">  width</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">100</span><span style="color:#F97583">%</span><span style="color:#E1E4E8">;</span></span>
<span class="line"><span style="color:#79B8FF">  height</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">100</span><span style="color:#F97583">%</span><span style="color:#E1E4E8">;</span></span>
<span class="line"><span style="color:#79B8FF">  object-fit</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">cover</span><span style="color:#E1E4E8">;</span></span>
<span class="line"><span style="color:#E1E4E8">}</span></span></code></pre>
<p>And, if you want your images to be scale responsively when your CSS actually loads, just overwrite the height/width there! Fun fact: adding height/width properties to the <code>img</code> element also helps many frameworks load correctly-sized images to make them more lightweight.</p>
<h3 id="think-inside-the-box">Think <em>inside</em> the box 📦</h3>
<p>There’s a lot of talk about thinking outside the box when developing - and indeed it’s one of the best skills a developer can have! But often the inside of the box is really useful too. Whatever you’re thinking of building, there’s probably an HTML element that behaves similarly. Why not expand on that instead of just adding more events to a div?</p>
<ul>
<li>For hyperlinks, use <code>&#x3C;a></code> (please);</li>
<li>For accordions, use <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details">the </a><details><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details"> element</a>;</details></li>
<li>Need to send data to a server? Just use a <code>&#x3C;form></code>! No need for onClicks or onSubmits;</li>
<li>Any element that has a toggleable state - for example, a hamburger menu or even a modal dialog - can be controlled by a checkbox instead. Yes, really! Check out the mobile hamburger menu of this website;</li>
<li>In fact, add <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element">MDN’s list of HTML elements</a> to your bookmarks. Use divs only as a last resort. Chances are, using a proper HTML element is going to make your code simpler, more readable and more accessible.</li>
</ul>
<h3 id="use-feature-queries">Use feature queries</h3>
<p>In the past few years, CSS has been getting a ton of new features, making web development easier in general. Things like <a href="https://fantinel.dev/container-queries">container queries</a>, <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/:has">parent selectors</a> (:has), and many more.</p>
<p>However, even when all browsers adopt the new features quickly, users still might keep using older versions of a browser for a while. But it the feature you want to use has been available for a bit, you can use feature queries to check if a feature is available or not. I’ve briefly mentioned this in my <a href="https://fantinel.dev/css-hover-media-query">conditional hover styles post</a>, but to sum it up, you can use the <code>@supports</code> keyword to check if a property is available or not. Take this example of a feature check for container queries:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="css"><code><span class="line"><span style="color:#F97583">@supports</span><span style="color:#E1E4E8"> (</span><span style="color:#79B8FF">container-type</span><span style="color:#E1E4E8">: inline-size) {</span></span>
<span class="line"><span style="color:#6A737D">	/* container queries styles */</span></span>
<span class="line"><span style="color:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">@supports</span><span style="color:#F97583"> not</span><span style="color:#E1E4E8"> (</span><span style="color:#79B8FF">container-type</span><span style="color:#E1E4E8">: inline-size) {</span></span>
<span class="line"><span style="color:#6A737D">	/* fallback styles */</span></span>
<span class="line"><span style="color:#E1E4E8">}</span></span></code></pre>
<p>And what if the feature isn’t supported, do I need to write my CSS rules twice? No, not really. I mean, it’s your choice how much effort you want to have in supporting the older browsers in this case, but I’d recommend you to at least do something that’s good enough. In the example above (common for creating responsive elements), the older browser could be served some media queries that might not be as pixel-perfect or smooth, but still good enough to not serve a broken experience.</p>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>I hope this article has been at least a bit enlightening as to how important and easy it can be to start adopting Progressive Enhancement. I feel like as web frameworks got more advanced and decoupled from the basic web development experience, we also started to forget using what the platform gives us, and began reinventing wheels, often in less accessible and less performant ways.</p>
<p>That was a response to a stagnant web platform, more than a decade ago. But that time is past, and the web platform has been progressing greatly in the past few years. By consequence, there has been a change in web frameworks that seem to be adopting more of the native web and depending less on client-side JavaScript. That can be seen in newer frameworks like Svelte and Solid when compared to older ones like React and Angular. But most of all it can be seen in meta-frameworks like Astro or NextJS, that are trying to make the JS-dependent frameworks all produce markup in the server and serving the user a complete page even with JavaScript disabled.</p>
<p>Web development is changing again (has it ever stopped?), and this time it’s hard to see a downside.</p>
      ]]></content:encoded>
      
                
    </item>
  
    <item>
      <guid>https://fantinel.dev/2023-year-in-review</guid>
      <title>2023 In Retrospective</title>
      <description>A quiet year, but still with lots to talk about.</description>
      <link>https://fantinel.dev/blog/2023-year-in-review</link>
      <pubDate>Thu, 21 Dec 2023 16:09:02 +0000</pubDate>
      <category>Retrospective</category><category>Meta</category><category>Personal</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/2023-year-in-review">
              read on the site!
            </a>
          </strong>
        </div>

        <h2 id="last-years-predictions">Last Year’s Predictions</h2>
<p><a href="https://fantinel.dev/2022-year-in-review">Last year</a>, I hoped 2023 would be a <em>less exciting</em> year, compared to how 2022 was. And I got that right. Nothing major happened to me in 2023 and I feel like I needed that. It was a year to put my head in its right place and prepare for the next couple of years.</p>
<p>I also mentioned I was planning on refactoring my website, and releasing at least one of the side projects I had planned. I did refactor a huge chunk of my website, but… at this point, I don’t even remember what side project I had in mind when I said that 😅.</p>
<p>I also said I wanted 2023 to be <em>cozy</em>. Eeeh… kinda?</p>
<h2 id="personal-life">Personal Life</h2>
<p>As for my personal life, nothing really big happened this year. Last year I got married and lived in Italy for 3 months, but this year I just moved from one apartment to another in the same city and that’s it. There were a bunch of smaller, but still amazing moments, though!</p>
<h3 id="travel">Travel</h3>
<p>My wife and I took some time to discover more of the region we live in (Serra Gaúcha - Brazil). We found out a cool buddhist temple, a random place on a country road filled with mannequins of horror movie characters (!), some really nice parks and places to eat at nearby cities, and, for our 1-year anniversary, we went to a nice retreat in the mountains, and even watched the sunrise atop a hot air balloon!</p>
<p><img src="https://fantinel.dev/cms/media/articles/2023-year-in-review/serra.jpeg" alt="Picture of a mountain range. The mountains are covered with trees, the sky is cloudy but with enough gaps for the sun to come through. In the foreground, a pool and two chairs overlook the cliffs." title="This mountain range is in the divide between the states of Rio Grande do Sul and Santa Catarina, in the south of Brazil. Photo by me."></p>
<p>We also took a trip to São Paulo in August. São Paulo is the biggest city in Brazil (and the biggest in the American continent), and I’d never been anywhere even comparably as big. I consider the city I live in big (with 500k people), but São Paulo’s 22 million people is just something else.</p>
<p>Being such a big city, it’s also a boiling pot of different cultures from all around the country (and neighboring countries as well). Which makes it really fun! We spent a few days there and came across so many different cultures, often on the same street, side by side, resulting in a sight you can’t really see anywhere else.</p>
<p>On Sundays, one of the city’s main avenues (Avenida Paulista) is closed for cars, which turns it into a public space where everything happens, all at once. Just as we got there, we saw a zumba class happening, while a guy dressed up as Batman was singing karaoke in the middle of the street. Wolverine was calmly watching that happen, while African-based religious groups were grouping up across the street to sing and praise their deities. Walking up the street for a couple minutes, you’d find a samba school rehearsing for the carnival and a bit further a DJ in their booth playing some hardcore dubstep. Everything everywhere all at once. It was great.</p>
<p>São Paulo also has an immense amount of museums, including modern art, african art, a museum of Japanese Immigration, and even one of the story of football (soccer) in Brazil. There’s no shortage of culture to be absorbed there, and most of it was either free or really cheap.</p>
<p><img src="https://fantinel.dev/cms/media/articles/2023-year-in-review/masp.jpeg" alt="Picture taken from below the MASP museum, in São Paulo. The sidewalk and the street are filled with all kinds of people, and on the other side of the street, there are street food stands and a park." title="Photo taken below the MASP, in São Paulo."></p>
<h2 id="blog">Blog</h2>
<p>I’m pretty sure this was the best year for the blog, at least considering the amount of posts published. Not counting this one, I’ve published <strong>12</strong> posts, up from the 3 from last year.</p>
<p>I feel like what made me motivated to post more is the fact that I’m now following personal blogs much more than I was in the past - with the death of Twitter, I’ve <a href="https://fantinel.dev/from-twitter-to-mastodon">moved to Mastodon</a> and this pivot towards <a href="https://fantinel.dev/open-web-and-the-masses">a more open web</a> has made me start using RSS seriously. Seeing so many people write on their own blogs and experiment with their own websites made me want to work on mine more. It’s a win-win!</p>
<p>I think my favorite post was “<a href="https://fantinel.dev/belonging-somewhere">Belonging Somewhere</a>”, which was the first post that’s purely personal, no tech involved. My other highlights were the <a href="https://fantinel.dev/review-zelda-tears-of-the-kingdom">new Zelda game review</a> and <a href="https://fantinel.dev/iron-maiden-killers-concept-album">me trying to explain why I think “Killers” by Iron Maiden is a concept album</a>. I enjoyed diversifying the subjects I post about, even if those posts don’t do big numbers (which is honestly fine, that’s not why I write).</p>
<p>Speaking of numbers, I joined the blogging trend of <a href="https://fantinel.dev/default-apps-2023">posting about my default apps</a>, and after Matt Birchler reposted it on his Mastodon account, I got a huge traffic spike (compared to the regular traffic I get), from 35-50 to 270 visitors on a single day!</p>
<p><img src="https://fantinel.dev/cms/media/articles/2023-year-in-review/traffic-spike.png" alt="Screenshot of a line graph showing a huge spike in traffic on a specific day on my website, going from between 35 and 50 visitors to 275."></p>
<p><em>As a reminder, all my website’s analytics are privacy-respecting and public, <a href="https://plausible.io/fantinel.dev">you can see them here</a>.</em></p>
<p>Something else I’ve tried is shorter articles that are usually reading recommendations of other great articles I’ve read. I’m grouping them under the “Reading Recs” tag and plan to add some filtering in the future.</p>
<hr>
<p>On the development side, I’ve done a bunch of changes to the website, though none are too visually impactful:</p>
<ul>
<li>Added a new Table of Contents component to the longer articles I have. It is auto-generated, responsive, and I think it’s working great! There’s one right here on this post 😁.</li>
<li>Significantly refactored how blog posts are stored and loaded. I went full steam on Markdown, with all blog posts having their content and metadata stored in Markdown files, and managed inside VS Code with <a href="https://frontmatter.codes/">FrontMatter</a>;
<ul>
<li>This included extending Markdown to be able to use some custom elements inside it! Which means that even if I choose to manage posts differently in the future (like using a CMS), I won’t need to change anything in the posts themselves;</li>
</ul>
</li>
<li>I started using my own <a href="https://www.npmjs.com/package/image-transmutation">image-transmutation</a> package to optimize the images of the website, making sure their quality is good and their size is the smallest possible;</li>
<li>Swapped the font I use to <a href="https://fixel.macpaw.com/">MacPaw’s Fixel font</a>, a delightful variable font that’s as easy to use as it is to read;</li>
<li>Added view transitions so that navigating between pages now animates smoothly (Chromium-only, for now);</li>
<li>Added pagination to posts and a hamburger menu for mobile;</li>
</ul>
<h2 id="work-life">Work Life</h2>
<p>This year, I’ve started working full-time for <a href="https://www.usefulgroup.com/">Useful Group</a>, a lovely agency from Illinois, USA, as a web developer building mainly WordPress websites. This was a very interesting development, as previously I was mainly a “web app guy” instead of working on websites, and me having zero experience handling WordPress or PHP. It still felt as a natural step, as I had started to focus more on “<a href="https://css-tricks.com/the-great-divide/">the front of the frontend</a>” in recent years, and working with websites allows me to focus on that.</p>
<p>I learned quickly though, and really enjoy the way they work there, and was able to fit in nicely. I particularly enjoyed seeing how you can adapt older, battle-hardened technologies to newer, more modern concepts (like atomic design), and how well everything fits in. It also made me realize how much of what’s “new” in web frameworks nowadays is mainly us going back from SPAs to older concepts.</p>
<p>That might sound bad, but I think it’s actually good when tech comes full circle and goes back to concepts that have worked well in the past, because it means that it has matured. Static/multi-page websites had their issues years ago, which is why SPAs became a popular thing. Of course, SPAs brought their own set of issues and now we’re transitioning back to multi-page websites, but with more mature tooling and with a lot of the original issues solved.</p>
<p>Speaking of static websites, I was contacted by a company that was interested in using my personal website as a template for their own. My website is open source and can be modified by anyone, but they would like me to do the needed modifications, since I was already used to the codebase.</p>
<p>I ended up making <a href="https://sveltekit-static-blog-template.vercel.app/">a generic blog template</a> instead, based on what my website looked like in that specific point in time. I strongly recommend using that code instead of this in case you’re interested in forking it!</p>
<p>I internally debated for a while if I wanted to keep my website as a fork of that template, or keep them separate. I opted for the latter after realizing that I didn’t want my personal website to be tied up to a template meant to help others. I like being able to experiment here, and didn’t want the onus of having to maintain compatibility and/or having to backport anything there.</p>
<h2 id="fun">Fun</h2>
<p>Now the best part: the things I enjoyed watching, playing or listening the most in 2023!</p>
<h3 id="tv-and-movies">TV and Movies</h3>
<ul>
<li><strong>Succession</strong> is a show that I’ve been recommended for a while, but its synopsis had never struck a chord with me. However, I was bored one day and decided to start watching it. And it’s <em>amazing</em>! The show doesn’t take itself seriously, so it’s as much of a comedy as it is a family drama. It follows the lives of a wealthy family that owns a big media conglomerate, and the patriarch’s health is deteriorating and his children start fighting to be his successors. They’re so out of touch with reality, though, that even mundane situations reach absurd conclusions. <em>Available on HBO/Max</em>.</li>
<li><strong>Bojack Horseman</strong> speaks to me on a really deep level, even more on my 2nd rewatch of the show. It starts deceptively simple, and you think it’s going to be a really funny, albeit kinda generic cartoon. And then it hits you. And hits again. It’s an incredibly emotional show that gives you tears of joy and sadness in equal amounts. It’s a must-watch for anyone that has ever gone through any mental health issues and/or would like to understand them better. Plus, it has one of the best autistic representations on TV! <em>Available on Netflix</em>.</li>
<li><strong>Severance</strong> was a surprise hit for me. It’s a show about a company where its employees have their mind <em><strong>severed</strong></em> in half, with one “personality” that only exists in the workplace, while the regular personality doesn’t remember anything from work and only ever experiences the “good parts” of life. As you can imagine, it goes really wrong really fast. It masterfully keeps you hooked with an ever-increasing amount of mystery, but then the bad part comes: season 1 ends in a cliffhanger, and there’s still no word on when season 2 is coming. So I recommend waiting a bit before watching it. <em>Available on Apple TV+.</em></li>
<li><strong>Ted Lasso</strong> was one of my favorite shows when I watched seasons 1 and 2 last year, and this year I rewatched it all in preparation for the final season 3. It is the most heartwarming show I’ve ever seen, and even the sadder parts hit you just hard enough to make the heartwarming moments even better. Season 3 was not as high quality as the first two, but it still provided me a lot of joy and the ending was really satisfying. <em>Available on Apple TV+.</em></li>
<li><strong>The Last of Us</strong> was an absolutely incredible experience - a gut punch every episode, but in a good way. I had played the game before, and while I knew the major plot points before they happened, I was absolutely floored when they happened on the show. Incredible writing, acting, and with just enough tweaks to make it feel like a real TV show and not just a game adaptation. The game’s story was already really good, and I feel like it was greatly improved for TV. Doesn’t mean the show’s story is better than the game’s, but it definitely is <em>better for TV</em>.</li>
</ul>
<h3 id="games">Games</h3>
<ul>
<li><strong>Disco Elysium</strong> is one of the most unique games I’ve ever played. It has by far the best dialogue of any game I know, and there are so many branching paths that I think it can be replayed multiple times and still provide an unique experience each time. It is basically an investigation RPG, where you play as a cop with an alcohol-induced amnesia, trying to figure out who you are and who’s responsible for a murder. There’s a lot of dialogue and the majority of it is with other voices in your head (fragments of your psyche). It’s nuts. It takes place in a unique world with a ton of lore, it’s incredibly political and it hits really heavy at some points.</li>
</ul>
<p><img src="https://fantinel.dev/cms/media/articles/2023-year-in-review/disco-elysium.jpg" alt="Official artwork for Disco Elysium. A heavily stylized drawing of two men, one of them wearing glasses and a puffy jacket, holding a gun and a flashlight. The other has mutton chops and long hair and is wearing a formal shirt with a tie and holding a green jacket in one hand and a gun in the other. In the background, a very dense city and the twilight sky."></p>
<ul>
<li><strong>The Legend of Zelda: Tears of the Kingdom</strong> is my game of the year - an incredible step up from what was already one of my favorite games of all time. I wrote <a href="https://fantinel.dev/review-zelda-tears-of-the-kingdom">an in-depth review of it here</a>!</li>
<li><strong>Hogwarts Legacy</strong> was a pretty good game that unfortunately didn’t seem to understand its own appeal. It does an incredible job of bringing Hogwarts to life, making the entire castle explorable and full of secrets. Every part of it carefully designed and decorated, with so much attention to detail to praise even the most hardcore book fans. And then… it keeps sending you outside the castle to do generic action adventure game things. And compared to all the other games that do that, it’s just okay.</li>
<li><strong>Bloodborne</strong> again. I’ve written about it last year, and I’ve played it again (twice!) this year, to get the Platinum trophy and… I just love this game so much. It’s become my favorite game of all time and just writing this I feel like playing it again. Masterpiece.</li>
</ul>
<h3 id="music">Music</h3>
<ul>
<li>Last year I discovered the album Infinite Granite by <strong>Deafheaven</strong>, and it’s also been my most listened to album (by far) in 2023. However, I also started listening to other albums by the band and I love them so much. <strong>Sunbather</strong> and <strong>Ordinary Corrupt Human Love</strong> especially are gems.</li>
<li><strong>Shoegazing</strong> has become my favorite genre. Wanting to listen to more stuff that sounded like Infinite Granite, I found out about this genre and started building up a playlist on Apple Music, and it’s become my go-to whenever I need to relax, focus or simply listen to great music. If you have Apple Music, you can listen to it too:</li>
</ul>
<iframe allow="autoplay *; encrypted-media *; fullscreen *; clipboard-write" frameborder="0" height="450" style="width:100%;max-width:100%;overflow:hidden;border-radius:10px;" sandbox="allow-forms allow-popups allow-same-origin allow-scripts allow-storage-access-by-user-activation allow-top-navigation-by-user-activation" src="https://embed.music.apple.com/br/playlist/shoegazing/pl.u-V9D7mqacBrRjxkR"></iframe>
<h3 id="concerts">Concerts</h3>
<p>This year, my wife and I went to 3 amazing concerts! They were all in Porto Alegre, the capital of our state, which is a 2-hour drive from here. They were:</p>
<ul>
<li><a href="https://www.youtube.com/watch?v=Ag-WBs8kVWc">Bruce Dickinson + Band + Orchestra</a> playing a concert piece composed by Jon Lord from Deep Purple, back in April. They also played some Deep Purple classics and a couple of Bruce Dickinson’s solo songs. Admittedly, I didn’t know the concert piece nor some of the Deep Purple songs, so I went mostly to see Bruce, my favorite singer. It was in a smaller theatre and it was a fantastic experience. I’ll see him again next year, this time on a full solo concert!</li>
<li><a href="https://www.youtube.com/watch?v=dsa9ZnD2tWM">Roger Water’s “This Is Not A Drill” tour</a>, back in November. Absolutely incredible show, a mix of Pink Floyd classics and his own solo work (which I like), and most of all a very clear political statement. I also saw him back in 2018, which was also incredible, and I have no idea which one I’ve liked best. The production value of his shows are simply unmatched and I cried like a baby multiple times.</li>
<li><a href="https://www.youtube.com/watch?v=KWWkppRb7yU">Blind Guardian’s “The God Machine” tour</a>, also in November. A smaller venue, but the audience in Blind Guardian’s concerts makes you feel like there are thousands of people with you there. The band is just fantastic and I felt they played extra hard to match the energy of the audience!</li>
</ul>
<h2 id="2024-expectations">2024 Expectations</h2>
<p>Time for the predictions - hopefully I’ll get more right this time!</p>
<p>I expect to take big steps towards owning my own house. I really want to stop paying rent and hopefully get a house instead of an apartment. It’s more work, but it’s more fulfilling and freeing.</p>
<p>I expect to write even more on this blog, and continue to expand on the topics I post about. Dev articles aren’t going anywhere, but I don’t always have something to post about in that regard.</p>
<p>Also, I want to add proper support for categories and filtering, as well as search. As I post more, even I am starting to struggle finding something I wrote in the past to reference. Additionally, some code refactoring is always welcome.</p>
<p>I also want to travel a bit next year too - either to the US to meet my work colleagues or to Italy to see my brother. And of course, do some sightseeing!</p>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>Starting this post, I didn’t think there was much to write about 2023, but I ended up talking about it quite a bit. That’s great! I guess it’s the main point of these kinds of retrospectives - remembering what was noteworthy about the year and reflecting on it. I hope you enjoyed the read and see you next year!</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/cms/media/articles/2023-year-in-review/cover.png"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/cms/media/articles/2023-year-in-review/cover.png"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/css-isolation-property</guid>
      <title>CSS&apos;s &quot;isolation&quot; property is pretty cool</title>
      <description>I had never heard about it before, but it&apos;s a pretty clean way of solving z-index related issues.</description>
      <link>https://fantinel.dev/blog/css-isolation-property</link>
      <pubDate>Fri, 24 Nov 2023 13:15:59 +0000</pubDate>
      <category>Reading Recs</category><category>Front-End</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/css-isolation-property">
              read on the site!
            </a>
          </strong>
        </div>

        <blockquote>
<p><code>isolation</code> is a special property whose sole and unique purpose is to create a new stacking context on the element it is applied to, with no other side-effects. 😄</p>
<p>– Francesco Vetere, in <a href="https://dev.to/francescovetere/the-css-property-you-didnt-know-you-needed-3fk0">The CSS property you didn’t know you needed</a></p>
</blockquote>
<p>You learn something new everyday! Today, I learned about CSS’s <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/isolation">isolation</a> property. I learned it through <a href="https://dev.to/francescovetere/the-css-property-you-didnt-know-you-needed-3fk0">this Francesco Vetere post</a>, which I highly recommend you to read, as he explains it way better than I do.</p>
<p>Basically, <code>isolation: isolate</code> is a very simple way of creating a stacking context in CSS. Stacking context is basically a “reset” of how <code>z-index</code> works, so you can manage indexes in a smaller scope.</p>
<p>Francesco gives a perfect example of a card element with a decorative element in its background. To make that element appear below the text, you’d naturally add <code>z-index: -1;</code>, but that means the element would show below the card as well! By creating a new stacking context on the card, you can safely manage <code>z-index</code> without worrying about any child element appearing below/outside its parent.</p>
      ]]></content:encoded>
      
                
    </item>
  
    <item>
      <guid>https://fantinel.dev/default-apps-2023</guid>
      <title>My Default Apps: 2023</title>
      <description>I&apos;m jumping on the trend of posting my default apps!</description>
      <link>https://fantinel.dev/blog/default-apps-2023</link>
      <pubDate>Tue, 07 Nov 2023 12:11:50 +0000</pubDate>
      <category>Apps</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/default-apps-2023">
              read on the site!
            </a>
          </strong>
        </div>

        <p>I saw this on <a href="https://birchtree.me/blog/my-default-apps-at-the-end-of-2023/">Matt Birchler’s blog</a>, and then saw the multitude of people posting about their default apps in their own blogs. I’ve never jumped on a trend like this before, so I figured this would be a fun first one to do 😅.</p>
<p>I guess you can say my choices are pretty vanilla; I’ve been through my phase of trying a specific app for everything, but nowadays I’m settling down on things that actually work.</p>
<ul>
<li>📧 Mail service: <a href="https://fastmail.com">Fastmail</a></li>
<li>📬 Mail client: Apple Mail (Mac and iPhone)</li>
<li>✅ Tasks: <a href="https://notion.so">Notion</a>/<a href="https://ticktick.com/">TickTick</a></li>
<li>📰 RSS service: <a href="https://readwise.io/read">Readwise Reader</a></li>
<li>⌨️ Launcher: <a href="https://www.raycast.com/">Raycast</a></li>
<li>☁️ Cloud storage: iCloud</li>
<li>🌅 Photo library: iCloud</li>
<li>🌐 Web browser: <a href="https://arc.net/">Arc</a> on Mac, Safari on iPhone</li>
<li>📆 Calendar: Apple Calendar</li>
<li>📖 Reading: <a href="https://readwise.io/read">Readwise Reader</a></li>
<li>🌤️ Weather: Apple Weather</li>
<li>🎙️ Podcasts: I barely listen to any, but when I do, Apple Podcasts</li>
<li>🎶 Music: Apple Music</li>
<li>🛹 Clipboard manager: <a href="https://maccy.app/">Maccy</a></li>
<li>🔐 Passwords: <a href="https://1password.com/">1Password</a></li>
<li>💸 Budgeting: Apple Numbers</li>
<li>🐘 Mastodon: <a href="https://tapbots.com/ivory/">Ivory</a> (Mac and iPhone)</li>
<li>💁🏻‍♂️ Social: <a href="https://joinmastodon.org/">Mastodon</a> and <a href="https://join-lemmy.org/">Lemmy</a> (sporadically)</li>
<li>🎮 Gaming: PS5</li>
<li>🖼️ Screenshots: <a href="https://cleanshot.com/">CleanShot X</a></li>
<li>📝 Notes: <a href="https://notion.so">Notion</a></li>
<li>🧮 Code Editor: <a href="https://code.visualstudio.com/">Visual Studio Code</a></li>
<li>👨🏻‍💻 Terminal: <a href="https://iterm2.com/">iTerm 2</a></li>
<li>🔎 Search: <a href="https://kagi.com">Kagi</a></li>
</ul>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/cms/media/articles/default-apps-2023/cover.jpeg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/cms/media/articles/default-apps-2023/cover.jpeg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/belonging-somewhere</guid>
      <title>Belonging Somewhere</title>
      <description>I could always see parts of myself belonging to some groups or places, but never really felt like I was truly a part of anything.</description>
      <link>https://fantinel.dev/blog/belonging-somewhere</link>
      <pubDate>Sat, 04 Nov 2023 17:21:47 +0000</pubDate>
      <category>Personal</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/belonging-somewhere">
              read on the site!
            </a>
          </strong>
        </div>

        <p>Throughout my life, I’ve never really felt like I belonged somewhere. I could see parts of myself belonging to some groups or places, but never really felt like I was truly a part of anything.</p>
<p>During my childhood, I had some friends, and there was a group of kids/teens in my street that I would often hang out with, but to me it was always because of the activities we’d do, and not because I enjoyed being around them, or thought I was similar to them. We’d play football (<em>soccer</em>) in the street, play with our Beyblades or maybe play some Winning Eleven on our PS1s, but whenever the activity wasn’t one of these things, I just didn’t want to be there. I felt like an outsider that was just part of that group temporarily.</p>
<p>Now, this probably (certainly) is related to me being on the Autism spectrum (something I didn’t know back then), which does <em>literally</em> mean I wasn’t like them. The thing is, that feeling has kind of followed me through my whole life.</p>
<h2 id="local-culture">Local Culture</h2>
<p>I live in the southernmost state of Brazil, a state that is known to have a local culture that’s very different from the rest of the country. Nowadays, I know that there’s no such thing as an homogeneous Brazilian culture. Brazil is pretty much an amalgamation of thousands of different cultures, indigenous, african, european, asian, and the results of all of those meeting together. That’s what makes it great. But back then, to me, Brazilian culture was whatever was shown on national TV (I didn’t have access to local channels). And honestly, nothing shown there looked like anything we had here. So, it was hard to see myself as part of this country.</p>
<p>Around 10 years ago, I started a long-distance relationship with a girl that lived about 700km (430 miles) from me. Against all odds we made it work and we’re now happily married! Anyway, I remember when her parents came to visit my city for the first time. Her dad loved the “gaúcho” culture, and was looking forward to coming to my state and seeing firsthand what it was like, and to eat an authentic gaúcho barbecue. Little did he know… that’s not what it’s like here either.</p>
<blockquote>
<p>[!info]
“Gaúcho” is how the people born in the state of Rio Grande do Sul (like me) are called. The name is also used a lot in Uruguay and Argentina, since those countries share a border with my state and also share a lot of the same culture.</p>
</blockquote>
<p>You see, I do live in the state known for all that stuff, but the region I live in was populated waaaay later in history, by…. italian immigrants.</p>
<hr>
<h3 id="historical-context">Historical Context</h3>
<p>Back in the 1800s, the Empire of Brazil wanted to populate some areas of the south, partly to protect the land from neighboring countries, partly to push away the indigenous people. But what they also wanted was to “whiten the population”, so they started the process of bringing europeans from countries currently going through economic crisis, and bringing people that wanted to start over here. In this region, the majority came from Germany (in the 1820s), and Italy (in the 1870s).</p>
<p>This region was unpopulated before because it was hard to do so - it is very mountainous and with really dense forestation, so nobody wanted the trouble of building things here - not when they had better (easier) options still. When the immigrants came, they didn’t have much of a choice. They didn’t speak Portuguese, and all the best pieces of land were already taken. The Serra was the only place they could build on and own a piece of land. From what I’ve heard from my grandparents (and they heard from theirs), the government didn’t do much to help, so they pretty much grew here in a partially isolated manner - with their own culture brought from their origin countries and the life they built here.</p>
<hr>
<p>So, when my in-laws came here for the first time, they were surprised (and somewhat disappointed) that it was nothing like what they thought it would be. Instead of barbecue everywhere, we eat pasta and polenta. The accent was different. The architecture was different. Of course, since I live in the biggest city of the region we did eventually start getting drops of culture from everywhere, most of it being from the gaúcho culture, since it’s the nearest, but still it’s definitely not as native as they had expected.</p>
<p>I’m not really a fan of going out and seeing all of this culture firsthand (at least I wasn’t; as I’m getting older I’m starting to enjoy it more), but I did have my fair share of family gatherings and events that definitely had italian roots. So, whenever I saw people talk about Brazil or my state, I could never see myself or the people around me in any of it. My logical conclusion is that I didn’t belong to my country or state at all, and maybe, since I was an “internet citizen” since I was a kid and had a lot of (virtual) contact with cultures from around the world that felt more like me, maybe my place in the world would be somewhere else!</p>
<p>During my late teen years and early 20s, this idea of leaving the country was always present, and I’ve tried dipping my toes a couple times. I tried going to Canada to study English for a month to see how I’d like there, but unfortunately my visa was denied twice, and I gave up. Then I went to Europe for tourism, which was really fun! I thought that since my family came from there, then naturally that’d be the place I’d belong to.</p>
<p>Last year, I moved to Italy for 3 months, in order to get my italian citizenship recognized. The original idea was to stay there after those 3 months, find a place to live, and leave Brazil behind. But, as the date approached, my wife and I realized we weren’t really comfortable with the idea of leaving everything behind and jumping into the unknown. So, we decided to use those 3 months as a test to see how much we liked it, and if we missed Brazil at all. We were wise.</p>
<p>Those 3 months were… tough. Of course, I was naturally uncomfortable with all the changes involved in moving across the Atlantic, but we moved into a really, <strong>really</strong> small town (with around 300 people), stayed in a house that had at least 300 years and we were in the middle of nowhere. Now, I’m really trying to not be unfair and judge an entire country by just a small town, and I do believe Italy is a great place to live. But, after all that, I think the biggest feeling my wife and I had was not that we hated where we were - but that we missed our <strong>home</strong> so much.</p>
<p>Suddenly, it all became clear. Even though we lived in an <em>italian-brazilian</em> place, it still was <em>brazilian</em>. That mix of different people might be more subtle than in other parts of the country, but it’s still there. And it’s still <strong>amazing</strong>. The people, the weather, the food, the music, it’s all unique to this small, very special part of the world. Special because it’s mine.</p>
<hr>
<p>As a recently diagnosed autistic person, I’m now learning a lot about myself, and I’ve realized I’ve always struggled with my sense of identity. How can I learn who I am if I never see much of myself anywhere? It’s not an easy journey, and honestly I don’t think it has an end. Our identity changes all the time and is molded by what’s around us. This feeling of belonging is unique to each person, some find it at an early age, some never do. I always thought I’d be the latter.</p>
<p>Today, I was watching local TV and they were covering a festival celebrating local culture. More specifically, the italian-brazilian culture that developed here after all these years. I watched that and thought: <em>“Yeah, I belong here”</em>.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/cms/media/articles/belonging-somewhere/cover.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/cms/media/articles/belonging-somewhere/cover.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/mario-kart-tailwind</guid>
      <title>“Classic rock, Mario Kart, and why we can&apos;t agree on Tailwind”</title>
      <description>Great article from Josh Collinsworth explaining why Tailwind is good and bad for the exact same reasons.</description>
      <link>https://fantinel.dev/blog/mario-kart-tailwind</link>
      <pubDate>Sat, 14 Oct 2023 15:07:19 +0000</pubDate>
      <category>Reading Recs</category><category>Front-End</category><category>Dev</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/mario-kart-tailwind">
              read on the site!
            </a>
          </strong>
        </div>

        <blockquote>
<p>Likewise, I suspect most people on opposing sides of the Tailwind debate actually completely agree on Tailwind itself. I don’t think our divide centers on atomic CSS, or utility classes; I think the contention comes from valuations we made long before we ever chose our tools. Where one of us sees a selling point, the other sees a flaw.– Josh Collinsworth, in <a href="https://joshcollinsworth.com/blog/tailwind-is-smart-steering?utm_source=matt-fantinel">Classic rock, Mario Kart, and why we can’t agree on Tailwind</a></p>
</blockquote>
<p>I’ve found this gem of an article a while ago, and have been thinking constantly about it ever since. I think its idea of things being good and bad for the same reasons is spot on - and also the reason why we’ll never agree on them.</p>
<p>I also dislike the idea of Tailwind, which made me never interested in using it. I prefer having total control of my styles and enjoy a clear separation between templates and styles. This, I guess, makes me a “crafter”.</p>
      ]]></content:encoded>
      
                
    </item>
  
    <item>
      <guid>https://fantinel.dev/open-web-and-the-masses</guid>
      <title>The Open Web and &quot;the masses&quot;</title>
      <description>A rant about the Open Web and how it is not for everyone - but that&apos;s okay.</description>
      <link>https://fantinel.dev/blog/open-web-and-the-masses</link>
      <pubDate>Tue, 19 Sep 2023 21:28:46 +0000</pubDate>
      <category>Misc</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/open-web-and-the-masses">
              read on the site!
            </a>
          </strong>
        </div>

        <p>Back when I started using the internet, most of its content was split between millions of different websites and platforms. They were all accessible via search engines, and there were plenty of websites that existed only to aggregate content from others. There were social networks (Orkut was a huge hit in Brazil), but their scale never reached the levels we see today, and overall, <strong>if you wanted to see anything, you’d have to look for it.</strong></p>
<p>Cut to today, most of the content is either hosted on a handful of platforms or at least found inside them. Large scale social networks and search engines that are increasingly displaying information without requiring you to open another website have made it incredibly easy to find content you’d never find otherwise.</p>
<p>This convenience has led to a boom in internet usage. Before, you had to be a “tech person” or at least slightly interested in tech to be able to understand how the internet worked, and then be able to find the good stuff. This transformation in how we find and see things would be great, if not for a huge, non-obvious problem: we are now locked in to those platforms.</p>
<p>Ideally, that wouldn’t be a big deal. Most of those platforms are free, right? Well, not really. You pay with your data, with being the subject of psychological experiments, and with the price of becoming dependent on it. If you don’t know what I’m talking about, I highly recommend watching <a href="https://www.thesocialdilemma.com/">The Social Dilemma documentary</a> on Netflix (I know, another locked-in platform. But there’s some nice content on the website itself).</p>
<p>To many people, deleting a Facebook/Instagram/Twitter account would mean they will no longer see updates from things they care about (mixed with things they don’t). To many journalists/writers/brands, deleting an account would mean losing the majority of their traffic. Some companies even base their entire business on a single platform, as can be often seen in Brazil where WhatsApp is often the only way to contact/hire/buy something.</p>
<p>So, can we really call the web open, or free? If we are virtually obligated to sign up and* *accept the terms and conditions of at least one overly powerful corporation to be able to have a “normal life”, can we really say we have a choice?</p>
<p>Maybe.</p>
<h2 id="swimming-against-the-tide">Swimming Against The Tide</h2>
<p>There’s been quite a resurgence of some technologies and concepts that go against all that. Mostly, in the form of personal blogs and them being accessed via RSS readers. Plus, decentralized social media is now a thing and has gathered a relatively significant audience, especially since Twitter’s destruction.</p>
<p>If you don’t know what RSS readers and decentralized social media are, yeah, that’s their main issue. They are systems that aim to give you some of the convenience of social media feeds back, but without the downsides!</p>
<ul>
<li><strong>RSS</strong> is a method of sharing content in a standardized way. A lot of websites have RSS feeds (including mine), which are files that hold the content of, for example, all the blog posts. This means that if you use a RSS reader app, you can read whatever I write, and all the new posts will be automatically delivered to you! You just have to subscribe.
<ul>
<li>RSS has existed for a long time, and most websites had a feed. When social media got popular, however, the number of websites with a RSS feed started getting smaller. Nowadays, it seems that the number of websites offering them has started growing again.</li>
<li>There are a bunch of RSS reader apps, in all shapes and forms. Some of the ones I know are <a href="https://feedly.com/">Feedly</a> (free, for all platforms), <a href="https://netnewswire.com/">NetNewsWire</a> (free, for iPhone, iPad and Mac), <a href="https://feedbin.com/home">Feedbin</a> (subcription, for Web, iPhone and iPad) and the one I use is <a href="https://readwise.io/read">Readwise Reader</a> (subscription, for Web, iPhone and Android)</li>
</ul>
</li>
<li><strong>Decentralized Social Media</strong> are, as implied, social media services that communicate with one another. I wrote a few months ago about <a href="https://fantinel.dev/from-twitter-to-mastodon">my migration from Twitter to Mastodon</a>, and Mastodon is only one of many in an universe of projects that can talk with one another. What does this mean?
<ul>
<li>It means that, even if I have my profile on a specific Mastodon server, I can still follow and interact with people that created their account on a different social media. So, nobody is virtually obligated to have an account in a specific place;</li>
<li>It also means that I can take all my connections with me in case I wanna move from one service to another. I’m never stuck using a service I don’t like just because my friends or followers are there;</li>
</ul>
</li>
</ul>
<p>My use of the internet is now heavily based on both of these systems; I follow some news websites’ RSS feeds so I can read the news in a single place while drinking a cup of coffee; I follow cool people’s blogs via RSS so I can read all the cool stuff they’re writing about; and I can also follow the same cool people on Mastodon (even though some use something else) to see they complain about the heat or make bad jokes in their social media.</p>
<p>This is very nice! And the best thing is - if I ever wanna start using another app, or change devices, I can do that without losing anything. It’s liberating. It’s the <strong>Open Web</strong>.</p>
<h2 id="but-how-feasible-is-it">But how feasible is it?</h2>
<p>Most of the previous section was spent explaining concepts. They’re not really that complex, but the fact that they need any explanation at all is already a sign that they’re not feasible for “the masses”.</p>
<p>You see, if you are still reading this, you’re probably in a small group of people that actually cares about their online experience. For most people, being locked in a service isn’t really a concern, which is why those services grow so big. It’s unfortunate, but the convenience those services provide is (or was, at some point, when they got us hooked) so big that I can’t think of anything that could create any kind of mass exodus from them. The closest we’ve ever got to it was the Twitter → Mastodon migration, and that was around what, 10 million people? 10 million is a lot for Mastodon, but only a fraction of Twitter’s userbase.</p>
<hr>
<p>But honestly, that’s fine. As I’ve recently started to realize more and more, it really doesn’t matter if what I’m using or doing is popular or not. If I do things a certain way, the only person it needs to work for is me. And honestly, the social bubble I’m in is really fond of the Open Web too! There are nice discussions, and it never feels like a small group. In fact, there’s always new people around!</p>
<p>I think I’m gonna create a page with suggestions of nice RSS feeds to follow. One more thing to my to-do list, I guess 😅</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/cms/media/articles/open-web-and-the-masses/cover.jpeg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/cms/media/articles/open-web-and-the-masses/cover.jpeg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/review-zelda-tears-of-the-kingdom</guid>
      <title>My Review of The Legend of Zelda: Tears of the Kingdom</title>
      <description>A spoiler-free review of the ultimate Zelda game</description>
      <link>https://fantinel.dev/blog/review-zelda-tears-of-the-kingdom</link>
      <pubDate>Sat, 16 Sep 2023 14:29:19 +0000</pubDate>
      <category>Games</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/review-zelda-tears-of-the-kingdom">
              read on the site!
            </a>
          </strong>
        </div>

        <blockquote>
<p>[!success]
This review is <strong>spoiler-free</strong>, so you can safely read it even if you haven’t played or finished the game yet.</p>
</blockquote>
<p>The Legend of Zelda is my favorite game series of all time. Understandably, I was pretty excited for Tears of the Kingdom to come out. And it took its time! Announced in 2019, two years after the release of its predecessor (Breath of the Wild), a lot of people thought it would have a shorter development cycle, considering it seemed to feature the same world as Breath of the Wild’s, the graphics were the same, and the engine was already done. How wrong we were!</p>
<p>Instead, Tears of the Kingdom (which I’m gonna call “TOTK” from here on for brevity) had at least a 5-year development cycle, almost as long as BOTW’s. We had a global pandemic in the meantime, which definitely slowed things down. Still, it was a lot of time for “just a sequel”. And, to be honest, while the first trailers were exciting because they pointed to a return to BOTW’s world, they also were a bit lackluster, as they didn’t show much new.</p>
<p>You see, Breath of the Wild’s release was a pivotal moment in the gaming industry. In theory, it didn’t introduce a lot of new things - open world games have existed for a while, even the first Legend of Zelda from 1986 was like that! But in practice, it brought a world that was so fun to explore, so seamless to traverse, and its gameplay had so many possibilities, that there was nothing quite like it. It also reinvented the series, which had been stagnant for some years, with games of high quality but that weren’t really hitting the mark.</p>
<p>That was in 2017, though, and games have evolved since then. With BOTW’s relevance and success, a lot of games started taking it as an inspiration (and almost every open world game was compared to it somehow). So, when TOTK was shown and it didn’t seem to offer a lot new, I was a bit worried it’d be more of the same.</p>
<p>And then <strong>BAM!</strong> that final trailer shows up.</p>
<iframe style="width: 100%;aspect-ratio:16/9;border-radius:10px;box-shadow:var(--image-shadow);" src="https://www.youtube-nocookie.com/embed/uHGShqcAHlQ" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe>
<p><em>It is incredible!</em> It shows new story bits, but more importantly, a <strong>lot</strong> of new gameplay mechanics, abilities, and some of them are so unique that I don’t think anyone could have imagined they’d be in a Zelda game. And so, after years of waiting for the game to come out and an extra month of waiting for my copy to be delivered, I finally played it.</p>
<h2 id="the-new-abilities">The New Abilities</h2>
<p>The game begins quite differently than it did in BOTW. In this game, the idea of being able to go anywhere anytime is still present, but not taken to extremes (no, you can’t go straight to the final boss right after the tutorial). We have a linear story-focused tutorial, where the main plot begins, and then we are introduced to Link’s new abilities in this game. They are a huge step up from BOTW’s abilities, which were fun but feel very limited now.</p>
<p>There are 4 main abilities:</p>
<ul>
<li>Ultrahand, which lets you manipulate most objects in the game, and then <em>stick</em> them on other objects. It’s an improved version of Magnesis from BOTW, and probably the one you use the most;</li>
<li>Recall, which lets you make an object go back in time (and space). You can make some really great combos with this and Ultrahand, like using Ultrahand to hold a platform up high, then let it fall, and use Recall to have it go back up with you on top of it;</li>
<li>Fuse, which lets you fuse any material to your weapon/shield/arrows, improving its attack power or giving some status effects;</li>
<li>Ascend, which lets you <em>swim</em> through any solid material, as long as its right on top of you. Honestly, I thought this one was going to be the lamest one, but it was the biggest surprise! I used it so much and always felt smart when I did;</li>
</ul>
<p>The abilities are less specialized, but more powerful, which gives you so much freedom to use them! 80h into the game, I was still figuring out new ways to use and combine them.</p>
<h2 id="improvements-over-botw">Improvements over BOTW</h2>
<p>I believe TOTK fixes or remediates all of BOTW’s problems. The story isn’t told only through a series of flashbacks (though flashbacks are still a thing); the world feels much more alive (which makes sense after BOTW); the soundtrack is more varied; and weapon durability is fixed.</p>
<p><em>Weapon durability is fixed? Does that mean they don’t break anymore?</em> No, actually they break much faster now 😅. You see, the main problem with BOTW’s weapon durability system is that it was very frustrating. Fighting monsters was often not worth it because it’d make your weapons break, and there was a big chance that whatever new weapons you’d get from fighting them would be worse.</p>
<p>In TOTK, all weapons are now “decayed”, which means their durability is even lower. But, remember the Fuse ability? Whenever you fuse a material to your weapons, they become stronger and more durable. And most of the better materials are obtained from… killing monsters! So, chances are, if you fight monsters you’ll probably end up with better materials that make your weapons stronger than before! It’s as it should be.</p>
<p>I admire that, after all the backlash this system had in BOTW, they didn’t simply remove it, and instead figured out a way to make that game system enjoyable and unique. Mix-and-matching weapons and materials was a fun part of the game, and made it a lot more varied.</p>
<h2 id="hylian-engineering">Hylian Engineering</h2>
<p>Ultrahand lets you attach objects to each other, and there are a ton of objects in the world, including new Zonai devices, which include things like motorized wheels, steering sticks, and fans. They could have half-assed this mechanic and limit how you can attach things to each other, or the number of objects you can use. But nope, they went all the way. That final trailer gives you a taste of just how flexible it is, with Link riding a tank made of stone, with wheels and arms (at the 2:33 mark), and that only scratches the surface.</p>
<p>I had a lot of fun building things! Honestly, most of the stuff I built either didn’t work or was a car that was slower than walking, but the fun of experimenting was worth it. By the end of the game, I was already building flying machines to cross Hyrule with.</p>
<p><img src="https://fantinel.dev/cms/media/articles/review-zelda-tears-of-the-kingdom/link-and-zelda.JPG" alt="Link and Zelda in their royal clothes in a forest"></p>
<h2 id="story">Story</h2>
<p>It’s hard to talk about the story without spoiling, but I just want to say: it is <strong>very good</strong>. Even with all the freedom and gameplay possibilities of the game, it doesn’t feel like the story takes a backseat this time. It has incredible moments, great pacing, and honestly one of the best endings I’ve ever seen in a game. It brings back some Zelda conventions but also has some unexpected twists that keep it from being predictable. Zelda games were never known for having a super complex and nuanced story, but as far as heroic adventure games go, this is one of the best.</p>
<h2 id="problems">Problems</h2>
<p>This review has been very positive so far, which doesn’t mean the game doesn’t have problems. But honestly, they are a bit hard to identify now because they’re very minor and they don’t stain the overall experience. You might notice them while playing, but they don’t stick out in your memory after you play, if that makes sense.</p>
<p>The main issue of the game might be a huge deal to some people, but less so to others. The thing is, <strong>this game has so much of everything!</strong> There are so many mechanics, sidequests, places to explore, things to find and collect, stories to see, people to save. And honestly, besides some of the collectibles, which are understandably many because the world is so big, all of them are pretty high quality. I never found a sidequest that I just didn’t want to do.</p>
<p>The problem is, it can become quite overwhelming. If you’re one of those people that feel overwhelmed by having many (optional) things to do, you will feel overwhelmed with this game. When I first started playing I felt like that, and I wanted to do everything, because it was so good. Of course, eventually the feeling of <strong>having</strong> to do everything made it a bit of a chore to play. I realized this and also realized that the game was not making me do any of those things at all. It was just giving me the freedom of choosing what I liked best. So, I started focusing on some of my favorite things and decided to progress the main story until I got to the ending.</p>
<p>Not everyone will be able to do that, though. And I can totally see those people giving up on the game because it’s just too much.</p>
<p><img src="https://fantinel.dev/cms/media/articles/review-zelda-tears-of-the-kingdom/balloon.JPG" alt="Screenshot of the game, with a hot air balloon flying, with the sunrise in the horizon"></p>
<h2 id="verdict">Verdict</h2>
<p>Tears of the Kingdom is the ultimate Zelda game - and to me, also the ultimate open-world game. I finished the main story and did a lot of side content, but after 95 hours, I only played a fraction of what the game has to offer.</p>
<p><img src="https://fantinel.dev/cms/media/articles/review-zelda-tears-of-the-kingdom/tropical-village.JPG" alt="Screenshot of the game, showing a tropical village with palm trees and huts. A lot of people are partying around bonfires"></p>
<p>I believe its impact is not as big as Breath of the Wild’s was, though, at least not to me personally. That was a rebirth of the series, and it came at a moment of big changes in my life. TOTK, in comparison, doesn’t fully reinvent anything, but instead improves (greatly) all the reinventions that BOTW did. It’s not fair to judge a game by its impact alone though. If every single game reinvented everything, then reinvention wouldn’t matter.</p>
<p>If BOTW was a 10/10 to me, because of its impact and just how good it was, then there’s no way TOTK, which improves on BOTW in every single way, could be anything less than that.</p>
<p>So, yeah, @@Tears of the Kingdom is a 10/10@@. It is a masterpiece, one of the best games I’ve ever played, and I’ll remember it fondly for years to come. It will be <strong>very</strong> hard for the Zelda team to ever top this game, but if anyone can do it, it’s them.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/cms/media/articles/review-zelda-tears-of-the-kingdom/cover.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/cms/media/articles/review-zelda-tears-of-the-kingdom/cover.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/automatically-manage-node-versions</guid>
      <title>How to automatically manage Node versions</title>
      <description>Tired of manually switching Node versions for different projects? Discover how to automate the process using nvm.</description>
      <link>https://fantinel.dev/blog/automatically-manage-node-versions</link>
      <pubDate>Sun, 10 Sep 2023 17:26:42 +0000</pubDate>
      <category>Dev</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/automatically-manage-node-versions">
              read on the site!
            </a>
          </strong>
        </div>

        <p>If you’re like me, you’re constantly jumping between projects, and often those projects need different versions of NodeJS. Ideally, all of them would be using (or compatible with) the latest version, but in reality a lot of those projects are old and you just don’t have the time to update them and make sure all dependencies play nice.</p>
<p>When jumping between them, it’s usually a pain to remember which Node version to use and have to switch to that version. Tools like <a href="https://github.com/nvm-sh/nvm">nvm</a> make it easy to switch, but it’s still a manual task that we often forget to do.</p>
<p>Turns out, you can use nvm to automatically switch the NodeJS version when you <code>cd</code> into a folder! All you need is a file in your project and a few lines of magic in your bash/zsh profile.</p>
<h2 id="configuring-your-shell-profile-to-change-node-versions">Configuring your shell profile to change Node versions</h2>
<p>The first step is to edit your shell profile file, so that every time you <code>cd</code> into a folder, it checks if there’s a <code>.nvmrc</code> file and if so, it switches to that Node version.</p>
<h3 id="if-using-zsh">If using zsh</h3>
<p>If you’re using zsh, you can add the following to your <code>.zshrc</code> file:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="shell"><code><span class="line"><span style="color:#F97583">~</span><span style="color:#E1E4E8">filename .zshrc</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D"># Automatically use Node version specified via .nvmrc file</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D"># place this after nvm initialization!</span></span>
<span class="line"><span style="color:#79B8FF">autoload</span><span style="color:#79B8FF"> -U</span><span style="color:#9ECBFF"> add-zsh-hook</span></span>
<span class="line"><span style="color:#B392F0">load-nvmrc</span><span style="color:#E1E4E8">() {</span></span>
<span class="line"><span style="color:#F97583">  local</span><span style="color:#E1E4E8"> node_version</span><span style="color:#F97583">=</span><span style="color:#9ECBFF">"$(</span><span style="color:#B392F0">nvm</span><span style="color:#9ECBFF"> version)"</span></span>
<span class="line"><span style="color:#F97583">  local</span><span style="color:#E1E4E8"> nvmrc_path</span><span style="color:#F97583">=</span><span style="color:#9ECBFF">"$(</span><span style="color:#B392F0">nvm_find_nvmrc</span><span style="color:#9ECBFF">)"</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">  if</span><span style="color:#E1E4E8"> [ </span><span style="color:#F97583">-n</span><span style="color:#9ECBFF"> "</span><span style="color:#E1E4E8">$nvmrc_path</span><span style="color:#9ECBFF">"</span><span style="color:#E1E4E8"> ]; </span><span style="color:#F97583">then</span></span>
<span class="line"><span style="color:#F97583">    local</span><span style="color:#E1E4E8"> nvmrc_node_version</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">$(</span><span style="color:#B392F0">nvm</span><span style="color:#9ECBFF"> version</span><span style="color:#9ECBFF"> "$(</span><span style="color:#B392F0">cat</span><span style="color:#9ECBFF"> "${</span><span style="color:#E1E4E8">nvmrc_path</span><span style="color:#9ECBFF">}")"</span><span style="color:#E1E4E8">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">    if</span><span style="color:#E1E4E8"> [ </span><span style="color:#9ECBFF">"</span><span style="color:#E1E4E8">$nvmrc_node_version</span><span style="color:#9ECBFF">"</span><span style="color:#F97583"> =</span><span style="color:#9ECBFF"> "N/A"</span><span style="color:#E1E4E8"> ]; </span><span style="color:#F97583">then</span></span>
<span class="line"><span style="color:#B392F0">      nvm</span><span style="color:#9ECBFF"> install</span></span>
<span class="line"><span style="color:#F97583">    elif</span><span style="color:#E1E4E8"> [ </span><span style="color:#9ECBFF">"</span><span style="color:#E1E4E8">$nvmrc_node_version</span><span style="color:#9ECBFF">"</span><span style="color:#F97583"> !=</span><span style="color:#9ECBFF"> "</span><span style="color:#E1E4E8">$node_version</span><span style="color:#9ECBFF">"</span><span style="color:#E1E4E8"> ]; </span><span style="color:#F97583">then</span></span>
<span class="line"><span style="color:#B392F0">      nvm</span><span style="color:#9ECBFF"> use</span></span>
<span class="line"><span style="color:#F97583">    fi</span></span>
<span class="line"><span style="color:#F97583">  elif</span><span style="color:#E1E4E8"> [ </span><span style="color:#9ECBFF">"</span><span style="color:#E1E4E8">$node_version</span><span style="color:#9ECBFF">"</span><span style="color:#F97583"> !=</span><span style="color:#9ECBFF"> "$(</span><span style="color:#B392F0">nvm</span><span style="color:#9ECBFF"> version default)"</span><span style="color:#E1E4E8"> ]; </span><span style="color:#F97583">then</span></span>
<span class="line"><span style="color:#79B8FF">    echo</span><span style="color:#9ECBFF"> "Reverting to nvm default version"</span></span>
<span class="line"><span style="color:#B392F0">    nvm</span><span style="color:#9ECBFF"> use</span><span style="color:#9ECBFF"> default</span></span>
<span class="line"><span style="color:#F97583">  fi</span></span>
<span class="line"><span style="color:#E1E4E8">}</span></span>
<span class="line"><span style="color:#B392F0">add-zsh-hook</span><span style="color:#9ECBFF"> chpwd</span><span style="color:#9ECBFF"> load-nvmrc</span></span>
<span class="line"><span style="color:#B392F0">load-nvmrc</span></span></code></pre>
<h3 id="if-using-bash">If using bash</h3>
<p>With bash, you can add the following to your <code>.bash_profile</code> file:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="bash"><code><span class="line"><span style="color:#F97583">~</span><span style="color:#E1E4E8">filename .bash_profile</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D"># Automatically use Node version specified via .nvmrc file</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D"># place this after nvm initialization!</span></span>
<span class="line"><span style="color:#B392F0">load_nvmrc</span><span style="color:#E1E4E8">() {</span></span>
<span class="line"><span style="color:#F97583">  local</span><span style="color:#E1E4E8"> node_version</span><span style="color:#F97583">=</span><span style="color:#9ECBFF">"$(</span><span style="color:#B392F0">nvm</span><span style="color:#9ECBFF"> version)"</span></span>
<span class="line"><span style="color:#F97583">  local</span><span style="color:#E1E4E8"> nvmrc_path</span><span style="color:#F97583">=</span><span style="color:#9ECBFF">"$(</span><span style="color:#B392F0">nvm_find_nvmrc</span><span style="color:#9ECBFF">)"</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">  if</span><span style="color:#E1E4E8"> [ </span><span style="color:#F97583">-n</span><span style="color:#9ECBFF"> "</span><span style="color:#E1E4E8">$nvmrc_path</span><span style="color:#9ECBFF">"</span><span style="color:#E1E4E8"> ]; </span><span style="color:#F97583">then</span></span>
<span class="line"><span style="color:#F97583">    local</span><span style="color:#E1E4E8"> nvmrc_node_version</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">$(</span><span style="color:#B392F0">nvm</span><span style="color:#9ECBFF"> version</span><span style="color:#9ECBFF"> "$(</span><span style="color:#B392F0">cat</span><span style="color:#9ECBFF"> "${</span><span style="color:#E1E4E8">nvmrc_path</span><span style="color:#9ECBFF">}")"</span><span style="color:#E1E4E8">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">    if</span><span style="color:#E1E4E8"> [ </span><span style="color:#9ECBFF">"</span><span style="color:#E1E4E8">$nvmrc_node_version</span><span style="color:#9ECBFF">"</span><span style="color:#F97583"> =</span><span style="color:#9ECBFF"> "N/A"</span><span style="color:#E1E4E8"> ]; </span><span style="color:#F97583">then</span></span>
<span class="line"><span style="color:#B392F0">      nvm</span><span style="color:#9ECBFF"> install</span></span>
<span class="line"><span style="color:#F97583">    elif</span><span style="color:#E1E4E8"> [ </span><span style="color:#9ECBFF">"</span><span style="color:#E1E4E8">$nvmrc_node_version</span><span style="color:#9ECBFF">"</span><span style="color:#F97583"> !=</span><span style="color:#9ECBFF"> "</span><span style="color:#E1E4E8">$node_version</span><span style="color:#9ECBFF">"</span><span style="color:#E1E4E8"> ]; </span><span style="color:#F97583">then</span></span>
<span class="line"><span style="color:#B392F0">      nvm</span><span style="color:#9ECBFF"> use</span></span>
<span class="line"><span style="color:#F97583">    fi</span></span>
<span class="line"><span style="color:#F97583">  elif</span><span style="color:#E1E4E8"> [ </span><span style="color:#9ECBFF">"</span><span style="color:#E1E4E8">$node_version</span><span style="color:#9ECBFF">"</span><span style="color:#F97583"> !=</span><span style="color:#9ECBFF"> "$(</span><span style="color:#B392F0">nvm</span><span style="color:#9ECBFF"> version default)"</span><span style="color:#E1E4E8"> ]; </span><span style="color:#F97583">then</span></span>
<span class="line"><span style="color:#79B8FF">    echo</span><span style="color:#9ECBFF"> "Reverting to nvm default version"</span></span>
<span class="line"><span style="color:#B392F0">    nvm</span><span style="color:#9ECBFF"> use</span><span style="color:#9ECBFF"> default</span></span>
<span class="line"><span style="color:#F97583">  fi</span></span>
<span class="line"><span style="color:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">if</span><span style="color:#E1E4E8"> [ </span><span style="color:#F97583">-n</span><span style="color:#9ECBFF"> "</span><span style="color:#E1E4E8">$BASH_VERSION</span><span style="color:#9ECBFF">"</span><span style="color:#E1E4E8"> ]; </span><span style="color:#F97583">then</span></span>
<span class="line"><span style="color:#E1E4E8">  PROMPT_COMMAND</span><span style="color:#F97583">=</span><span style="color:#9ECBFF">"load_nvmrc; </span><span style="color:#E1E4E8">$PROMPT_COMMAND</span><span style="color:#9ECBFF">"</span></span>
<span class="line"><span style="color:#F97583">fi</span></span>
<span class="line"></span></code></pre>
<h2 id="creating-a-nvmrc-file">Creating a .nvmrc file</h2>
<p>Inside your project folder, create a file called <code>.nvmrc</code> and add the Node version you want to use. For example, if you want to use Node 14.18.0, you’d add the following to the file:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="shell"><code><span class="line"><span style="color:#B392F0">v14.18.0</span></span></code></pre>
<p><strong>You need to do this for every project</strong> that needs a specific NodeJS version. If you don’t add a <code>.nvmrc</code> file, nvm will use the default.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/cms/media/articles/automatically-manage-node-versions/Cover.png"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/cms/media/articles/automatically-manage-node-versions/Cover.png"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/iron-maiden-killers-concept-album</guid>
      <title>Iron Maiden&apos;s &quot;Killers&quot; is a concept album</title>
      <description>Seriously, hear me out</description>
      <link>https://fantinel.dev/blog/iron-maiden-killers-concept-album</link>
      <pubDate>Fri, 16 Jun 2023 00:00:00 +0000</pubDate>
      <category>Misc</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/iron-maiden-killers-concept-album">
              read on the site!
            </a>
          </strong>
        </div>

        <p>So, today I was doing the dishes and decided to listen to some music on my headphones. I was in the mood for some Iron Maiden, so I shuffled my playlist of my favorites from them (which includes almost every one of their songs 😆). The first track to play was <em>Drifter</em>, the last song from their second album, <em>Killers</em>. It’s a really good song so I decided to play the entire album instead.</p>
<p>I’ve listened to this album countless times, but I’d never really paid attention to the lyrics. So, as the album played, I was surprised by the small <em>plot twist</em> at the end of <em>Murders In The Rue Morgue</em>, which was one of the songs I never really paid much attention to the lyrics. I was like <em>“wait, what?!”</em> and decided to pay more attention to the lyrics of the rest of the album. And then it hit me: @@Killers is a concept album@@, with all songs being sung by the same character!</p>
<blockquote>
<p>[!warning]
The album touches on the subjects of murder, suicide and drugs, so be wary of proceeding if this is a sensitive topic for you.</p>
</blockquote>
<p>So, let’s go through each song and see how they fit together. Feel free to follow along with the music:</p>
<iframe style="border-radius:12px" src="https://open.spotify.com/embed/album/5REF2imQI3lMAmeWcDXE3D?utm_source=generator" width="100%" height="352" frameborder="0" allowfullscreen allow="autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture" loading="lazy"></iframe>
<h2 id="the-ides-of-march">The Ides Of March</h2>
<p>This song is instrumental, so there’s not much to analyse here (the music is awesome though!). All we have is the name of the song, which refers to the fate of Julius Caesar, most famously retold by Shakespeare, after he was warned to “beware the Ides of March”; he was brutely murdered on that date.</p>
<p>So, yeah, the album starts with a warning of what’s to come. It’s no secret that a concept album called “Killers” is going to be about murder, right?</p>
<h2 id="wrathchild">Wrathchild</h2>
<p>Here we get introduced to the narrator (which we’ll call “Wrathchild” from now on). He seems to have had a very troubled childhood, as seen below:</p>
<blockquote>
<p>I was born into a scene of angriness and greed
Dominance and persecution
My mother was a queen, my dad I’ve never seen
I was never meant to be</p>
</blockquote>
<p>This obviously isn’t good and a child that goes through that kind of trauma will likely have issues in the future - as seen in the next verse, where the Wrathchild talks about an obsessive wish to meet his father.</p>
<blockquote>
<p>And now I spend my time looking all around
For a man that’s nowhere to be found
Until I find him, I’m never gonna stop searching
I’m gonna find my man, gonna travel around
(…)
Say it doesn’t matter, ain’t nothin’ gonna alter
The course of my destination
know I’ve got to find some serious peace of mind
Or I know I’ll just go crazy</p>
</blockquote>
<p>Already feeling the consequences of a troubled childhood, the Wrathchild starts traveling around in search of his father. Which leads us to…</p>
<h2 id="murders-in-the-rue-morgue">Murders In The Rue Morgue</h2>
<p>Honestly the lyrics of this song are much more straightforward since it’s a more direct narration of what’s going on. I’m gonna bring over only the most relevant parts:</p>
<blockquote>
<p>I was strolling through the streets of Paris
And then I heard a piercing scream And I rushed to the scene of the crime
But all I found was the butchered remains Of two girls layin’ side by side</p>
</blockquote>
<p>The Wrathchild (who was traveling around in search of their father) is now in Paris and suddenly finds the scene of a murder in the street.</p>
<blockquote>
<p>There’s some people coming down the street
At last someone heard my call
Can’t understand why they’re pointing at me
I never done nothing at all
But I got some blood on my hands
Because everybody’s shouting at me
I can’t speak French so I couldn’t explain
And like a fool I started running away</p>
</blockquote>
<p>People start accusing the Wrathchild as the murderer, and he can’t defend himself since he doesn’t speak the local language. He starts running away out of panic, and then starts being persecuted by the law, which makes him flee France through the southern border, to Italy.</p>
<blockquote>
<p>If I could go to somebody for help
To get me out of trouble for sure
But I know that it’s on my mind
That my doctor said I’ve done it before</p>
</blockquote>
<p>… Ooooh! That last line is a big plot twist! This means that the Wrathchild might actually be the murderer, he just doesn’t remember it, and his doctor has told him something similar has happened before. To me, this strongly implies <a href="https://en.wikipedia.org/wiki/Dissociative_identity_disorder">Dissociative Identity Disorder (DID)</a>, which often originates from trauma (the troubled childhood), and would explain how the Wrathchild could have murdered someone and not remember it. It also explains the “I got some blood on my hands” line earlier in the song, which apparently wasn’t a metaphor…</p>
<p>It’s also worth noting that it seems that the <em>killer</em> personality might be the one that’s causing the urge to run away, as that personality knows what it’s done.</p>
<h2 id="another-life">Another Life</h2>
<blockquote>
<p>As I lay here lying on my bed
Sweet voices come into my head
(…)
There’s a feeling that’s inside me
Telling me to get away, yeah
But I’m so tired of living
I might as well end today</p>
</blockquote>
<p>This song’s lyrics and pretty short. Here, the Wrathchild talks about hearing voices in his head, and these voices are telling him to “get away”. It’s probably the voice of the <em>killer</em> personality from the previous song. The Wrathchild is tired of living and contemplates suicide.</p>
<h2 id="genghis-khan">Genghis Khan</h2>
<p>This is another instrumental song and doesn’t really tie in to the rest of the album’s theme - except for the fact the Genghis Khan killed a lot of people, I guess?</p>
<h2 id="innocent-exile">Innocent Exile</h2>
<blockquote>
<p>My life is so empty, nothing to live for
My mind is all confusion, ‘cos I defied the law
When you weren’t there to help me, I lost my mind and ran
I never had no trouble before this all began, yeah</p>
</blockquote>
<p>In this song, the Wrathchild is still suffering the consequences of the Murder in the Rue Morgue. They claim their mind is confused (which makes sense because of DID).</p>
<p>Another <strong>really</strong> interesting line is when he says he “never had no trouble before this all began”. Usually cases of DID occur because the mind is trying to protect itself from the trauma. So, it makes sense that this personality feels like it never had any trouble before, as it ==doesn’t remember any of the trauma==. All the bottled up anger was instead channeled into the <em>killer</em> personality.</p>
<blockquote>
<p>They say I killed a woman, they know it isn’t true
They’re just trying to frame me, and all because of you, yeah</p>
</blockquote>
<p>This last line feels like one personality talking to another. The Wrathchild appears to be aware that the <em>Killer</em> exists, and is talking directly to it. However, it doesn’t seem like he realizes that they are in fact the same person.</p>
<h2 id="killers">Killers</h2>
<p>In the title track, the lines between the personalities start to blur. Think about it - so far there has been only one killer in the album, but the track is called Killers, <em>plural</em>.</p>
<p>The first verse of the song is narrated by the Wrathchild:</p>
<blockquote>
<p>You walk through the subway, his eyes burn a hole in your back
A footstep behind you, he lunges prepared for attack
Scream for mercy, he laughs as he’s watching you bleed
Killer behind you, his blood lust defies all his needs</p>
</blockquote>
<p>While the second verse is narrated by the <em>Killer</em> personality, but <strong>pay attention to the last line</strong>:</p>
<blockquote>
<p>My innocent victims are slaughtered with wrath and despise
The mocking religion of hatred that burns in the night
I have no one, I’m bound to destroy all this greed
<strong>A voice inside me compelling to satisfy me</strong></p>
</blockquote>
<p>The other 3 lines are sang by the <em>Killer</em>, but that last one seems to be sang by the Wrathchild, since the voice inside him is the <em>Killer</em>.</p>
<p>At the end of the song, the line between the two personalities seem to have disappeared completely. It’s really similar to the first verse of the song, but the pronouns are all mixed up now, and we don’t really know who is talking anymore.</p>
<blockquote>
<p>You walk through the subway, <strong>my</strong> eyes burn a hole in your back
A footstep behind you, <strong>he</strong> lunges prepared for attack
Scream for mercy, <strong>he</strong> laughs as <strong>he</strong>’s watching you bleed
Killer behind you, <strong>my</strong> blood lust defies all <strong>my</strong> needs
Ooh look out, <strong>I</strong>’m coming for you!</p>
</blockquote>
<p>🤯</p>
<h2 id="prodigal-son">Prodigal Son</h2>
<p>This song was the hardest to make sense of, but I think I figured out how it fits overall. In this song, the Wrathchild seems to be back in control, but has become aware of the <em>Killer</em> personality, and aware that they are the same person.</p>
<p>A “Prodigal Son” is a person that leaves home to live a reckless life, and then makes a repentant return. This is exactly what the Wrathchild is doing in this song. He is repenting for the murders he committed, and is scared of his inner urges and scared of ending his own life.</p>
<blockquote>
<p>Listen to me, Lamia, listen to what I’ve got to say
I’ve got these feelings and they won’t go away
I’ve got these fears inside that’ll bring me to my knees
Oh, help me, Lamia, or I’m sure I’ll die, oh, please
I feel unsettled, now I know that I’ve done wrong
I’ve messed around with mystic things and magic for far too long
I feel I’m being paid with this nightmare inside me
The devil’s got a hold on my soul and he just won’t let me be</p>
</blockquote>
<p>Here it becomes clear that the Wrathchild now understands better what’s been going on, and starts asking Lamia for help. He mentions “mystic things and magic” which could be a reference to the uncommon and unknown nature of his disorder (which could be seen as some sort of “magic” or “pact with the devil”). Maybe he sees the <em>Killer</em> as the devil taking hold of his soul.</p>
<p><strong>But who is Lamia?</strong> I’ve found some info about her being a monster in Greek Mythology. But what caught my attention was this line from <a href="https://en.wikipedia.org/wiki/Lamia">the Wikipedia article</a>:</p>
<blockquote>
<p>The “Lamia” was a bogeyman or bugbear term, invoked by a mother or a nanny to frighten children into good behavior.</p>
</blockquote>
<p>It makes sense for a traumatized person to recall his childhood and think of the “bogeyman” that used to be used to scare him into behaving. The Wrathchild definitely feels like he deserves some sort of punishment in order to atone for his sins.</p>
<h2 id="purgatory">Purgatory</h2>
<p>This absolute banger of a song has some cryptic lyrics, but I’m confident they are about the Wrathchild trying to get away from his pain by resorting to drugs, possibly LSD.</p>
<blockquote>
<p>I split my brain, melt through the floor
Over clouds my mind will fly
Forever now I can’t think why
My body tries to leave my soul
Is it me, I just don’t know</p>
</blockquote>
<p>“Split my brain” could be a reference to the DID, and “melt through the floor” could be a reference to the effects of LSD. The sensation of the soul leaving the body is a commonly reported effect of the drug.</p>
<blockquote>
<p>Oh, another time, another place
Oh, another smile on another face
Please take me away, take me away, so far away</p>
</blockquote>
<p>These effects, while confusing, appear to be a welcome escape from the Wrathchild’s pain. He seems to be able to picture a different life where he is actually happy, and wants to be taken to that place. The name of the song, Purgatory, makes a lot of sense in this context, as he seems to be in a state that’s not really good, but it’s at least better than the life he’s had.</p>
<h2 id="drifter">Drifter</h2>
<p>The last song of the album depicts the Wrathchild after being severely affected by the drugs. He is now a drifter, and seems to be wanting to “cure” other people of their pain by bringing them over to the same “purgatory” he’s been living in.</p>
<blockquote>
<p>Gonna get you feeling so secure
Listen, child, don’t you see there’s a cure?
Anyway, got to get you away
Feels so good, think it’s gonna be a new day</p>
</blockquote>
<p>The song (and the album) then ends with the Wrathchild fading out “singing his song”. It’s not really a happy ending, but one that makes a lot of sense, doesn’t it?</p>
<h2 id="wrapping-up">Wrapping up</h2>
<p>So… that’s it. I hope you enjoyed going down this rabbit hole with me. I’m pretty sure a lot of this wasn’t intended by the band, but it’s my interpretation and that’s good enough for me.</p>
<p>Up the Irons! 🤘</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/cms/media/articles/iron-maiden-killers-concept-album/cover.png"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/cms/media/articles/iron-maiden-killers-concept-album/cover.png"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/container-queries</guid>
      <title>Container Queries Are Here!</title>
      <description>Responsive web development will never be the same again.</description>
      <link>https://fantinel.dev/blog/container-queries</link>
      <pubDate>Thu, 16 Feb 2023 00:00:00 +0000</pubDate>
      <category>Front-End</category><category>Dev</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/container-queries">
              read on the site!
            </a>
          </strong>
        </div>

        <p>With the release of <a href="https://www.mozilla.org/en-US/firefox/110.0/releasenotes/">Firefox 110</a>, <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Container_Queries">CSS container queries</a> are now supported in all major browsers! It has joined Chromium (since v105) and Safari (since v16) in adding support to what I consider a revolution on responsive web development, especially to people who build UIs in a component-driven way.</p>
<h2 id="what-are-container-queries">What are container queries?</h2>
<p>Usually when we do responsive development, we use media queries to change the layout of our components based on the viewport/screen/window size. So, for example, we can have a card component that’s displayed like this on a laptop:</p>
<p><img src="https://fantinel.dev/cms/media/articles/container-queries/Pre-CQ-Desktop.png" alt="A large window frame with a card taking up 100% of its width. Since it&#x27;s a large window, the card will display its contents horizontally."></p>
<p>And, if the window size is smaller (for example on mobile), we can use a media query to change how the card is displayed:</p>
<p><img src="https://fantinel.dev/cms/media/articles/container-queries/Pre-CQ-Mobile.png" alt="A small window frame with a card taking up 100% of its width. Since it&#x27;s a small window, the card will display its contents vertically."></p>
<p>This can be done with code like this:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="html"><code><span class="line"><span style="color:#E1E4E8">&#x3C;</span><span style="color:#85E89D">style</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#B392F0">	.card</span><span style="color:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#79B8FF">		display</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">flex</span><span style="color:#E1E4E8">;</span></span>
<span class="line"><span style="color:#79B8FF">		flex-direction</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">row</span><span style="color:#E1E4E8">;</span></span>
<span class="line"><span style="color:#E1E4E8">	}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">	@media</span><span style="color:#E1E4E8"> (</span><span style="color:#79B8FF">max-width</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">600</span><span style="color:#F97583">px</span><span style="color:#E1E4E8">) {</span></span>
<span class="line"><span style="color:#B392F0">		.card</span><span style="color:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#79B8FF">			flex-direction</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">column</span><span style="color:#E1E4E8">;</span></span>
<span class="line"><span style="color:#E1E4E8">		}</span></span>
<span class="line"><span style="color:#E1E4E8">	}</span></span>
<span class="line"><span style="color:#E1E4E8">&#x3C;/</span><span style="color:#85E89D">style</span><span style="color:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#E1E4E8">&#x3C;</span><span style="color:#85E89D">div</span><span style="color:#B392F0"> class</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"card"</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#6A737D">	&#x3C;!-- card content --></span></span>
<span class="line"><span style="color:#E1E4E8">&#x3C;/</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">></span></span></code></pre>
<p>So far, so good. But what if the card doesn’t take up the entire width of the screen? We might want to display it in a sidebar with much less horizontal space, and have another card in the main content area, taking up the rest of the screen width. Here’s how we want it to look:</p>
<p><img src="https://fantinel.dev/cms/media/articles/container-queries/Sidebar-Example.png" alt="A large window frame with a sidebar taking up a third of its space. A card is displayed in the sidebar, taking up 100% of its width. The card will display its contents vertically. The main content area takes up the rest of the screen width. A card is displayed in the main content area, taking up 100% of its width. The card displays its contents horizontally."></p>
<p>But how can we do this, if the screen is not small enough to trigger our media query and display the card’s contents vertically? Previously, the ways of doing this were either to use JavaScript (by checking the window width every time it changes and then toggling a class in the card component) or using <code>flex-wrap: wrap</code>, which is better than using JavaScript, but really hard to get right.</p>
<p>This is where container queries come in. With container queries, we can change the layout of our components based on the size of the container they’re in. So, instead of using the viewport size to control how the card is displayed, we can use the size of the card’s container.</p>
<h2 id="how-to-use-container-queries">How to use container queries</h2>
<p>Starting from the problem described above, we can use container queries to fix it like this:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="html"><code><span class="line"><span style="color:#E1E4E8">&#x3C;</span><span style="color:#85E89D">style</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#B392F0">	.container</span><span style="color:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#6A737D">		/* A containment context needs to be declared on the container element */</span></span>
<span class="line"><span style="color:#79B8FF">		container-type</span><span style="color:#E1E4E8">: inline-size;</span></span>
<span class="line"><span style="color:#E1E4E8">	}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#B392F0">	.card</span><span style="color:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#79B8FF">		display</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">flex</span><span style="color:#E1E4E8">;</span></span>
<span class="line"><span style="color:#79B8FF">		flex-direction</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">row</span><span style="color:#E1E4E8">;</span></span>
<span class="line"><span style="color:#E1E4E8">	}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D">	/* </span></span>
<span class="line"><span style="color:#6A737D">    Instead of @media, we use @container to use its size instead</span></span>
<span class="line"><span style="color:#6A737D">  */</span></span>
<span class="line"><span style="color:#F97583">	@container</span><span style="color:#E1E4E8"> (max-width: 600px) {</span></span>
<span class="line"><span style="color:#B392F0">		.card</span><span style="color:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#79B8FF">			flex-direction</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">column</span><span style="color:#E1E4E8">;</span></span>
<span class="line"><span style="color:#E1E4E8">		}</span></span>
<span class="line"><span style="color:#E1E4E8">	}</span></span>
<span class="line"><span style="color:#E1E4E8">&#x3C;/</span><span style="color:#85E89D">style</span><span style="color:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#E1E4E8">&#x3C;</span><span style="color:#85E89D">div</span><span style="color:#B392F0"> class</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"container"</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#E1E4E8">	&#x3C;</span><span style="color:#85E89D">div</span><span style="color:#B392F0"> class</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"card"</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#6A737D">		&#x3C;!-- card content --></span></span>
<span class="line"><span style="color:#E1E4E8">	&#x3C;/</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#E1E4E8">&#x3C;/</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#E1E4E8">&#x3C;</span><span style="color:#85E89D">div</span><span style="color:#B392F0"> class</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"container"</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#E1E4E8">	&#x3C;</span><span style="color:#85E89D">div</span><span style="color:#B392F0"> class</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"card"</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#6A737D">		&#x3C;!-- card content --></span></span>
<span class="line"><span style="color:#E1E4E8">	&#x3C;/</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#E1E4E8">&#x3C;/</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">></span></span></code></pre>
<p>Now, if the card’s container is smaller than 600px, the card will display its contents vertically. If it’s larger, it will display its contents horizontally.</p>
<h2 id="nested-containers">Nested Containers</h2>
<p>In situations where we would have nested containers, things can get a bit messy. That’s why you can also name a container, using the <code>container-name</code> property, and then use that name in the <code>@container</code> rule. In this example, we want the card to use the <code>.outer-container</code>’s size as a base, instead of its immediate parent container’s size:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="html"><code><span class="line"><span style="color:#E1E4E8">&#x3C;</span><span style="color:#85E89D">style</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#B392F0">	.outer-container</span><span style="color:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#79B8FF">		container-type</span><span style="color:#E1E4E8">: inline-size;</span></span>
<span class="line"><span style="color:#79B8FF">		container-name</span><span style="color:#E1E4E8">: outer-container;</span></span>
<span class="line"><span style="color:#E1E4E8">	}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#B392F0">	.inner-container</span><span style="color:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#79B8FF">		container-type</span><span style="color:#E1E4E8">: inline-size;</span></span>
<span class="line"><span style="color:#79B8FF">		container-name</span><span style="color:#E1E4E8">: inner-container;</span></span>
<span class="line"><span style="color:#E1E4E8">	}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#B392F0">	.card</span><span style="color:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#79B8FF">		display</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">flex</span><span style="color:#E1E4E8">;</span></span>
<span class="line"><span style="color:#79B8FF">		flex-direction</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">row</span><span style="color:#E1E4E8">;</span></span>
<span class="line"><span style="color:#E1E4E8">	}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">	@container</span><span style="color:#E1E4E8"> outer-container (max-width: 600px) {</span></span>
<span class="line"><span style="color:#B392F0">		.card</span><span style="color:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#79B8FF">			flex-direction</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">column</span><span style="color:#E1E4E8">;</span></span>
<span class="line"><span style="color:#E1E4E8">		}</span></span>
<span class="line"><span style="color:#E1E4E8">	}</span></span>
<span class="line"><span style="color:#E1E4E8">&#x3C;/</span><span style="color:#85E89D">style</span><span style="color:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#E1E4E8">&#x3C;</span><span style="color:#85E89D">div</span><span style="color:#B392F0"> class</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"outer-container"</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#E1E4E8">	&#x3C;</span><span style="color:#85E89D">div</span><span style="color:#B392F0"> class</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"inner-container"</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#E1E4E8">		&#x3C;</span><span style="color:#85E89D">div</span><span style="color:#B392F0"> class</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"card"</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#6A737D">			&#x3C;!-- card content --></span></span>
<span class="line"><span style="color:#E1E4E8">		&#x3C;/</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#E1E4E8">	&#x3C;/</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#E1E4E8">	&#x3C;</span><span style="color:#85E89D">div</span><span style="color:#B392F0"> class</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"inner-container"</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#E1E4E8">		&#x3C;</span><span style="color:#85E89D">div</span><span style="color:#B392F0"> class</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"card"</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#6A737D">			&#x3C;!-- card content --></span></span>
<span class="line"><span style="color:#E1E4E8">		&#x3C;/</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#E1E4E8">	&#x3C;/</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#E1E4E8">&#x3C;/</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">></span></span></code></pre>
<p><em>Easy, right?</em></p>
<h2 id="fallback-for-older-browsers">Fallback for older browsers</h2>
<p>As mentioned before, the latest versions of all major browsers support this feature. However, some users might take a while to update their browsers and your site might look broken in the meantime. To avoid this, you can add a <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Conditional_Rules/Using_Feature_Queries">feature check</a> in your CSS that checks for container queries support and only apply the styles if the browser supports it:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="css"><code><span class="line"><span style="color:#F97583">@supports</span><span style="color:#E1E4E8"> (</span><span style="color:#79B8FF">container-type</span><span style="color:#E1E4E8">: inline-size) {</span></span>
<span class="line"><span style="color:#6A737D">	/* container queries styles */</span></span>
<span class="line"><span style="color:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">@supports</span><span style="color:#F97583"> not</span><span style="color:#E1E4E8"> (</span><span style="color:#79B8FF">container-type</span><span style="color:#E1E4E8">: inline-size) {</span></span>
<span class="line"><span style="color:#6A737D">	/* fallback styles */</span></span>
<span class="line"><span style="color:#E1E4E8">}</span></span></code></pre>
<p>As for fallback styles, I recommend giving <code>flex-wrap: wrap</code> a try. It’s not that easy to use, but you can emulate the behavior of container queries in a way that’s <em>good enough</em> for the users who haven’t updated their browsers yet.</p>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>Years ago, when I started learning about responsive development and working with components, I eventually hit a barrier when I couldn’t find a good way of making components adapt to their size like this. Which is why I’m so excited this is finally happening! It’s an exciting time to be a web developer and I can’t wait to see what’s next.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/cms/media/articles/container-queries/cover.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/cms/media/articles/container-queries/cover.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/css-hover-media-query</guid>
      <title>Conditional Hover Styles in CSS</title>
      <description>Have you ever crafted a nice hover effect for an element in your website, then opened it on mobile and saw that effect erroneously appear when that element is tapped on?</description>
      <link>https://fantinel.dev/blog/css-hover-media-query</link>
      <pubDate>Sun, 12 Feb 2023 00:00:00 +0000</pubDate>
      <category>Front-End</category><category>Dev</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/css-hover-media-query">
              read on the site!
            </a>
          </strong>
        </div>

        <p>Have you ever crafted a nice hover effect for an element in your website, then opened it on mobile and saw that effect erroneously appear when that element is tapped on? That’d be nice perhaps, if it weren’t for the fact that the effect just… stays there.</p>
<p>That happens because touch-based browsers apply the hover effect to an element when it’s tapped on regardless of hover capabilities and keep that effect active until you tap something else. Why do they do this? My guess is backwards compatibility, as many websites built with a mouse cursor in mind can hide important information behind a hover action, like for example displaying a tooltip.</p>
<p>Luckily, if you want to avoid having the hover effect applied when it shouldn’t be (and instead using <code>:focus</code> and <code>:active</code> selectors properly), there’s a CSS media query to help with that:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="css"><code><span class="line"><span style="color:#F97583">@media</span><span style="color:#E1E4E8"> (</span><span style="color:#79B8FF">hover</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">hover</span><span style="color:#E1E4E8">) {</span></span>
<span class="line"><span style="color:#B392F0">	.my-component</span><span style="color:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#6A737D">		/* Styles here */</span></span>
<span class="line"><span style="color:#E1E4E8">	}</span></span>
<span class="line"><span style="color:#E1E4E8">}</span></span></code></pre>
<p>If you wanna do the opposite (add styles only to devices that do not support hover), you can use <code>hover: none</code> instead:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="css"><code><span class="line"><span style="color:#F97583">@media</span><span style="color:#E1E4E8"> (</span><span style="color:#79B8FF">hover</span><span style="color:#E1E4E8">: none) {</span></span>
<span class="line"><span style="color:#B392F0">	.my-component</span><span style="color:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#6A737D">		/* Styles here */</span></span>
<span class="line"><span style="color:#E1E4E8">	}</span></span>
<span class="line"><span style="color:#E1E4E8">}</span></span></code></pre>
<p>And there you go! This is a nice thing to keep in mind whether you build mobile-first or desktop-first, as adding that media query to hover styles at the same time you’re developing the styles themselves will avoid any unintended effects.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/cms/media/articles/css-hover-media-query/cover.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/cms/media/articles/css-hover-media-query/cover.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/who-runs-our-code</guid>
      <title>Who Runs Our Code</title>
      <description>In a world with tons of business requirements, conversion metrics, and a stampede of new technologies, front-end development can easily become oblivious of one thing: where our code runs.</description>
      <link>https://fantinel.dev/blog/who-runs-our-code</link>
      <pubDate>Fri, 10 Feb 2023 00:00:00 +0000</pubDate>
      <category>Dev</category><category>Front-End</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/who-runs-our-code">
              read on the site!
            </a>
          </strong>
        </div>

        <p>In a world with tons of business requirements, conversion metrics, and a stampede of new technologies, front-end development can easily become oblivious of one thing: where our code runs.</p>
<blockquote>
<p>[!info]
When I say front-end developer, I don’t just mean those who work on the web. I mean everyone who writes code that runs directly on a user’s machine - be it a website, a mobile app, a desktop program, or even an IoT driver.</p>
</blockquote>
<p>Software development is hard. And not just because of coding itself (which might be the easiest part sometimes), but mostly because in the end, developing commercial software is a great balancing act. We need to balance time, quality, features and expectations all the time.</p>
<p>In an ideal situation, we should have all the time we need to make sure all our components are bug-free, testable, accessible, generic, performant and intuitive. The main problem is, developers are often the only ones who read the code, and therefore the only ones in a company that care about more than 2 of the items above.</p>
<p>Still, it’s valid to take a shot every once in a while and try to push for higher standards. ==In a way you’re borrowing the user’s machine to run your code==, and if you care about your user you wanna make sure that code is the best it could be.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/cms/media/articles/who-runs-our-code/cover.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/cms/media/articles/who-runs-our-code/cover.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/from-twitter-to-mastodon</guid>
      <title>From Twitter to Mastodon</title>
      <description>Turns out the elephant network feels much lighter than the bird one.</description>
      <link>https://fantinel.dev/blog/from-twitter-to-mastodon</link>
      <pubDate>Tue, 07 Feb 2023 00:00:00 +0000</pubDate>
      <category>Personal</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/from-twitter-to-mastodon">
              read on the site!
            </a>
          </strong>
        </div>

        <p>I’m one of the many people that stopped using Twitter after the latest… 💩 <em>developments</em> since it was purchased by Elon Musk. My account still exists, but I’ve deleted all of my tweets and uninstalled the mobile app, and haven’t opened the website in a good while.</p>
<p>Like many, I’ve moved to Mastodon, and so did ~90% of the people I used to follow. Initially it did feel a bit rough! This forced change required me to adapt and learn how the platform works. But I’m glad I did it!</p>
<p>Nowadays my Mastodon community is almost the same as my previous Twitter one, with a few added people, but it feels tighter. It’s hard to explain, but it seems like I feel much closer to them. I guess the lack of a recommendation algorithm (timeline is always in reverse chronological order) makes people actually see each others’ posts, and liking/replying/boosting(retweeting) is way more common.</p>
<p>There’s one category of Twitter account that hasn’t really moved to Mastodon yet: brands. I used to follow some brands on Twitter just to keep up to date on stuff, and brands like Notion and Arc do a great job with their accounts! But I’m not sure I wanna see that on Mastodon, at least not on that level. The best part of it is how it’s built and used by <em>real people</em>, and not brands or influencers simply seeking engagement. You know, kinda like Twitter in its early days.</p>
<p>If you wanna follow me in there, feel free to do so! I’m on <a href="https://hachyderm.io/@fantinel">hachyderm.io/fantinel</a> , or you can search me in there by <a href="https://hachyderm.io/@fantinel">@fantinel@hachyderm.io</a>. Happy tooting!</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/cms/media/articles/from-twitter-to-mastodon/cover.png"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/cms/media/articles/from-twitter-to-mastodon/cover.png"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/2022-year-in-review</guid>
      <title>2022 In Hindsight</title>
      <description>A year of big changes, both to me and to the world.</description>
      <link>https://fantinel.dev/blog/2022-year-in-review</link>
      <pubDate>Mon, 19 Dec 2022 00:00:00 +0000</pubDate>
      <category>Retrospective</category><category>Meta</category><category>Personal</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/2022-year-in-review">
              read on the site!
            </a>
          </strong>
        </div>

        <p>2022 is ending and it brought a lot of changes to the lives of many - me included. I’m not able to say all of them were good, but they also bring opportunities of personal and societal growth, and for me at least, a little glimpse of hope.</p>
<h2 id="last-years-predictions">Last Year’s Predictions</h2>
<p><a href="https://fantinel.dev/2021-year-in-review">Last year</a> I wrote about a few things that were hopefully gonna happen this year and I got half of them right:</p>
<ul>
<li>💍 I <strong>did</strong> get married! The love of my life and I did make it official. We had a nice, somewhat small ceremony and reception for family and friends, and everything was very emotional and beautiful. We already had been living together for 4 years, so the wedding did not represent a big change in our lives, but as a celebration of love it did it’s job of being fun and keeping hearts and bellies full and warm.</li>
<li>🇮🇹 I <strong>did</strong> move temporarily to Italy, and lived there for 3 months. It was an interesting experience, I got to know a different culture, learn another language, and experience what life can be on another corner of the world. I also was able to get an Italian citizenship since my family is originally from there. That being said, those 3 months made me miss my home so much! It was one of those “you never miss it ‘til it’s gone away” type of situations. I missed the culture, the people, the food and the weather, not to mention the improved political situation after the elections gave me a bit of hope for the country. Ironically, I came back from Italy with an Italian citizenship but more Brazilian than ever 🇧🇷.</li>
<li>🚫📝 I <strong>did not</strong> write more on this blog, compared to last year. I only released 2 blog posts (about <a href="https://fantinel.dev/plausible-ethical-analytics">Plausible</a> and <a href="https://fantinel.dev/github-copilot-thoughts">GitHub Copilot</a>) and updated my <a href="https://fantinel.dev/blog-development-sveltekit">SvelteKit post</a> this year. I thought the big changes were gonna bring me inspiration, but what they did was really suck all my energy away 😮‍💨.</li>
<li>🚫🚀 I <strong>did not</strong> release or develop any personal projects this year either. I had ideas and even toyed with some proofs-of-concept, but nothing concrete came out of it. They were pushed back to 2023!</li>
</ul>
<h2 id="personal-changes">Personal Changes</h2>
<p>This was a really busy year personally. The first few months I was busy planning my wedding, then later planning my trip to Italy, while working multiple projects at the same time to be able to finance all that. And while handling the anxiety of those things, I was also struggling a bit to get used to seeing people again after quarantine, and re-learning to socialize was being mentally draining as well. This difficulty socializing made me pursue another big thing: my ASD diagnosis. It was confirmed and I think that helped me understand myself much better. Things are improving 😊.</p>
<p><img src="https://fantinel.dev/cms/media/articles/2022-year-in-review/italy.png" alt="Picture taken from behind the Altar of the Fatherland in Rome, Italy. On the foreground, a statue of the first king of Italy mounted on a horse. In the background, the italian flag and the cityscape of Rome, with old, historical buildings below a blue sky. || full-bleed" title="Altar of the Fatherland in Rome, Italy. Photo by me."></p>
<h2 id="world-changes">World Changes</h2>
<p>Coming from two years of pandemic and big changes due to that, the world seems to be entering a new phase now, getting used to handling Covid (which is still a very real threat) and starting to see the more lasting effects it has caused on society, with the psychological effects of isolation and loss, and with the bursting of economic bubbles that were formed during the pandemic.</p>
<p>The big boom some industries had when the quarantine started (mostly related to e-commerce and digital work tools) seems to have winded down, which is causing a lot of companies to downsize or shut down completely, causing mass layoffs. This is awful, of course, but my hope is that this serves as a wake-up call to make companies start considering more sustainable models of business instead of just seeking exponential growth over everything else.</p>
<p>Something else that’s been coming for years, but seem to have reached a new milestone in 2022: AI-generated content. Both art-focused AI (like this blog post’s cover image), through services like Dall-E, Midjourney and Stable Diffusion, or text-based AIs like ChatGPT and GitHub Copilot have been the center of a lot of discussions regarding the ethics and potential of such technology. On one hand, artists, writers and programmers might be worried that the AIs will make them obsolete. Plus, the potentials of spreading AI-generated content as truth could have (even more) devastating effects to democracy. On the other hand, some of them can really have a positive effect on our daily jobs, as I wrote about in <a href="https://fantinel.dev/github-copilot-thoughts">the post where I shared my thoughts about GitHub Copilot</a>. I do believe their use will have to be regulated sooner rather than later, so we can minimize the downsides without halting their progress, which is really mindblowing.</p>
<h2 id="professional-life">Professional Life</h2>
<p>With such a busy and tiresome year, I haven’t got to learning many <em>new</em> things, and instead focused on becoming better at what I already work with. I’ve worked a lot with React and NextJS this year. I’ve covered my opinions on both of them <a href="https://fantinel.dev/2021-year-in-review">last year</a>, and they haven’t really changed.</p>
<p>WordPress is something I’d worked a little bit with in the past, but now I’ve started working more with it as well. It’s definitely a different way of working compared to modern JS stacks, but it’s kind of refreshing in a way. Plus, I believe seeing firsthand how this kind of stack works brings a new perspective and maybe even ways of improving how we build with the newer ones.</p>
<p>One of the things I get excited the most learning about is <a href="https://www.blueacornici.com/blog/5-major-elements-of-atomic-design/">Atomic Design</a> - not a tech stack, but instead an architectural concept. Seeing it being implemented in different kinds of projects make me really admire the pattern and I’m hoping I can apply it to other projects - including this website - and writing about it soon.</p>
<h2 id="fun">Fun</h2>
<p>My favorite part of these “Year In Review” posts - the entertainment content I enjoyed the most in 2022!</p>
<h3 id="tv-and-movies">TV and Movies</h3>
<p>Not much new on this section, as I’ve mostly rewatched stuff I liked this year (including <a href="https://fantinel.dev/2020-year-in-review#tv-and-movies">Mr. Robot</a> for the third time - still my favorite series!)</p>
<ul>
<li><strong>Superstore</strong> was the best sitcom I’ve watched this year. I think it nails the comedy while also touching on important subjects, like fighting for workers rights and unionizing. Plus, I think it’s the only series I’ve watched that acknowledged the pandemic and added it to its plot really well.</li>
<li><strong>Better Call Saul</strong> was already mentioned here last year when I watched seasons 1-5, but this year I’ve watched season 6, which was the last one. It was entrancing, exciting and bleak - exactly what you’d expect from the Breaking Bad universe. The finale was a perfect ending not just to this particular series but to the whole Breaking Bad universe. I highly recommend watching it <strong>after</strong> Breaking Bad though, as it spoils some of that series, even though it’s mostly a prequel.</li>
</ul>
<h3 id="games">Games</h3>
<ul>
<li><strong>Bloodborne</strong>… wow. I had never played a From Software game before, and it was such a journey. Hard, yes, but fair. I always knew what I did wrong when I died. I always wanted to try again and do a bit better every time. But the star in this game is the setting and atmosphere. It starts in a somber Victorian-era city, where you face some beastly humans and werewolves. Soon you’re facing creatures that are a bit different but still fit in the same world. Eventually, though, the game descends into full-on Eldritch/Cosmic Horror and it’s awesome. I’ve never seen a game capture that vibe so well. Every new enemy or boss you encounter is accompanied by a <em>“what the heck is this?!?”</em> and fighting them is always a box of surprises. Best game I’ve played in 2022 and one of the best I’ve ever played.</li>
</ul>
<p><img src="https://fantinel.dev/cms/media/articles/2022-year-in-review/bloodborne.jpeg" alt="Official artwork of Bloodborne. To the right, a person wearing black Victorian-era clothes, a gun and a meat cleaver is seen from the back. In the background, a dark, gothic city by night." title="Official artwork of Bloodborne"></p>
<ul>
<li><strong>Elden Ring</strong> was the game I was the most excited to play after playing Bloodborne, as it’s made by the same company. I was not disappointed. It delivered great combat, exploration, soundtrack and lore, which combined into another one of the best games I’ve ever played. I wrote <a href="https://fantinel.dev/reviews/elden-ring/">a more in-depth review of the game</a> where I talk about what I enjoyed the most.</li>
<li><strong>Red Dead Redemption 2</strong> is SO GOOD. You can play it many ways: as an old-west life simulator, a cowboy shootout action game, a hunting and fishing game, or even just explore and photograph the scenery. It has so much content, even after 50+ hours I still kept finding events and secrets totally unlike what I’d seen before. You can get completely lost in this game without even progressing the story. But you really should progress the story, as it’s one of the best written ones I’ve ever experienced. The characters are the real focus here, as they’re all masterfully written and you get really attached to them and care for what happens to them. I cried like a baby in the ending.</li>
<li><strong>The Last of Us Part II</strong> is a bleak, brutal sequel to what was already a pretty brutal game. The story is the focus once again, but the gameplay was vastly improved over the first game. They took a creative route when telling this game’s story, making you experience it through two completely different points of view. This plays with your emotions, as the game’s brutal nature may bring out strong emotions in you, just to invert those feelings completely by showing you the other side of the situation. I loved this game, but I’m not sure I wanna play it again, because I feel like it takes a toll on your mind. Still, I recommend experiencing it at least once.</li>
<li><strong>Rocket League</strong> is, year after year, my favorite game to just <em>have fun</em>. The formula is just so good and it never gets old.</li>
</ul>
<h3 id="music">Music</h3>
<p>New category! I wanted to write a bit about music since it’s really important in my life.</p>
<ul>
<li><strong>The Gods We Can Touch</strong> by <strong>AURORA</strong> is a fantastic album by a one-of-a-kind artist. It’s a pop album, but dips a lot into many genres and sounds really fresh and unique. The lyrics are beautiful as always, plus I got to see a lot of these songs played live! Aurora is one of my favorite artists and this is her best album <em>yet</em>.</li>
<li><strong>IMPERA</strong> by <strong>Ghost</strong> is a monumental album from start to finish. It’s incredible how Ghost is able to make each album sound like a completely different era, and coming from the disco-ish Prequelle, IMPERA is a punch in the face with golden era metal. A lot of it sounds like Ozzy’s “No More Tears” era, with acoustic guitar riffs morphing into heavy guitar riffs. But it still sounds unmistakingly Ghost. Plus, it has a metal reggaeton song! That’s awesome.</li>
<li><strong>Infinite Granite</strong> by <strong>Deafheaven</strong> was a lucky find. I knew a couple songs by Deafheaven and while I enjoyed them, the gutural vocals never really pleased me. In Infinite Granite, they were replaced by soft vocals, while keeping the hypnotic, multi-layered instrumentals that I fell in love with immediatelly. I still listen to it on repeat because it’s just so pleasant to listen to.</li>
</ul>
<p><img src="https://fantinel.dev/cms/media/articles/2022-year-in-review/infinite-granite.jpeg" alt="Album cover of the Infinite Granite album. It&#x27;s a bunch of small blue particles grouping together to form some sort of a circle." title="Cover of Infinite Granite, by Deafheaven."></p>
<h2 id="what-to-expect-in-2023">What to Expect in 2023</h2>
<p>I am hoping for 2023 to be a <strong>less exciting</strong> year, honestly. With so many changes this year, I want next year to be a good, chill year to recover and build up on what my life is right now. Hopefully, that means more time and energy to build nice things, and consequently write more.</p>
<p>I’m planning on refactoring the code structure on this website, adopting Atomic Design, and publishing that alongside a nice article explaining all about it. Plus, releasing at least one of the side projects that I’ve built the proof-of-concept for this year.</p>
<p>Above all, I want 2023 to be @@C O Z Y@@. That’s a good word.</p>
<h2 id="wrapping-up">Wrapping up</h2>
<p>Thank you for reading all of this! I hope you’ve had a great 2022, but more than that, I wish you a very <em>cozy</em> 2023!</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/cms/media/articles/2022-year-in-review/cover.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/cms/media/articles/2022-year-in-review/cover.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/github-copilot-thoughts</guid>
      <title>Thoughts on GitHub Copilot</title>
      <description>I was initially skeptical about it, but I&apos;ve come to appreciate it more and more as I use it.</description>
      <link>https://fantinel.dev/blog/github-copilot-thoughts</link>
      <pubDate>Mon, 05 Sep 2022 00:00:00 +0000</pubDate>
      <category>Dev</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/github-copilot-thoughts">
              read on the site!
            </a>
          </strong>
        </div>

        <p>I started using GitHub Copilot around May this year as part of its free beta, and my opinion on it has changed a bit during this time. I was initially skeptical about it, but I’ve come to appreciate it more and more as I use it.</p>
<h2 id="what-it-is">What it is</h2>
<p>First off, let’s see what Copilot has to say about itself:</p>
<p><img src="https://fantinel.dev/cms/media/articles/github-copilot-thoughts/copilot-describing-itself.png" alt="Screenshot of GitHub Copilot describing itself: &#x27;GitHub Copilot is a code completion tool that uses machine learning to suggest code snippets based on the context of your code. It&#x27;s available as a VS Code extension, and it&#x27;s free to use for now.&#x27;" title="It got the idea right, however the pricing info is outdated: it&#x27;s now $10/month."></p>
<p>So, yeah, it’s a code completion tool. You can think of it as <a href="https://code.visualstudio.com/docs/editor/intellisense">Intellisense</a> on steroids, but really it’s much more than that. It is much smarter and is capable of detecting your code style, the context of what you’re writing, and even the intent of what you’re trying to do. So, if you’ve just created a form input for a phone number and start writing a function on a related file, you’ll see it automatically suggest some phone number validation logic.</p>
<p>I think what surprises me the most on a daily basis is how it interacts with code comments. You see, I often write comments detailing the next steps my code should take. In the example below, I’ve only written the comment, Copilot did the rest:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#6A737D">// Check if a task with same title already exists</span></span>
<span class="line"><span style="color:#6A737D">// Throw error if yes</span></span>
<span class="line"><span style="color:#F97583">const</span><span style="color:#B392F0"> checkDuplicate</span><span style="color:#F97583"> =</span><span style="color:#E1E4E8"> (</span><span style="color:#FFAB70">task</span><span style="color:#F97583">:</span><span style="color:#B392F0"> Task</span><span style="color:#E1E4E8">) </span><span style="color:#F97583">=></span><span style="color:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#F97583">	const</span><span style="color:#79B8FF"> duplicate</span><span style="color:#F97583"> =</span><span style="color:#E1E4E8"> tasks.</span><span style="color:#B392F0">find</span><span style="color:#E1E4E8">((</span><span style="color:#FFAB70">t</span><span style="color:#E1E4E8">) </span><span style="color:#F97583">=></span><span style="color:#E1E4E8"> t.title </span><span style="color:#F97583">===</span><span style="color:#E1E4E8"> task.title);</span></span>
<span class="line"><span style="color:#F97583">	if</span><span style="color:#E1E4E8"> (duplicate) {</span></span>
<span class="line"><span style="color:#F97583">		throw</span><span style="color:#F97583"> new</span><span style="color:#B392F0"> Error</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">`Task with title ${</span><span style="color:#E1E4E8">task</span><span style="color:#9ECBFF">.</span><span style="color:#E1E4E8">title</span><span style="color:#9ECBFF">} already exists`</span><span style="color:#E1E4E8">);</span></span>
<span class="line"><span style="color:#E1E4E8">	}</span></span>
<span class="line"><span style="color:#E1E4E8">};</span></span></code></pre>
<p>The opposite can also happen. Sometimes I write some complex functions and Copilot helps me make comments to explain what’s going on. It’s not perfect, but it’s pretty good!</p>
<h2 id="what-it-isnt">What it isn’t</h2>
<p>==Copilot is not something that’s going to take away your job==. At least not soon. The reason is in its name itself: it is a Copilot, meant to be by your side while you’re coding and help you out with busy work, not completely replace you. It’s really not capable of doing that.</p>
<p>You’re still responsible for the code it writes, and for the code you ship. Whatever code it suggests you’ll still have to review, as it may contain flaws that are either obvious or really hidden.</p>
<p>It’s also not a replacement for a good IDE. It’s a tool that works on top of your IDE, and it’s not going to replace it. It’s not going to help you debug your code, or refactor it, or do anything that requires you to think. It’s just a tool to help you write code faster, and perhaps save you some time Googling for the correct syntax.</p>
<p>I also found it kinda useless when writing HTML or CSS. It wouldn’t suggest more than the absolute obvious and was more often than not wrong about what I intended to write, so it almost always got in the way. I only started enjoying it more after using it for more logic-heavy code.</p>
<h2 id="licensing-issues">Licensing issues</h2>
<p>The main controversy around GitHub Copilot is related to the licensing of the code used to train it. You see, Copilot is trained on open source code uploaded to GitHub, and a lot of that code is under the GPL license. This means that if any code under that license is used on your project, you’re obligated to release your code under the GPL license as well. This is kind of a big deal, but GitHub’s CEO has claimed that <a href="https://twitter.com/natfriedman/status/1409914420579344385">training machine learning systems on public data falls under “fair use”</a>.</p>
<p>I’m not knowledgeable enough on the subject to have a well-formed opinion on this, but I’m inclined to disagree with him. If a developer using Copilot is still responsible for the generated code, the GPL license should still apply. Plus, I’m really fond of <a href="https://fantinel.dev/open-source-x-free-software">Free Software</a> and the more shared knowledge we have, the merrier.</p>
<p>Until a consensus is reached, GitHub has added the option to disable public code suggestions on <a href="https://github.com/settings/copilot">Copilot Settings</a>, so you can disable them if you’re worried about licensing issues.</p>
<p><img src="https://fantinel.dev/cms/media/articles/github-copilot-thoughts/copilot-settings.png" alt="Screenshot of a GitHub Copilot settings displaying the option to block suggestions from public code."></p>
<h2 id="is-it-worth-the-price">Is it worth the price?</h2>
<p>I started using Copilot on the free beta, and after it ended I decided to keep using it, even for $10/month. It all depends on how much time it saves you. After using it for a good while I figured out that the time it saves me every month makes Copilot pay for itself. It has a free trial, so you can try it out and see if it’s the same for you.</p>
<p>You can check out all the details on the official <a href="https://github.com/features/copilot/">GitHub Copilot website</a>.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/cms/media/articles/github-copilot-thoughts/cover.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/cms/media/articles/github-copilot-thoughts/cover.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/review-elden-ring</guid>
      <title>Review: Elden Ring</title>
      <description>My thoughts on what probably is the biggest game release of 2022.</description>
      <link>https://fantinel.dev/blog/review-elden-ring</link>
      <pubDate>Mon, 01 Aug 2022 00:00:00 +0000</pubDate>
      <category>Games</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/review-elden-ring">
              read on the site!
            </a>
          </strong>
        </div>

        <blockquote>
<p>[!info]
TL;DR: It’s incredible and worth all the praise it’s got. Not perfect, but really close to it.</p>
</blockquote>
<p>Elden Ring was probably the most hyped up game of the past couple of years, and its release in February (2022) was met with a lot of media coverage and players raving about how incredible it was.</p>
<p>I wasn’t hyped at all about the game, mainly because I had never played a From Software (the game’s developer) game before. Dark Souls was a big hit when it came out, but I was never into difficult games, and I didn’t have a console to play it on (I was one of the 6 people that owned a Wii U, but not a PlayStation or Xbox). However, with all that talk surrounding Elden Ring I couldn’t just ignore its existence. The game was really expensive though, and I didn’t feel like spending R$300 to play something I thought I wouldn’t enjoy. So, I downloaded Bloodborne (free on PS Plus), another From Software game with good reviews, which I figured would give me a taste if I liked this kind of game or not.</p>
<p>I was completely amazed by that game, and it became one of my all-time favorites. After finishing that one and immediatelly tweeting about wanting a sequel, I had no choice but to play Elden Ring next.</p>
<h2 id="the-game">The Game</h2>
<p>A few minutes into the game and you’re presented with its selling point: you open a door and you’re now free to explore an entire world, full of mysteries and dangers. Right in front of you is a boss that @@will totally destroy you@@ if you try to beat it, and it’s an important lesson of what to expect in the game: you need to be prepared. You circle around that boss and make a note on the map to come back to it later. Then you proceed into a forest and find some soldiers that you can defeat. You’re not sure why you’re fighting them or why they wanna kill you, but it’s fun, so you do it. Then you find an entire camp full of them, including one with a more menacing armor, a big shield and a spear. You try to face it and it kills you <i>once. Twice. Three times</i>. You get really close to beating it, your hands start sweating with anticipation, and you finally do it! Metaphorical tears of joy roll down your face and you feel really good about what you just accomplished. Then you realize that was just a regular enemy, that will respawn every time you rest. <b>There’s so much more to uncover.</b></p>
<p>That’s the main hook of all games in the “Soulsborne” series, and it works really well. It uses frustration to build up an extremely satisfying reward when you win. Elden Ring did not change that, instead it mixed that feeling with an open world that gives you more ways to cool down between tough fights, or multiple ways to get stronger to prepare. It took me over 30 hours to have my character strong enough to tackle that boss right after the first door of the game. That was like 1/3 of my play time, and it was probably the best part of it. Every discovery was something brand new, and many encounters provided a fair challenge.</p>
<p><img src="https://fantinel.dev/cms/media/articles/review-elden-ring/tree-sentinel.jpg" alt="Elden Ring screenshot. In it, the player is fighting a huge knight in golden armor riding a horse, also in golden armor." title="The Tree Sentinel is likely the first boss you&#x27;ll encounter, but not the first one you&#x27;ll defeat."></p>
<p>However, I feel that the feeling of challenge was really uneven throughout the game. When both my character and I were not well prepared or strong enough, it felt more rewarding when I got a win. Later in the game, I was so overleveled for some bosses that I didn’t get that feeling of adrenaline during a big fight, nor the big endorphine release when I defeated them. And that did not come from me grinding XP somewhere. I was just enjoying the game, exploring every nook and cranny I could find, and getting stronger as I did that.</p>
<p>To be fair, I do think making a game like this completely balanced is nigh impossible. Not only there are dozens of different builds you can make for your character, but also every player will have a different journey, ending up with more or less Runes (the currency you use to buy levels and improve your character) than others. So, on a technical level, I truly understand the complexity of balancing this and still think they did a good job overall. It just was a bit underwhelming on my specific case.</p>
<h2 id="the-story">The Story</h2>
<p>Elden Ring, just like other From Software games, has almost no story. It has a huge and rich lore, though. For most of the game, you simply have no idea what’s going on. It’s by hearing some cryptic dialogue, reading item descriptions, and observing the world around you that you can start to put together the puzzle surrounding the game. Or, you can watch some really good YouTube videos explaining the lore behind the game, which make you finally understand why things are the way they are.</p>
<p>I like this.</p>
<p>It has a bit of a more realistic approach to storytelling - your character is part of that world, and as such you’d probably be acquainted to some of the more common stories and myths in there, and definitely not be aware at all of the secrets and twists of the royal family of demigods. Having to go out of your way to figure things out makes you more attached to the game in a way, and makes you care more. Old games used to do this for technical reasons, but there’s definitely an appeal here. I do understand it’s not very accessible though.</p>
<p><img src="https://fantinel.dev/cms/media/articles/review-elden-ring/malenia-and-radahn.jpg" alt="Elden Ring art. A long-haired swordswoman seems hurt, holding her arm with one hand, and a katana on another. In front of her, a huge warrior with a red mane stands tall with arms crossed." title="The demigods have an interesting backstory and while you interact little with them, knowing that backstory makes their appearances much more meaningful."></p>
<p>The lore itself is great. It’s a more common fantasy setting, with dragons, wizards, gods, but it feels unique by adding a lot of nuance to it, and making it centered on a family of very unique characters that you either befriend or fight during the game. These characters are well-written and have a lot of depth, and looking into how they got to be what they are now is a really cool way to spend time when you’re not playing the game.</p>
<p>Had I not played this immediately after playing Bloodborne, I think I’d have enjoyed it more. Bloodborne was just so unique and atmospherically perfect, this one didn’t really get to the same level. But it’s still one of the best game worlds I’ve ever adventured in.</p>
<h2 id="the-world">The World</h2>
<p>The Lands Between (the name of the in-game world) is a beautiful place, and all of its areas tell a bit of the story surrounding everything. You have vast green areas, a huge lake surrounded by mist, a mountain around a volcano, icy peaks, a golden city, and an area that’s almost literal hell and that will kill you so many times you’ll kinda start to like it. Almost everything is fully explorable from the start of the game, though you might face some “natural barriers” in the form of enemies you’re still not ready to face.</p>
<p><img src="https://fantinel.dev/cms/media/articles/review-elden-ring/liurnia.jpg" alt="Screenshot of Elden Ring. It&#x27;s taken from the top of a cliff, showing a beautiful landscape. To the left, many rocky mountains and a golden tree. To the center, a huge lake covered in mist, and a castle sitting on top of a cliff farther back. To the right, more rocky cliffs with trees and another golden tree."></p>
<p>==The world is fully realized, and there are rewards in every corner.== However, it’s not the best part of the game.</p>
<p>In some parts of the world are places called Legacy Dungeons - they’re areas that work just like the older games (like Dark Souls or Bloodborne) did: smaller, more linear areas, with great level design and that offer a more curated, if limited, experience. Those are still the best parts of the game, not its open world. I really hope they continue to be a thing in future games.</p>
<h2 id="the-bad">The Bad</h2>
<p>The sidequests, really. They’re kinda bullshit. Not their content per se, but they rely so much on “randomly coming across a character on a very random part of a gigantic open world” that it’s nearly impossible to complete one of them without following an online guide. Quest markers would make this better and still keep the same level of immersion.</p>
<h2 id="verdict">Verdict</h2>
<p>I think the best thing I can say about this game is that after I’ve beat it (which took me 92 hours), I immediately started the game over again, to play with a different character. Even after so many hours, I was still wanting more. Though not my favorite game ever, it was still one of the best.</p>
<p>I hereby give this game a sparkling @@10/10@@ score!</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/cms/media/articles/review-elden-ring/cover.png"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/cms/media/articles/review-elden-ring/cover.png"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/plausible-ethical-analytics</guid>
      <title>Ethical Analytics are Plausible, at Last</title>
      <description>Web Analytics is such a morally ambiguous area that I’ve avoided it for years, but now I can finally do it with peace of mind.</description>
      <link>https://fantinel.dev/blog/plausible-ethical-analytics</link>
      <pubDate>Tue, 01 Feb 2022 00:00:00 +0000</pubDate>
      <category>Dev</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/plausible-ethical-analytics">
              read on the site!
            </a>
          </strong>
        </div>

        <p>Analytics have become intrinsic to software development in the past decade. On the marketing side, analytics allow us to measure the success of marketing campaigns, optimize the wording, measure ad results, or simply keep track of the growth of the audience. On the development side, it allows to more easily detect points of failure, bad patterns or UX hiccups.</p>
<p>These benefits however, usually come at a price: ==in-depth analytics tend to store way too much information about the user==, and the user is often unaware of how much is being collected. It’s a decision the user doesn’t really make; we make it for them. <em>(Yes, I know, legally the user often accepts cookies or terms of service… but the reality is that almost nobody reads or understands that.)</em></p>
<p>On platforms like Google Analytics, there is a lot of information we can see, however none of them personally identifies the user. That’s good, right? Well, not really: Google can personally identify (and does through its Ads business) them, and by using Google Analytics or similar products on your site, you’re feeding them your user’s information for a really low price - otherwise, why would these analytics be free?</p>
<p>Luckily, there are ways to get most of the needed metrics without compromising anybody’s privacy, and even get a better experience in the end.</p>
<h2 id="ethical-analytics">Ethical Analytics</h2>
<p>Recently increasing concerns about online privacy have sprouted a lot of ethical analytics services, that aim to collect only necessary, anonymous data. Services like <a href="https://matomo.org/">Matomo</a> and <a href="https://usefathom.com/">Fathom</a> provide an experience that is simple and to-the-point, and I’ve heard nothing but praise about both services. For myself though, I’ve picked <a href="https://plausible.io/">Plausible Analytics</a>, which I use on this website and in some of my side projects.</p>
<p>It’s important to point out, though, that none of those are free. Since they don’t collect personal data and don’t monetize it, they must charge for their service. The prices are pretty low though, and unless it’s a hobby project you have no intention of monetizing, that cost will be absorbed really quickly and the gains will make it worth it.</p>
<h3 id="plausible">Plausible</h3>
<p>Plausible is a small company from Europe that makes Plausible Analytics, a privacy-friendly alternative to Google Analytics. The reasons I’ve picked Plausible instead of others are that:</p>
<ul>
<li>It’s open source;</li>
<li>It can be self-hosted in case you already have your own server;</li>
<li>It’s intuitive: a quick glance through the dashboard and you’re already able to understand everything that’s in there. <a href="https://plausible.io/fantinel.dev">Check out mine</a>;</li>
<li>It’s sustainable: their business model allows them to grow organically and <a href="https://plausible.io/blog/bootstrapping-saas">the project is already profitable</a> without any kind of external funding (and <a href="https://twitter.com/PlausibleHQ/status/1282678251148763137">they have no plans of accepting it</a>);</li>
<li>They offer <a href="https://plausible.io/docs/proxy/introduction">ways of bypassing adblockers</a>;</li>
<li>It’s simply a great product.</li>
</ul>
<p><img src="https://fantinel.dev/cms/media/articles/plausible-ethical-analytics/dashboard.png" alt="Screenshot of the Plausible dashboard, showing number of visitors, top sources, top pages, visitors by city, and device information." title="Clicking on some data blocks allow you to filter data - so you can still understand how your users behave differently depending on a number of factors."></p>
<h4 id="custom-goals">Custom Goals</h4>
<p>The data that Plausible collects by default should be enough for most use cases. However, there is still some information that is not gathered that you might want to have. For cases like that, Plausible offers the <a href="https://plausible.io/docs/custom-event-goals">Custom Event Goals API</a>.</p>
<p>With a simple function call, you can send data to the Plausible API and track some behaviors or extra information. For example, if you want to track the percentage of your users that click on “Sign up”, you can fire a custom event for that. These are also filterable in the dashboard, which means you can compare where most of the users that sign up come from.</p>
<p>In my case, I wanted to know the percentage of users that use light/dark mode in their browser or system. There is <a href="https://github.com/plausible/analytics/discussions/622">an open Feature Request to add this functionality</a> to Plausible that has received some interest, but while that isn’t implemented, I built my own detection using the Custom Events API:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="javascript"><code><span class="line"><span style="color:#6A737D">// Use Plausible to track the % of people that have a dark/light mode preference</span></span>
<span class="line"><span style="color:#F97583">if</span><span style="color:#E1E4E8"> (plausible) {</span></span>
<span class="line"><span style="color:#F97583">	if</span><span style="color:#E1E4E8"> (window.matchMedia </span><span style="color:#F97583">&#x26;&#x26;</span><span style="color:#E1E4E8"> window.</span><span style="color:#B392F0">matchMedia</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">'(prefers-color-scheme: dark)'</span><span style="color:#E1E4E8">).matches) {</span></span>
<span class="line"><span style="color:#B392F0">		plausible</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">'Dark Mode Enabled'</span><span style="color:#E1E4E8">);</span></span>
<span class="line"><span style="color:#E1E4E8">	} </span><span style="color:#F97583">else</span><span style="color:#F97583"> if</span><span style="color:#E1E4E8"> (window.matchMedia </span><span style="color:#F97583">&#x26;&#x26;</span><span style="color:#E1E4E8"> window.</span><span style="color:#B392F0">matchMedia</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">'(prefers-color-scheme: light)'</span><span style="color:#E1E4E8">).matches) {</span></span>
<span class="line"><span style="color:#B392F0">		plausible</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">'Light Mode Enabled'</span><span style="color:#E1E4E8">);</span></span>
<span class="line"><span style="color:#E1E4E8">	} </span><span style="color:#F97583">else</span><span style="color:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#B392F0">		plausible</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">'No Dark/Light Preference'</span><span style="color:#E1E4E8">);</span></span>
<span class="line"><span style="color:#E1E4E8">	}</span></span>
<span class="line"><span style="color:#E1E4E8">}</span></span></code></pre>
<p><strong>Note:</strong> <em>tracking Custom Events without user interaction like this breaks the “Bounce Rate” metric. By firing an event, Plausible interprets that the user has interacted with your site, and therefore the Bounce Rate goes to zero. If the Bounce Rate metric is important to you, this might not be a good idea.</em></p>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>I apologize if this post felt like an ad. It is not. Web Analytics is such a morally ambiguous area that I’ve avoided it for years, and now I can finally grab some metrics with a peace of mind. Regardless of what service you choose and prefer, having privacy-friendly alternatives is good for everyone. With Plausible being one of my favorite pieces of software at the moment, I felt it was right to write a bit about it and spread the word.</p>
<p>Thanks for reading!</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/cms/media/articles/plausible-ethical-analytics/cover.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/cms/media/articles/plausible-ethical-analytics/cover.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/2021-year-in-review</guid>
      <title>2021 In Review</title>
      <description>Another year, another yearly review post. 2021 felt like pretty much 2020 part II for a lot of people, me included.</description>
      <link>https://fantinel.dev/blog/2021-year-in-review</link>
      <pubDate>Thu, 09 Dec 2021 00:00:00 +0000</pubDate>
      <category>Retrospective</category><category>Dev</category><category>Meta</category><category>Personal</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/2021-year-in-review">
              read on the site!
            </a>
          </strong>
        </div>

        <p>Another year, another yearly review post. 2021 felt like pretty much 2020 part II for a lot of people, me included. The ongoing pandemic still affected our lives, and even though most people in my country are now vaccinated (including me), the danger still exists and preventive measures are still necessary. But it’s nice to be able to start doing things and going places we haven’t been able to for a while. I personally think this situation is still going to drag on for a while, but we’ll adapt to it with time.</p>
<h2 id="mental-health">Mental Health</h2>
<p><a href="https://fantinel.dev/2020-year-in-review">Last year</a>, I wrote a bit about doomscrolling, and how it started affecting me until I decided to avoid this behavior. I tried to keep avoiding it this year, and I think I succeeded. I’m not blind to what’s going on - I still know @@the world is a dumpster fire@@ at the moment and in many ways it was even worse than 2020 - but I think I was successful in not dwelling on the bad news and the daily absurdities going on.</p>
<p>That doesn’t mean my mental health didn’t take a hit though. The year started with the loss of a person I held dear - a victim of the inexcusable vaccination delays and genocidal actions of our leaders - and I felt like I lost a bit of control over my work-life-balance for a portion of the year.</p>
<p>This year I also started therapy though, which I feel is a good way to deal with the growing anxiety from the past few years. I’ve been able to know more about myself and how my brain works, so I can focus on what I found out to improve my life, one day at a time.</p>
<h2 id="work-life">Work Life</h2>
<p>Early this year I changed jobs, and started working as a contractor. This has given me more freedom to make my own hours and allowed me to work with a more diverse set of people. It took me a while to use this freedom the right way, though. For a few months I felt like my work-life balance was off, and I felt like my energy was being drained fast. Luckily I was able to notice the early signs of a burnout and took a step back to avoid it. I am now working on my 2nd project as a contractor and I feel that getting the work hours under control allowed me to do my work better, since my mind gets the time to rest.</p>
<h2 id="learning-and-comparing">Learning and Comparing</h2>
<p>Most of the learning I did this year was on my day job, and not personal projects. Last year, I worked a lot with Angular and Vue 2. This year, I worked with Vue 3, React and PHP (😩), and on my personal time I started to practice my Svelte skills. That’s a lot of frameworks!</p>
<p>Although every project is different, working with components makes you solve similar problems all the time. And after having worked with basically all mainstream frameworks for a bit, I feel like I can see pros and cons to each of them:</p>
<h3 id="angular">Angular</h3>
<ul>
<li><strong>Pros:</strong> it’s very opinionated, which means there are always recommended ways of doing something. This makes the code consistent, and using TypeScript by default makes it much easier to maintain big projects. I like the template syntax and the separation between HTML, CSS and TS. I also can definitely see why it’s a choice in many enterprise environments: the majority of Angular projects I’ve seen have similar structure, which makes devs productive really quickly after being hired.</li>
<li><strong>Cons:</strong> it’s very opinionated, which means it’s not as flexible as other options. The focus on clear separation of concerns and TypeScript can also be a burden for smaller apps. Sometimes you want to make a quick prototype or just build something simpler, and it’s easy to find Angular overkill.</li>
</ul>
<h3 id="vue">Vue</h3>
<ul>
<li><strong>Pros:</strong> I really like the premise of Vue being “incrementally adoptable”. This makes Vue really simple on the surface and hides many of its complexities. The template syntax is similar to Angular and it’s easy to keep concerns separated if you want to.</li>
<li><strong>Cons:</strong> In my personal experience I felt like the Composition API, introduced in Vue 3, goes against my favorite things in Vue previously. Instead of being an extension to HTML/JS, it makes you completely rethink how to build the components, and I feel like it makes things more complicated than they should be. Also, the ecosystem around it (plugins, components, etc) took a big hit with the transition from v2 to v3, because it had many breaking changes.</li>
</ul>
<h3 id="react">React</h3>
<p>My experience with React has not been as extensive as the other ones here, so I think I’m still not past the point of “initial impressions”. Still, here are my thoughts:</p>
<ul>
<li><strong>Pros:</strong> Super flexible, and with a ginormous ecosystem to back it up. You can write a React app in so many ways and there’s always an open-source plugin or component for what you want to do. The existence of NextJS makes using React a better experience.</li>
<li><strong>Cons:</strong> Ok, I really don’t like JSX. I usually like to keep concerns separated and using JS to build the HTML like this is not my cup of tea. I find it hard to understand at a glance, and feel like it overrides the default web stack too much. Unlike the other frameworks, when I use React I feel like I’m developing a React app instead of a web app. Also, in contrast to Angular, the flexibility of React makes it so that React projects can be so different from one another you’d have a difficult time adapting, even though they use the same framework (this is mitigated by meta-frameworks like NextJS, which are very opinionated, thankfully).</li>
</ul>
<h3 id="svelte">Svelte</h3>
<p>I have a clear favorite here, but I feel that Svelte hits the sweet spot in many ways.</p>
<ul>
<li><strong>Pros:</strong> I like the template syntax (not as clean as Vue’s or Angular’s, but similar enough), the clear separation of concerns, and how it likes to stay out of your way. Most of all though, I love how Svelte makes me just feel like I’m building a “vanilla” web page, but with added goodies. ==It extends the web instead of replacing it.==</li>
<li>The fact that it is a compiler frees it from the need for a Virtual DOM, which then makes the code you need to write much simpler. No need for <em>useState</em>, <em>useEffect</em>, or keeping track of two-way binding to maintain state between components - you just have to set the variables and Svelte does the rest. I just think about my logic, not about how the framework works.</li>
<li>Its official meta-framework, SvelteKit, also has great defaults. It works by using native HTML tags instead of trying to emulate them. Instead of a <code>&#x3C;Link></code>, just use <code>&#x3C;a></code>. Instead of using JS to post data, just use a <code>&#x3C;form></code>. This makes the app leaner, more accessible, and work with JS disabled by default.</li>
<li><strong>Cons:</strong> Svelte is relatively new, and therefore its ecosystem is still lacking. I’ve had trouble properly integrating a SvelteKit project with Storybook or Jest, for example, since Kit is still in Beta. There are also many components that don’t have a Svelte version yet, and while some of them have a “Vanilla JS” option that usually works, it’s still not as seamless as it could be.</li>
</ul>
<h2 id="personal-projects">Personal Projects</h2>
<p>I didn’t work on many personal projects this year, as I was already learning a lot on my day job. I did, however, ==completely rewrite my website!==</p>
<p>I really liked the previous one, but I wanted to change some things about it and use something more flexible than Jekyll. So I completely redesigned it using Figma - while retaining some design elements from before - and rewrote it with SvelteKit. <a href="https://fantinel.dev/blog-development-sveltekit/">I wrote a blog post talking more about the experience</a>, which also became my most-read post of all time. Nice!</p>
<p>Speaking of blog posts, @@I doubled the amount of posts published compared to last year!@@ Which means I wrote… <strong>4</strong> (excluding this one). I wanted to write more, but compared to the 2 posts from last year, it’s still an improvement, <em>right</em>?</p>
<h2 id="fun">Fun</h2>
<p>Not everything is code and gloom - I also do some fun things every once in a while.</p>
<h3 id="tv-and-movies">TV and Movies</h3>
<ul>
<li><strong>Ted Lasso</strong> is the TV equivalent of a hug. I’ve never seen a show that can balance comedy, wholesomeness and an enthralling story and characters so well. It touches on delicate subjects in a way that still makes you feel good, and yet is capable of sending the intended message. Honestly can’t find anything I dislike about it.</li>
<li><strong>Better Call Saul</strong> was surprisingly good. I initially dismissed it as being just a spin-off of a popular show, but it slowly evolved into a tightly-woven drama that keeps me at the edge of my seat. The stakes are lower here than they are in Breaking Bad, but I feel like I care more about the characters on this one.</li>
</ul>
<h3 id="games">Games</h3>
<ul>
<li><strong>The Last of Us</strong> was surprisingly surprising for me. I am 8 years late to the party here, but I didn’t have a PlayStation since the PS1, and not only I hadn’t played this game, I never felt like doing so. I usually favor creative gameplay instead of a complex story, but finally gave it a go this year. Wow. It was an amazing experience from start to finish, though I admit it felt more like a more immersive movie than a game. That being said, I don’t think any movie can match the immersion that a game with a good story can provide.</li>
<li><strong>No Man’s Sky</strong> is a mix of exploration and existential crisis. There are a lot of things to do (though not all of them have much depth), but the main gameplay loop of exploring planets and seamlessly traversing between them is addictive. Plus, the main story was surprisingly good, with a lot of amazing quotes from the Travellers in that universe. A bit of existential crisis was the icing on the cake.</li>
</ul>
<p><img src="https://fantinel.dev/cms/media/articles/2021-year-in-review/no-mans-sky.jpg" alt="Screenshot of No Man&#x27;s Sky, showing a robotic humanoid holding a tablet and looking directly at the camera. Below, a textbox says: &#x22;Existence is beautiful, if you let it be. Life is not a question. There does not need to be an answer."></p>
<ul>
<li><strong>Metroid Dread</strong> was a lot of fun! I never liked games of this genre (Metroidvania) until I played Hollow Knight, last year. I felt like finally giving Metroid a chance since it was the first main entry in 19 years and the reviews were stellar. I was not disappointed. I feel like it doesn’t match Hollow Knight in its worldbuilding and ambientation, but the gameplay in Metroid Dread felt much more fluid, making almost everything in it a joy.</li>
<li><strong>Dungeons &#x26; Dragons</strong> has continued to be one of the highlights of my weeks. My party still played online this year, but it was a lot of fun. We finally finished the Curse of Strahd campaign after 502 days. The finale was so epic we decided to have it illustrated by the amazing <a href="https://www.gholz.art/">Guilherme Holz</a>.</li>
</ul>
<p><img src="https://fantinel.dev/cms/media/articles/2021-year-in-review/dnd.png" alt="Illustration of a party of RPG heroes. From left to right: an blonde woman with angelical wings, readying a bow with an arrow made of light; a bald man wearing leather armor and holding a spectral spear, thrusting it into a vampire with red eyes; a tall, blonde woman with heavy armor, a shield and a sword made of light; and an old halfling holding a wooden staff and a beer mug." title="It was kind of poetic to have a bolt of holy light (in the form of a spear) be the end of one who brought so much darkness to the land. From left to right: Ylanis, Alesteir (me), Aliana and Garreth. The guy getting stabbed is Strahd, the big bad evil guy."></p>
<h2 id="what-to-expect-in-2022">What to Expect in 2022</h2>
<p>First of all, @@I’m getting married! 💍@@ The love of my life and I are finally making it official.</p>
<p>Later in the year I’m also temporarily moving to Italy for a few months with my (soon-to-be) wife. It’s going to be some sort of test drive - we’ll return to Brazil before the year ends and then decide if living abroad is something we want or not.</p>
<p>As for this blog, I hope to again write more than I did this year. I have a few ideas in the oven, but have been struggling to write them down. Hopefully the big life changes are the inspiration I need.</p>
<p>I also have some ideas for some personal projects that may or may not see the light of day. Let’s see how it goes.</p>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>2021 was overall a weird year, but I feel 2022 is going to have more going on. I wish you happy holidays and that you have a memorable 2022. See you then!</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/cms/media/articles/2021-year-in-review/cover.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/cms/media/articles/2021-year-in-review/cover.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/blog-development-sveltekit</guid>
      <title>How I built a blog with Svelte and SvelteKit</title>
      <description>An overview of the experience I&apos;ve had using these amazing projects.</description>
      <link>https://fantinel.dev/blog/blog-development-sveltekit</link>
      <pubDate>Wed, 08 Sep 2021 00:00:00 +0000</pubDate>
      <category>Front-End</category><category>Svelte</category><category>Dev</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/blog-development-sveltekit">
              read on the site!
            </a>
          </strong>
        </div>

        <p>I’ve recently re-launched my personal website and blog, that’s now reached its 3rd version. There was nothing wrong with the previous one, but I wanted to both give it a small visual refresh and learn something new. So, after a month or so of working on it on my free time, @@voilà@@, you’re seeing it right now!</p>
<blockquote>
<p>[!info]
While the code for this website is open-source, it has a lot of content that’s very specific to myself. So, if you want to use it as a template to get your own website started, I’ve created a <strong>public template that you can use as a starting point</strong>!</p>
</blockquote>
<p><a href="https://github.com/matfantinel/sveltekit-static-blog-template" title="button || color=secondary">Check out the starting template here!</a></p>
<h2 id="about-svelte-and-sveltekit">About Svelte and SvelteKit</h2>
<p><a href="https://svelte.dev/">Svelte</a> is a new cool kid on the JS block - it was launched in 2016, but it really started getting traction when its 3.0 version was launched in 2019. It is a competitor to the big JS frameworks - React, Vue, Angular - but, instead of simply being an alternative way of doing things, it differentiates itself on a fundamental level: ==it is a compiler, not a framework.==</p>
<p>This means that it does its job at build time, not at runtime. While other frameworks (React, for example) need JavaScript code that runs on the client in order for your components to work properly, Svelte does not, because it interprets your code on build time, and only ships exactly what it needs to run. This means websites made with Svelte can be much smaller in size and faster because it has less code to run.</p>
<p><a href="https://kit.svelte.dev/">SvelteKit</a> is a framework built on top of that. It is an opinionated set of rules that helps you get a Svelte app built pretty quickly. You can compare it to NextJS (for React) or Nuxt (for Vue). It has great defaults and encourages good practices, like server-side rendering, for example. At the time of building this website, SvelteKit was still in Beta. However, progress is steady and its API seems to be already stable, so it’s unlikely there will be any breaking changes before 1.0 arrives.</p>
<p>Using both of the above allowed my website to have two important characteristics:</p>
<ul>
<li>Every single page is rendered at build time (server-side rendering). This means that as long as the HTML and CSS files are downloaded, it will look as it was meant to be;</li>
<li>JavaScript is not needed. Try disabling JavaScript on your browser. You’ll still be able to read this blog post and everything will look the same.</li>
</ul>
<h2 id="progressive-enhancement">Progressive Enhancement</h2>
<p>One of the concepts that really caught my eye with Svelte is the idea of Progressive Enhancement: ==making sure your app runs for everyone, and making it progressively more featureful if the user’s device supports it==. My website is a pretty simple project, but still there are instances of this:</p>
<p>If there’s no JavaScript, the website uses the browser’s native navigation API. Which means you can navigate between pages normally without any client-side code. <em>However</em>, if JavaScript is available, a client-side router will be used to make the transition between pages smoother and faster. This means that even if the user’s device doesn’t support JS for any reason, the site will still be completely functional.</p>
<blockquote>
<p>[!info]
We tend to think of JavaScript being disabled as a user choice, but that is usually not the case. Think of someone using their phone on a weak 3G connection that fails to load the .js files, or someone in the subway that lost signal while loading the page. It happens often and being able to show your content even in these conditions is a great way of not losing a visitor.</p>
</blockquote>
<p>And you know what I had to do to support this? <em>Nothing</em>. Just using <code>&#x3C;a></code> elements is enough, as SvelteKit will intercept those if needed (JS enabled), or simply leave it to the browser otherwise.</p>
<h2 id="design">Design</h2>
<p>I’m not a designer, but I like to pretend I am. So, instead of designing-as-I-go when developing the website, I decided to fully design it on <a href="https://www.figma.com/">Figma</a> before starting development. I figured that using it to design stuff is the best way to learn the platform, and I really finished this design much better at Figma than I was before. Auto-layout is fantastic since it works so much like CSS Flexbox, so I was able to approach it with a similar thought-process as I have when developing stuff.</p>
<p>The design itself was something I came up with, and it’s an amalgamation of ideas and inspirations that were stored in my head. When building it, I wasn’t entirely sure where the inspiration came from, but now it is apparent to me that most of it comes from some websites I love: <a href="https://www.joshwcomeau.com/">Josh Comeau’s</a> and <a href="https://georgefrancis.dev/">George Francis’</a>.</p>
<p>After the design was complete, I finally began developing it. Since there are some things that you can only find out while developing it and giving it more attention, I made some small tweaks to the design while implementing it. Still, the final result was <em>really</em> similar to the initial design. I count that as a success!</p>
<p><img src="https://fantinel.dev/cms/media/articles/blog-development-sveltekit/design-vs-result.png" alt="Screenshot of the design and end result of the website. Both look really similar!" title="The design (left) and the result (right)"></p>
<h2 id="routing">Routing</h2>
<blockquote>
<p>[!info]
Initially, SvelteKit used a <em>file</em>-based routing, as opposed to the current <em>folder</em>-based routing. This article has been updated to reflect those changes. If you were familiar with the previous system, <a href="https://github.com/sveltejs/kit/discussions/5774#discussioncomment-3294867">this comment by the SvelteKit team</a> might help you understand the changes.</p>
</blockquote>
<p>SvelteKit uses a folder-based routing system. This means that your routes are dictated by the folder structure inside <code>routes</code> in your project. Every route <strong>must</strong> have a folder, except for the root (’/’).</p>
<p>Inside each folder, you can add files that build up your page. Typically, in other frameworks, you would have a <code>index.{html/js/jsx}</code> file where everything the page needed would be loaded from. In SvelteKit, you can have multiple files, each representing a different part of the page.</p>
<p>The only required file is <code>+page.svelte</code>. This is where you can add your markup, components, styles and logic. However, it’s likely that your page might need to load some data to be displayed. This can be done on another file, <code>+page.js</code>, in the same folder. There, you can add a <code>load</code> function where you’ll have access to query params and other things and be able to load all the data your page needs.</p>
<p>The cool thing about that is that SvelteKit will use the content of the <code>+page.js</code> file <strong>both server-side and client-side</strong>, depending on the situation. However, if the data you need can only be loaded on the server (like for example querying a database or using a secret key), you can name it <code>+page.server.js</code> instead. Both would do the same thing, however ==the naming difference makes it much easier on a quick glance to identify what runs where.==</p>
<p>So, an example of what the basic file structure for my website would look like:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="plaintext"><code><span class="line"><span>└── routes</span></span>
<span class="line"><span>	├── +layout.svelte</span></span>
<span class="line"><span>	├── +page.svelte # / (root page)</span></span>
<span class="line"><span>	├── resume</span></span>
<span class="line"><span>		└── +page.svelte # /resume</span></span>
<span class="line"><span>	├── blog</span></span>
<span class="line"><span>		├── +page.svelte # /blog</span></span>
<span class="line"><span>		└── +page.js # Loads data to show on /blog</span></span>
<span class="line"><span>	├── blog-post-name</span></span>
<span class="line"><span>		└── +page.md # Blog post content (Markdown file)</span></span></code></pre>
<p>Having multiple files in a folder might look a bit cumbersome at first, but it makes their purpose <strong>much</strong> clearer. The great reasoning behind this pattern is that it gives you a single way of doing routing instead of multiple, which makes things more standardized and easier to understand later on. Remember, SvelteKit is opinionated on purpose, and I think folder-based routing is more readable and I’d say more flexible than other file-based routing solutions, although it is a bit more verbose.</p>
<p>The <code>+layout.svelte</code> file is a base layout for all the pages inside the route. Which means that I can have shared code for all pages in there. See the example below, where I added the header and footer components to the layout, and load the content of the route itself in the <code>&#x3C;slot></code> element:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="svelte"><code><span class="line"><span style="color:#E1E4E8">~filename +layout.svelte</span></span>
<span class="line"><span style="color:#E1E4E8">&#x3C;</span><span style="color:#79B8FF">Header</span><span style="color:#E1E4E8"> /></span></span>
<span class="line"></span>
<span class="line"><span style="color:#E1E4E8">&#x3C;</span><span style="color:#85E89D">main</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#E1E4E8">	&#x3C;</span><span style="color:#F97583">slot</span><span style="color:#E1E4E8"> /></span></span>
<span class="line"><span style="color:#E1E4E8">&#x3C;/</span><span style="color:#85E89D">main</span><span style="color:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#E1E4E8">&#x3C;</span><span style="color:#79B8FF">Footer</span><span style="color:#E1E4E8"> /></span></span></code></pre>
<h3 id="layout-groups">Layout Groups</h3>
<p>Having a <code>+layout.svelte</code> file is a great way to avoid re-writing the same base layout multiple times. However, sometimes you might want to have a different layout for a group of pages. For example, I wanted the blog posts to have some extra elements that the rest of the pages didn’t have, like a title and related posts at the bottom.</p>
<p>For that, I used a <strong><a href="https://kit.svelte.dev/docs/advanced-routing#advanced-layouts-group">layout group</a></strong>, defining that the post page shouldn’t use the main layout, but instead have its own.</p>
<p>To create a layout group, I’ve created a folder inside of <code>routes</code> with the name of the new layout <code>blog-article</code>, wrapped in parentheses to indicate it uses its own layout. The routes directory ended up like this:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="plaintext"><code><span class="line"><span>└── routes</span></span>
<span class="line"><span>	├── +layout.svelte</span></span>
<span class="line"><span>	├── +page.svelte # / (root page)</span></span>
<span class="line"><span>	├── resume</span></span>
<span class="line"><span>		└── +page.svelte # /resume</span></span>
<span class="line"><span>	├── blog</span></span>
<span class="line"><span>		├── +page.svelte # /blog</span></span>
<span class="line"><span>		└── +page.js # Loads data to show on /blog</span></span>
<span class="line"><span>	├── (blog-article)</span></span>
<span class="line"><span>		├── +layout.svelte # This layout applies only to this folder</span></span>
<span class="line"><span>		├── +layout.js # Loads data to show on the layout file</span></span>
<span class="line"><span>		├── [slug]</span></span>
<span class="line"><span>			├── +page.md # Blog post content (Markdown file)</span></span></code></pre>
<p>Keep in mind that, just like pages, layout files can also have their data-loading counterparts: <code>+layout.js</code> and <code>+layout.server.js</code>.</p>
<h2 id="the-blog">The blog</h2>
<p>The main challenge for me was in processing Markdown (.md) files of the blog posts into actual Svelte code. Unlike Jekyll, which I was using previously for this blog, SvelteKit doesn’t have anything built-in for this (yet) and online resources are a bit scarce (which is normal, since it’s still in beta).</p>
<p>I had three requisites for this:</p>
<ul>
<li>I wanted to write blog posts in Markdown, because of its ease, and also so I wouldn’t have to rewrite all the existing posts;</li>
<li>I wanted to be able to use Svelte components inside the blog posts as well, for more interactive elements;</li>
<li>It has to be rendered on build time so the blog can be deployed as a static site;</li>
</ul>
<p>I had heard about something similar to what I wanted, called MDX. It allows everything I wanted, however, it was React-based and I couldn’t use it. Luckily, I found out about <a href="https://mdsvex.com/">MDsveX</a>, a project with the same goal as MDX, but for Svelte!</p>
<p>After setting it up, having the Markdown content rendered was very straightforward. However, I need to use some of the post’s data to load stuff into the <code>+layout.svelte</code> file, like the title, image, date and related posts. Plus, I wanted to be able to get a list of all blog posts and be able to filter them, to show on the “Recent Posts” cards on the home page, as well as the blog page itself.</p>
<h3 id="extracting-post-data">Extracting post data</h3>
<p>I found out when searching that we can import all the .md files as modules, and process them via JS, since this code will run on the server. I ended up creating a <code>posts.js</code> file to centralize this logic, and did the following:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="javascript"><code><span class="line"><span style="color:#F97583">~</span><span style="color:#E1E4E8">filename posts.js</span></span>
<span class="line"><span style="color:#6A737D">// Import the markdown files for each post</span></span>
<span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> imports</span><span style="color:#F97583"> =</span><span style="color:#F97583"> import</span><span style="color:#E1E4E8">.</span><span style="color:#79B8FF">meta</span><span style="color:#E1E4E8">.</span><span style="color:#B392F0">globEager</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">'./posts/*.md'</span><span style="color:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> posts</span><span style="color:#F97583"> =</span><span style="color:#E1E4E8"> [];</span></span>
<span class="line"><span style="color:#F97583">for</span><span style="color:#E1E4E8"> (</span><span style="color:#F97583">const</span><span style="color:#79B8FF"> path</span><span style="color:#F97583"> in</span><span style="color:#E1E4E8"> imports) {</span></span>
<span class="line"><span style="color:#F97583">	const</span><span style="color:#79B8FF"> post</span><span style="color:#F97583"> =</span><span style="color:#E1E4E8"> imports[path];</span></span>
<span class="line"><span style="color:#F97583">	if</span><span style="color:#E1E4E8"> (post) {</span></span>
<span class="line"><span style="color:#6A737D">		// For each of them, MDsveX will do the heavy lifting. The "metadata"</span></span>
<span class="line"><span style="color:#6A737D">		// is automatically recovered from the Frontmatter</span></span>
<span class="line"><span style="color:#E1E4E8">		posts.</span><span style="color:#B392F0">push</span><span style="color:#E1E4E8">({</span></span>
<span class="line"><span style="color:#F97583">			...</span><span style="color:#E1E4E8">post.metadata</span></span>
<span class="line"><span style="color:#E1E4E8">		});</span></span>
<span class="line"><span style="color:#E1E4E8">	}</span></span>
<span class="line"><span style="color:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D">// Filter the post and order them by published date</span></span>
<span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> filteredPosts</span><span style="color:#F97583"> =</span><span style="color:#E1E4E8"> posts</span></span>
<span class="line"><span style="color:#E1E4E8">	.</span><span style="color:#B392F0">filter</span><span style="color:#E1E4E8">((</span><span style="color:#FFAB70">post</span><span style="color:#E1E4E8">) </span><span style="color:#F97583">=></span><span style="color:#F97583"> !</span><span style="color:#E1E4E8">post.hidden)</span></span>
<span class="line"><span style="color:#E1E4E8">	.</span><span style="color:#B392F0">sort</span><span style="color:#E1E4E8">((</span><span style="color:#FFAB70">a</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">b</span><span style="color:#E1E4E8">) </span><span style="color:#F97583">=></span></span>
<span class="line"><span style="color:#F97583">		new</span><span style="color:#B392F0"> Date</span><span style="color:#E1E4E8">(a.date).</span><span style="color:#B392F0">getTime</span><span style="color:#E1E4E8">() </span><span style="color:#F97583">></span><span style="color:#F97583"> new</span><span style="color:#B392F0"> Date</span><span style="color:#E1E4E8">(b.date).</span><span style="color:#B392F0">getTime</span><span style="color:#E1E4E8">()</span></span>
<span class="line"><span style="color:#F97583">			?</span><span style="color:#F97583"> -</span><span style="color:#79B8FF">1</span></span>
<span class="line"><span style="color:#F97583">			:</span><span style="color:#F97583"> new</span><span style="color:#B392F0"> Date</span><span style="color:#E1E4E8">(a.date).</span><span style="color:#B392F0">getTime</span><span style="color:#E1E4E8">() </span><span style="color:#F97583">&#x3C;</span><span style="color:#F97583"> new</span><span style="color:#B392F0"> Date</span><span style="color:#E1E4E8">(b.date).</span><span style="color:#B392F0">getTime</span><span style="color:#E1E4E8">()</span></span>
<span class="line"><span style="color:#F97583">			?</span><span style="color:#79B8FF"> 1</span></span>
<span class="line"><span style="color:#F97583">			:</span><span style="color:#79B8FF"> 0</span></span>
<span class="line"><span style="color:#E1E4E8">	);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D">// Expose this info to other files</span></span>
<span class="line"><span style="color:#F97583">export</span><span style="color:#F97583"> default</span><span style="color:#E1E4E8"> filteredPosts;</span></span></code></pre>
<p>With that data being exported from that file, I can reuse it in the places I need:</p>
<h4 id="blog-post-page">Blog post page</h4>
<p>On the <code>routes/(blog-article)/+layout.js</code> file, I can now import the posts and look for the one I should display (by comparing slugs). After that, I’ll pass the data I need back to the <code>+layout.svelte</code> file.</p>
<p>On the <code>routes/[slug]/+page.svelte</code> file, I can now import the posts and look for the one I should display (by comparing slugs). After that, I’ll get its module and use the special <code>&#x3C;svelte:component></code> (<a href="https://svelte.dev/docs#svelte_component">see docs</a>) tag to use it inside the page.</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="javascript"><code><span class="line"><span style="color:#F97583">~</span><span style="color:#B392F0">filename</span><span style="color:#E1E4E8"> (blog</span><span style="color:#F97583">-</span><span style="color:#E1E4E8">article)</span><span style="color:#F97583">/+</span><span style="color:#E1E4E8">layout.js</span></span>
<span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { filteredPosts } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> '$lib/data/posts'</span><span style="color:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">export</span><span style="color:#F97583"> async</span><span style="color:#F97583"> function</span><span style="color:#B392F0"> load</span><span style="color:#E1E4E8">({ </span><span style="color:#FFAB70">url</span><span style="color:#E1E4E8"> }</span><span style="color:#F97583">:</span><span style="color:#E1E4E8"> { </span><span style="color:#FFAB70">url</span><span style="color:#F97583">:</span><span style="color:#E1E4E8"> { </span><span style="color:#FFAB70">pathname</span><span style="color:#F97583">:</span><span style="color:#79B8FF"> string</span><span style="color:#E1E4E8"> } }) {</span></span>
<span class="line"><span style="color:#F97583">	const</span><span style="color:#E1E4E8"> { </span><span style="color:#79B8FF">pathname</span><span style="color:#E1E4E8"> } </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> url;</span></span>
<span class="line"><span style="color:#F97583">	const</span><span style="color:#79B8FF"> slug</span><span style="color:#F97583"> =</span><span style="color:#E1E4E8"> pathname.</span><span style="color:#B392F0">replace</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">'/'</span><span style="color:#E1E4E8">, </span><span style="color:#9ECBFF">''</span><span style="color:#E1E4E8">);</span></span>
<span class="line"><span style="color:#F97583">	const</span><span style="color:#79B8FF"> post</span><span style="color:#F97583"> =</span><span style="color:#E1E4E8"> filteredPosts.</span><span style="color:#B392F0">find</span><span style="color:#E1E4E8">((</span><span style="color:#FFAB70">post</span><span style="color:#E1E4E8">) </span><span style="color:#F97583">=></span><span style="color:#E1E4E8"> post.slug </span><span style="color:#F97583">===</span><span style="color:#E1E4E8"> slug);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">	return</span><span style="color:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#E1E4E8">		post</span></span>
<span class="line"><span style="color:#E1E4E8">	};</span></span>
<span class="line"><span style="color:#E1E4E8">}</span></span></code></pre>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="svelte"><code><span class="line"><span style="color:#E1E4E8">~filename (blog-article)/+layout.svelte</span></span>
<span class="line"><span style="color:#E1E4E8">&#x3C;</span><span style="color:#85E89D">script</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#6A737D">	// Declare the data variable that comes from the server</span></span>
<span class="line"><span style="color:#6A737D">	// And extract its post property</span></span>
<span class="line"><span style="color:#F97583">	export</span><span style="color:#F97583"> let</span><span style="color:#E1E4E8"> data;</span></span>
<span class="line"><span style="color:#B392F0">	$</span><span style="color:#E1E4E8">: ({ post } </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> data);</span></span>
<span class="line"><span style="color:#E1E4E8">&#x3C;/</span><span style="color:#85E89D">script</span><span style="color:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D">&#x3C;!-- Here we can use the post data we need --></span></span>
<span class="line"><span style="color:#E1E4E8">&#x3C;</span><span style="color:#85E89D">h1</span><span style="color:#E1E4E8">>{post.title}&#x3C;/</span><span style="color:#85E89D">h1</span><span style="color:#E1E4E8">></span></span></code></pre>
<h4 id="listing-all-posts">Listing all posts</h4>
<p>Now the post page is done, we need a way to display all the posts on the main blog page. We already have them sorted and filtered in the <code>posts.js</code> file, so all I have to do is fetch that data and iterate on it to display them all.</p>
<p>Since the posts data comes from the server, it needs to run there. I’ll use a <code>blog/+page.server.js</code> file to fetch that data and send it to the client-side page.</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="javascript"><code><span class="line"><span style="color:#F97583">~</span><span style="color:#E1E4E8">filename blog</span><span style="color:#F97583">/+</span><span style="color:#E1E4E8">page.server.js</span></span>
<span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { error } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> '@sveltejs/kit'</span><span style="color:#E1E4E8">;</span></span>
<span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> posts </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> '$lib/posts'</span><span style="color:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">export</span><span style="color:#F97583"> async</span><span style="color:#F97583"> function</span><span style="color:#B392F0"> load</span><span style="color:#E1E4E8">() {</span></span>
<span class="line"><span style="color:#F97583">	const</span><span style="color:#79B8FF"> result</span><span style="color:#F97583"> =</span><span style="color:#E1E4E8"> Object.</span><span style="color:#B392F0">keys</span><span style="color:#E1E4E8">(posts).</span><span style="color:#B392F0">map</span><span style="color:#E1E4E8">((</span><span style="color:#FFAB70">index</span><span style="color:#E1E4E8">) </span><span style="color:#F97583">=></span><span style="color:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#F97583">		const</span><span style="color:#E1E4E8"> { </span><span style="color:#79B8FF">slug</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">title</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">date</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">excerpt</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">tags</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">readingTime</span><span style="color:#E1E4E8"> } </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> posts[index];</span></span>
<span class="line"><span style="color:#F97583">		return</span><span style="color:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#E1E4E8">			slug,</span></span>
<span class="line"><span style="color:#E1E4E8">			title,</span></span>
<span class="line"><span style="color:#E1E4E8">			date,</span></span>
<span class="line"><span style="color:#E1E4E8">			excerpt,</span></span>
<span class="line"><span style="color:#E1E4E8">			tags,</span></span>
<span class="line"><span style="color:#E1E4E8">			readingTime</span></span>
<span class="line"><span style="color:#E1E4E8">		};</span></span>
<span class="line"><span style="color:#E1E4E8">	});</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">	if</span><span style="color:#E1E4E8"> (result) {</span></span>
<span class="line"><span style="color:#F97583">		return</span><span style="color:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#E1E4E8">			posts: result</span></span>
<span class="line"><span style="color:#E1E4E8">		};</span></span>
<span class="line"><span style="color:#E1E4E8">	}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">	throw</span><span style="color:#B392F0"> error</span><span style="color:#E1E4E8">(</span><span style="color:#79B8FF">500</span><span style="color:#E1E4E8">, </span><span style="color:#9ECBFF">`Could not load blog posts`</span><span style="color:#E1E4E8">);</span></span>
<span class="line"><span style="color:#E1E4E8">}</span></span></code></pre>
<p>And, on the page itself:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="svelte"><code><span class="line"><span style="color:#E1E4E8">~filename blog/+page.svelte</span></span>
<span class="line"><span style="color:#E1E4E8">&#x3C;</span><span style="color:#85E89D">script</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#F97583">	export</span><span style="color:#F97583"> let</span><span style="color:#E1E4E8"> data;</span></span>
<span class="line"><span style="color:#F97583">	let</span><span style="color:#E1E4E8"> { posts } </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> data;</span></span>
<span class="line"><span style="color:#E1E4E8">&#x3C;/</span><span style="color:#85E89D">script</span><span style="color:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#E1E4E8">{#</span><span style="color:#F97583">each</span><span style="color:#E1E4E8"> posts </span><span style="color:#F97583">as</span><span style="color:#E1E4E8"> post}</span></span>
<span class="line"><span style="color:#E1E4E8">	&#x3C;</span><span style="color:#79B8FF">BlogPostCard</span><span style="color:#B392F0"> {</span><span style="color:#E1E4E8">post</span><span style="color:#B392F0">}</span><span style="color:#E1E4E8"> /></span></span>
<span class="line"><span style="color:#E1E4E8">{/</span><span style="color:#F97583">each</span><span style="color:#E1E4E8">}</span></span></code></pre>
<h3 id="rss">RSS</h3>
<p>Something that SvelteKit currently does not provide a solution for is a RSS feed. Previously, mine was built automatically by Jekyll and I never had to do anything to get it working. While this wasn’t the case right now, I’m sure once SvelteKit is stable and mature, solutions for this will be available so no manual work has to be done.</p>
<p>I made mine by taking advantage of <a href="https://kit.svelte.dev/docs/routing#server">SvelteKit’s endpoints</a> by creating a <code>+server.js</code> file, and serving a XML file that is generated at build time. I import the filtered posts from the <code>posts.js</code> file I created earlier, and use the metadata to build the content of the RSS file. You can check out <a href="https://github.com/matfantinel/sveltekit-static-blog-template/blob/main/src/routes/rss.xml/%2Bserver.ts">the source code</a> for implementation details.</p>
<h3 id="sitemap">Sitemap</h3>
<p>Another thing that’s still not built-in to SvelteKit is the sitemap. It’s great for SEO, especially for newer websites, so I researched a good way of adding one. Luckily, someone already built a tool to generate them automatically, and there is already a discussion to build it into SvelteKit directly. Check out <a href="https://github.com/bartholomej/svelte-sitemap">svelte-sitemap</a> for generating your own.</p>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>My first experience with Svelte and SvelteKit was, overall, fantastic. It simplifies many parts of development and having server-side-rendering as a default option really drives forward their purpose of simplifying web both for users and developers.</p>
<p>Though I definitely did not use all of Svelte’s strengths on this project, since it doesn’t need dynamic data and an application state, I’ll be working with that soon enough. Its ecosystem is not as big as its competitors’, since Svelte is still small compared to them, but they’re doing something right with their new approach, as Svelte is the most loved web framework according to <a href="https://insights.stackoverflow.com/survey/2021#section-most-loved-dreaded-and-wanted-web-frameworks">2021’s StackOverflow survey</a>. Not to mention that Svelte itself doesn’t try to reinvent the wheel, and therefore is compatible with almost everything JavaScript.</p>
<p>The small issues I’ve had while building this will definitely be gone soon, as they were caused by SvelteKit not being on a stable version yet. I’m excited to see how much the ecosystem grows the next year; I’ll definitely be keeping an eye on it!</p>
<p>Don’t forget all the code for this website and blog are open source, feel free to use it and to propose changes if you’d like!</p>
<p>Thanks for reading!</p>
<p><a href="https://github.com/matfantinel/sveltekit-static-blog-template" title="button || color=secondary">Check out the starting template here!</a></p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/cms/media/articles/blog-development-sveltekit/cover.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/cms/media/articles/blog-development-sveltekit/cover.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/web-images-modern-formats</guid>
      <title>Smarter, Lighter, Better Images: A Guide to Optimization</title>
      <description>Learn how to reduce page loading times and bounce rate.</description>
      <link>https://fantinel.dev/blog/web-images-modern-formats</link>
      <pubDate>Sat, 30 Jan 2021 00:00:00 +0000</pubDate>
      <category>Front-End</category><category>Dev</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/web-images-modern-formats">
              read on the site!
            </a>
          </strong>
        </div>

        <p>Do you know how big the images displayed on your website are? When you open a page, the browser starts downloading a bunch of files in order to display it. Research shows that <a href="https://httparchive.org/reports/state-of-images">images are the most requested asset type</a> and take up more bandwidth than any other resource. So, making sure they are as small as they can be can greatly improve the load times for your website. (spoiler alert: mine’s become 85% faster!)</p>
<h2 id="more-efficient-formats">More efficient formats</h2>
<p>For a long time, JPGs and PNGs have been our standard image formats. However, they are not optimized for the web - their quality is often unnecessarily high and the download size is too big. Over time, many new formats have appeared, but two of them have become quite notable: <a href="https://en.wikipedia.org/wiki/WebP">WebP</a> and <a href="https://www.lambdatest.com/blog/avif-image-format/">AVIF</a>.</p>
<p>WebP has been introduced in 2010, and has slowly gained adoption since then. Since 2020, <a href="https://caniuse.com/webp">WebP is now supported in all modern browsers</a>. AVIF <a href="https://jakearchibald.com/2020/avif-has-landed/">was launched in 2020</a>, and its adoption has been faster. As of May 2023 it is supported by all major browsers except Edge. You can always check out the up-to-date support status on <a href="https://caniuse.com/avif">caniuse.com</a>.</p>
<h3 id="browser-support">Browser Support</h3>
<p><em>But how do we use those shiny new formats if not all browsers support them?</em></p>
<p>With the <code>&#x3C;img></code> element, we can make the browsers do the work for us. We can declare multiple sources for the same image, and the browser will try to load them in order. If they do not support a format, they will immediatelly jump to the next one. To do that, we use the <code>srcset</code> property to declare the optimal versions (avif and webp), and then use the <code>src</code> as usual to point to the fallback image (jpg or png).</p>
<p>So, what we want to do is declare those different sources in the following order:</p>
<p>AVIF -> WebP -> JPG (or PNG)</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="html"><code><span class="line"><span style="color:#E1E4E8">&#x3C;</span><span style="color:#85E89D">img</span><span style="color:#E1E4E8"> </span></span>
<span class="line"><span style="color:#B392F0">  srcset</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"my-image.avif, my-image.webp"</span><span style="color:#E1E4E8"> </span></span>
<span class="line"><span style="color:#B392F0">  src</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"my-image.jpg"</span><span style="color:#E1E4E8"> </span></span>
<span class="line"><span style="color:#B392F0">  loading</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"lazy"</span><span style="color:#E1E4E8"> </span></span>
<span class="line"><span style="color:#B392F0">  decoding</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"async"</span><span style="color:#E1E4E8"> </span></span>
<span class="line"><span style="color:#E1E4E8">/></span></span></code></pre>
<p>If you look at the resulting HTML in your website, you can see that the <code>&#x3C;img></code> element has a <code>src</code> defined, but when you hover over it, it shows what is the actual file that’s being loaded. If you’re on a supported browser, it will have loaded the AVIF file. If you’re on Safari, it will have loaded the WebP one. Otherwise, if you’re using IE or something (I’m sorry), the original JPG or PNG file will be loaded.</p>
<p><img src="https://fantinel.dev/cms/media/articles/web-images-modern-formats/generated-html.png" alt="Screenshot of the generated HTML code. Hovering over the PNG filename reveals that an AVIF file is being downloaded instead." title="The img tag shows the PNG file as source, but hovering the mouse over it reveals that the AVIF file is the one that actually loaded."></p>
<h2 id="load-smaller-images">Load smaller images</h2>
<p>You can optimize even further than that. See, in my example, I am loading an image with a width of 1200px, however, the size it’s being displayed is only 319px wide. The <code>srcset</code> property supports different widths to load, depending on the screen size.</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="html"><code><span class="line"><span style="color:#E1E4E8">&#x3C;</span><span style="color:#85E89D">img</span></span>
<span class="line"><span style="color:#B392F0">	srcset</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"my-image-380w.avif 380w, my-image-640w.avif 640w, my-image-960w.avif 960w"</span></span>
<span class="line"><span style="color:#B392F0">	sizes</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"(max-width: 979px) 100vw, 640px"</span></span>
<span class="line"><span style="color:#B392F0">	src</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"my-image.jpg"</span></span>
<span class="line"><span style="color:#B392F0">	loading</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"lazy"</span></span>
<span class="line"><span style="color:#B392F0">	decoding</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"async"</span></span>
<span class="line"><span style="color:#E1E4E8">/></span></span></code></pre>
<p>The <code>srcset</code> property is smart. As the name implies, it is a set of sources, not just a single one. When we declare multiple file paths and add a width unit besides it, the browser looks at this data and tries to display the smallest possible image.</p>
<p>On the code snippet above, the browser will follow this: ==If the size of the displayed image (on the page) is smaller or equal to 380px, it will load the file with 380px of width. Otherwise, it will try to load the next declared path (640px).==</p>
<p><strong>However,</strong> not all parts of this process are smarty. The browser cannot know what is the final size of the image on the page before it actually loads it. Which is why the <code>sizes</code> property exists. Let’s see how it works:</p>
<p>The <code>sizes</code> property defines what rule the browser will use to get the width it uses to choose the correct file in <code>srcset</code>. The default value is <code>100vw</code>. That means that, to check what width the image will have, the browser just gets the width of the browser window. If we know the exact size the image will have on load, we can declare it here, or if we don’t know the exact size, we can estimate. We can use media queries to help us specify the sizes better as well.</p>
<p>Check out the value on the example: <code>(max-width: 979px) 100vw, 640px</code>. What that code does is: if the width of the viewport is equal or smaller than 979px, use 100vw. Else, use 640px.</p>
<p>It is easier to understand if we visualize it like this:</p>
<p><img src="https://fantinel.dev/cms/media/articles/web-images-modern-formats/sizes.png" alt="Screenshots picturing how the sizes property affects the image loading on both mobile and desktop" title="On mobile, the image width is almost the same as the viewport, so it&#x27;s okay to use 100vw. On desktop, we usually limit the image width, so 640px was the sweet spot in this particular case."></p>
<p>Of course, different websites have different needs and situations. Make sure to adapt the code to your specific need.</p>
<p>It’s also worth noting that most phones use a HiDPI mode. This means that ==even though the reported width for the phone above is 375px, the browser will likely use a higher resolution to load the images== (usually 2x), in order to serve a higher quality image.</p>
<h2 id="lazy-loading-and-async-decoding">Lazy Loading and Async Decoding</h2>
<p>You might have noticed the <code>loading="lazy"</code> and <code>decoding="async"</code> attributes in the code above. Those are relatively new options that are part of an ongoing effort to make the web faster.</p>
<p><code>decoding="async"</code> tells your browser it can try to parallelize loading your image. When your page is loading, it tries to decode both text and images at the same time. On lower-end devices though, decoding heavy images can take a while, and this might block the rendering of the rest of the content. With this option, the browser will try to proceed rendering the rest of the content and render the image later. This can be a great improvement to perceived performance.</p>
<p><code>loading="lazy"</code> is probably the most important of the two. It is an easy way of telling the browser to ==only load the images when they get close to appearing in the viewport==. There is a threshold that is defined by the browser that controls how close it needs to be before it gets loaded, so you don’t have to worry about them not showing up if the user scrolls fast. ==This ensures that the initial load of the website is as lean as it can get, improving perceived performance and also saving you some money on server requests.==</p>
<p><img src="https://fantinel.dev/cms/media/articles/web-images-modern-formats/first-load-requests.png" alt="Screenshot of the browser tools showing network requests: There are only two images that have been downloaded." title="When the website is initially loaded, it only downloads what&#x27;s needed: my avatar image and the preview of the first blog post, that will show up after scrolling a bit."></p>
<p><img src="https://fantinel.dev/cms/media/articles/web-images-modern-formats/after-scrolling-requests.png" alt="Screenshot of the browser tools showing network requests: now, there are many more images downloaded." title="When I scroll down the page, the images are downloaded as they&#x27;re close to appearing."></p>
<h2 id="results-in-practice">Results In Practice</h2>
<p>Since I like using my own website and blog as a testbed for new stuff that I learn, I have applied these optimizations to it. The results were incredible!</p>
<p>Note: after doing some tests, I have decided that the benefits of serving differently-sized images on my website were too small to justify the extra effort of handling all these extra images. So, the only optimizations I have really applied were the optimized file formats, lazy loading and async decoding. I also chose PNG as fallback type instead of JPG because some of my images have transparency in them, which JPG does not support.</p>
<p>The following data is taken from the home page of the website, since it has a lot of images:</p>
<p><img src="https://fantinel.dev/cms/media/articles/web-images-modern-formats/results.png" alt="Screenshot of browser tool network requests, from before and after optimization. Before: 1.6MB download; After: 249KB download"></p>
<p>==The total download size decreased by a whopping 85%!!== That’s an incredible difference, with no noticeable difference in quality. Your results may vary, as they depend on how much of your website’s size is images.</p>
<p>Before the changes, out of 1.6MB total, 92% of it were images, 5% were fonts, 1% was HTML, and the remaining 2% were of JS and other things like the web manifest.</p>
<p>Now, out of 249kB, just 24% of it are images. Fonts now make up 27% of page size, and are likely the target of a future optimization post!</p>
<h2 id="the-hard-part">The Hard Part</h2>
<p>The hardest part of this process is converting the images to all necessary formats and sizes. It is a lot of effort to do manually even for a single image, and even worse if you’re trying to optimize existing images like I was.</p>
<h3 id="generating-the-images">Generating the Images</h3>
<p>For my needs, I have developed a NodeJS script that uses the <a href="https://github.com/lovell/sharp">Sharp</a> library to do the magic for me. It accepts as parameters a source and a target folder, input file types (what files it will look for in the source folder), output file types (what types it will convert to), as well as the desired widths.</p>
<p>You can install the script as a NPM package, and then run it on a build command on your website!</p>
<p><a href="https://github.com/matfantinel/image-transmutation" title="button || color=secondary">Check out image-transmutation!</a>
<a href="https://github.com/matfantinel/sveltekit-static-blog-template/blob/main/package.json" title="button || color=secondary style=clear">See usage example</a></p>
<h3 id="using-the-images">Using the images</h3>
<p>To make this setup work, I had to do some changes on how images were used on my website.</p>
<p>Pre-existing conditions:</p>
<ul>
<li>All the images on my website were initially in a folder called “images”, with various subfolders;</li>
<li>I didn’t want all the versions of an image (webp, avif, etc) to take up space on my repository, so I only have them generated on build time.</li>
</ul>
<p>Modifications I did:</p>
<ul>
<li>I have created a component to centralize all image-loading logic;</li>
<li>This component receives two parameters: the file path and the alt text;</li>
<li>If my project is running in dev mode, it won’t add the srcset at all, as the optimized images won’t exist;</li>
</ul>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="html"><code><span class="line"><span style="color:#E1E4E8">&#x3C;</span><span style="color:#85E89D">script</span><span style="color:#B392F0"> lang</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"ts"</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#6A737D">	// This is a SvelteKit environment variable. If it's true, it means the project is running in dev mode.</span></span>
<span class="line"><span style="color:#F97583">	import</span><span style="color:#E1E4E8"> { dev } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> '$app/environment'</span><span style="color:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D">	// Declare the accepted parameters</span></span>
<span class="line"><span style="color:#F97583">	export</span><span style="color:#F97583"> let</span><span style="color:#E1E4E8"> src</span><span style="color:#F97583">:</span><span style="color:#79B8FF"> string</span><span style="color:#E1E4E8">;</span></span>
<span class="line"><span style="color:#F97583">	export</span><span style="color:#F97583"> let</span><span style="color:#E1E4E8"> alt</span><span style="color:#F97583">:</span><span style="color:#79B8FF"> string</span><span style="color:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D">	// Declare the formats used for images</span></span>
<span class="line"><span style="color:#F97583">	export</span><span style="color:#F97583"> let</span><span style="color:#E1E4E8"> formats</span><span style="color:#F97583">:</span><span style="color:#79B8FF"> string</span><span style="color:#E1E4E8">[] </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> [</span><span style="color:#9ECBFF">'avif'</span><span style="color:#E1E4E8">, </span><span style="color:#9ECBFF">'webp'</span><span style="color:#E1E4E8">, </span><span style="color:#9ECBFF">'png'</span><span style="color:#E1E4E8">];</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D">	// Get just the file name without extension (so "image.png" becomes "image")</span></span>
<span class="line"><span style="color:#B392F0">	$</span><span style="color:#E1E4E8">: fileName </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> src.</span><span style="color:#B392F0">split</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">'.'</span><span style="color:#E1E4E8">)[</span><span style="color:#79B8FF">0</span><span style="color:#E1E4E8">];</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D">	// Function to build the srcset string</span></span>
<span class="line"><span style="color:#F97583">	function</span><span style="color:#B392F0"> buildSrcset</span><span style="color:#E1E4E8">() {</span></span>
<span class="line"><span style="color:#6A737D">		// If project is in dev mode, I don't want a srcset since images aren't optimized yet</span></span>
<span class="line"><span style="color:#F97583">		if</span><span style="color:#E1E4E8"> (dev) </span><span style="color:#F97583">return</span><span style="color:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">		let</span><span style="color:#E1E4E8"> srcset </span><span style="color:#F97583">=</span><span style="color:#9ECBFF"> ''</span><span style="color:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D">		// Cycle through formats and add them to the srcset</span></span>
<span class="line"><span style="color:#F97583">		for</span><span style="color:#E1E4E8"> (</span><span style="color:#F97583">let</span><span style="color:#E1E4E8"> i </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> 0</span><span style="color:#E1E4E8">; i </span><span style="color:#F97583">&#x3C;</span><span style="color:#E1E4E8"> formats.</span><span style="color:#79B8FF">length</span><span style="color:#E1E4E8">; i</span><span style="color:#F97583">++</span><span style="color:#E1E4E8">) {</span></span>
<span class="line"><span style="color:#E1E4E8">			srcset </span><span style="color:#F97583">+=</span><span style="color:#9ECBFF"> `${</span><span style="color:#E1E4E8">fileName</span><span style="color:#9ECBFF">}.${</span><span style="color:#E1E4E8">formats</span><span style="color:#9ECBFF">[</span><span style="color:#E1E4E8">i</span><span style="color:#9ECBFF">]</span><span style="color:#9ECBFF">}`</span><span style="color:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">			if</span><span style="color:#E1E4E8"> (i </span><span style="color:#F97583">&#x3C;</span><span style="color:#E1E4E8"> formats.</span><span style="color:#79B8FF">length</span><span style="color:#F97583"> -</span><span style="color:#79B8FF"> 1</span><span style="color:#E1E4E8">) {</span></span>
<span class="line"><span style="color:#E1E4E8">				srcset </span><span style="color:#F97583">+=</span><span style="color:#9ECBFF"> ', '</span><span style="color:#E1E4E8">;</span></span>
<span class="line"><span style="color:#E1E4E8">			}</span></span>
<span class="line"><span style="color:#E1E4E8">		}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">		return</span><span style="color:#E1E4E8"> srcset;</span></span>
<span class="line"><span style="color:#E1E4E8">	}</span></span>
<span class="line"><span style="color:#E1E4E8">&#x3C;/</span><span style="color:#85E89D">script</span><span style="color:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D">&#x3C;!-- Now we just use the default img element! --></span></span>
<span class="line"><span style="color:#E1E4E8">&#x3C;</span><span style="color:#85E89D">img</span><span style="color:#B392F0"> srcset</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"{buildSrcset()}"</span><span style="color:#B392F0"> {src}</span><span style="color:#B392F0"> {alt}</span><span style="color:#B392F0"> loading</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"lazy"</span><span style="color:#B392F0"> decoding</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"async"</span><span style="color:#E1E4E8"> /></span></span></code></pre>
<p>And to use this component inside another page:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="html"><code><span class="line"><span style="color:#E1E4E8">&#x3C;</span><span style="color:#FDAEB7;font-style:italic">image</span><span style="color:#B392F0"> src</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"my-image.png"</span><span style="color:#B392F0"> alt</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"Example image"</span><span style="color:#E1E4E8"> /></span></span></code></pre>
<p>The full component code and usage example can be seen at <a href="https://github.com/matfantinel/sveltekit-static-blog-template/blob/main/src/lib/components/atoms/Image.svelte">my SvelteKit blog template repository</a>. That might be more up to date than here, even!</p>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>With such amazing results, it’s hard not to recommend you to optimize the images in your website ASAP. There sure are more approaches and optimizations that can be done besides the ones presented on this article, as the web world is always changing. But optimizations are a great way of showing respect to your users (as well as gathering more of them). Your site loads more quickly, and it uses less data and resources.</p>
<p>As front-end developers, we must acknowledge that all we write runs on someone else’s computer, the user’s. So it is important that we respect them and make sure we just use the resources we need to.</p>
<p>I hope you enjoyed reading this! Take care and happy coding!</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/cms/media/articles/web-images-modern-formats/cover.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/cms/media/articles/web-images-modern-formats/cover.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/fixing-favicons</guid>
      <title>How to fix your Favicons</title>
      <description>Favicons suck. Luckily, there are ways to make them suck less.</description>
      <link>https://fantinel.dev/blog/fixing-favicons</link>
      <pubDate>Wed, 06 Jan 2021 00:00:00 +0000</pubDate>
      <category>Dev</category><category>Front-End</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/fixing-favicons">
              read on the site!
            </a>
          </strong>
        </div>

        <p>Favicons kinda suck. They should be a simple icon that identifies your webpage on a bunch of scenarios, i.e. the icon displayed on the tab besides your website’s title, or the icon on the mobile browser’s bookmarks screen, or the icon on the phone’s home screen.</p>
<p>Unfortunately, different browsers, OSs, and implementations throughout the years have ended up with many different files and meta tags, that we as developers need to handle otherwise we end up with missing or pixelated icons.</p>
<h2 id="easy-mode">Easy Mode</h2>
<p>I recently met my new best friend, <a href="https://realfavicongenerator.net/">Real Favicon Generator</a>. It took all these pains I just wrote about and just made it a breeze to handle. Best of all, it’s customizable!</p>
<p>You just have to add in your existing favicon image (for best results, a SVG or high-quality PNG is recommended). From then on, the generator will display previews and allow customization of each category of favicon it will generate:</p>
<p><img src="https://fantinel.dev/cms/media/articles/fixing-favicons/favicon-generator-customization.jpg" alt="Screenshot of Real Favicon Generator&#x27;s customization" title="The customization options allow you to set different icons depending on device and OS, and even generate icons with background colors if needed."></p>
<p>At the bottom, you can even set up ways of avoiding caching issues, compression, and scaling algorithms (!). After all that, it will generate them all as set, give you a zip with all the files, and the HTML code to paste on your <code>&#x3C;head></code>. You can check the result on this very website (fantinel.dev). The favicon should look fantastic on every device/browser combination.</p>
<h2 id="manual-mode">Manual Mode</h2>
<p>So, there are 5 types of favicons that we need to add, if we want to support every current browser:</p>
<h3 id="1-desktop-browsers">1. Desktop browsers</h3>
<ul>
<li><code>favicon.ico</code>, for IE and any other legacy browsers. Optional if you don’t want to support it;</li>
<li><code>favicon-16x16.png</code>, the classic one that’s displayed on the tabs;</li>
<li><code>favicon-32x32.png</code>, used on Safari for macOS.</li>
</ul>
<h3 id="2-android-browsers">2. Android browsers</h3>
<ul>
<li><code>android-chrome-192x192.png</code>, shown on the tab card and when added to home screen;</li>
<li><code>android-chrome-512x512.png</code>, shown on the splash screen for when the website is installed to the phone. Optional if it’s not a PWA;</li>
</ul>
<h3 id="3-ios-safari-and-some-android-browsers-like-samsung-internet">3. iOS Safari, and some Android browsers like Samsung Internet</h3>
<ul>
<li><code>apple-touch-icon.png</code>, a 180x180 file displayed when the website is added to home screen;</li>
</ul>
<h3 id="4-windows-810-start-menu-optional">4. Windows 8/10 start menu (optional)</h3>
<ul>
<li><code>mstile-150x150.png</code>, only used when your website is added to the start menu as a tile on Windows 8/10, and when not declared, defaults to <code>apple-touch-icon.png</code> instead.</li>
</ul>
<h3 id="5-macos-safari-pinned-tabs-optional">5. macOS Safari Pinned Tabs (optional)</h3>
<ul>
<li><code>safari-pinned-tab.svg</code> is used when users pin a tab on Safari for macOS. Basically, you declare a monochrome SVG and a theme color. Safari does the rest.</li>
</ul>
<p><img src="https://fantinel.dev/cms/media/articles/fixing-favicons/safari-pinned-tabs-demo.jpg" alt="Demo of Safari pinned tabs favicon behavior"></p>
<p>With those files, you should be good to go on every single browser, by adding this to your <code>&#x3C;head></code>:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="html"><code><span class="line"><span style="color:#E1E4E8">&#x3C;</span><span style="color:#85E89D">head</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#E1E4E8">	&#x3C;</span><span style="color:#85E89D">link</span><span style="color:#B392F0"> rel</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"apple-touch-icon"</span><span style="color:#B392F0"> sizes</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"180x180"</span><span style="color:#B392F0"> href</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"/apple-touch-icon.png"</span><span style="color:#E1E4E8"> /></span></span>
<span class="line"><span style="color:#E1E4E8">	&#x3C;</span><span style="color:#85E89D">link</span><span style="color:#B392F0"> rel</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"icon"</span><span style="color:#B392F0"> type</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"image/png"</span><span style="color:#B392F0"> sizes</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"32x32"</span><span style="color:#B392F0"> href</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"/favicon-32x32.png"</span><span style="color:#E1E4E8"> /></span></span>
<span class="line"><span style="color:#E1E4E8">	&#x3C;</span><span style="color:#85E89D">link</span><span style="color:#B392F0"> rel</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"icon"</span><span style="color:#B392F0"> type</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"image/png"</span><span style="color:#B392F0"> sizes</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"16x16"</span><span style="color:#B392F0"> href</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"/favicon-16x16.png?"</span><span style="color:#E1E4E8"> /></span></span>
<span class="line"><span style="color:#E1E4E8">	&#x3C;</span><span style="color:#85E89D">link</span><span style="color:#B392F0"> rel</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"manifest"</span><span style="color:#B392F0"> href</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"/site.webmanifest"</span><span style="color:#E1E4E8"> /></span></span>
<span class="line"><span style="color:#E1E4E8">	&#x3C;</span><span style="color:#85E89D">link</span><span style="color:#B392F0"> rel</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"mask-icon"</span><span style="color:#B392F0"> href</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"/safari-pinned-tab.svg"</span><span style="color:#B392F0"> color</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"#000000"</span><span style="color:#E1E4E8"> /></span></span>
<span class="line"><span style="color:#E1E4E8">	&#x3C;</span><span style="color:#85E89D">link</span><span style="color:#B392F0"> rel</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"shortcut icon"</span><span style="color:#B392F0"> href</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"/favicon.ico"</span><span style="color:#E1E4E8"> /></span></span>
<span class="line"><span style="color:#E1E4E8">&#x3C;/</span><span style="color:#85E89D">head</span><span style="color:#E1E4E8">></span></span></code></pre>
<h2 id="single-svg-favicon">Single SVG Favicon</h2>
<p>Something that’s starting to become a thing is having a single SVG favicon. Theoretically, SVGs are infinitely scalable, and should look good on all sizes. Unfortunately, browser support is not there yet.</p>
<p><a href="https://caniuse.com/link-icon-svg">Can I Use</a> reports that, currently (January 2021), only recent versions of Firefox and Chromium-based browsers support this feature. This means that Safari, non-Chromium Edge, IE, and some mobile browsers do not support it. Therefore, unless you want your favicon to just not appear in those browsers, you’d still have to use other file formats as well. The downside of this approach is that all favicons would look the same, and you won’t be able to customize and have differently-shaped icons for mobile devices, for example.</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="html"><code><span class="line"><span style="color:#E1E4E8">&#x3C;</span><span style="color:#85E89D">head</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#E1E4E8">	&#x3C;</span><span style="color:#85E89D">link</span><span style="color:#B392F0"> rel</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"icon"</span><span style="color:#B392F0"> href</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"favicon.svg"</span><span style="color:#E1E4E8"> /></span></span>
<span class="line"><span style="color:#E1E4E8">&#x3C;/</span><span style="color:#85E89D">head</span><span style="color:#E1E4E8">></span></span></code></pre>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>Favicons suck. Luckily, there are people working to make it suck less. With the approach presented on this article, your website should be able to display its best favicon on all browsers for years to come.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/cms/media/articles/fixing-favicons/cover.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/cms/media/articles/fixing-favicons/cover.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/github-profile-readme</guid>
      <title>Spicing Up your GitHub Profile with HTML and CSS</title>
      <description>Make your GitHub profile more appealing with what you do best: code!</description>
      <link>https://fantinel.dev/blog/github-profile-readme</link>
      <pubDate>Sat, 02 Jan 2021 00:00:00 +0000</pubDate>
      <category>Dev</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/github-profile-readme">
              read on the site!
            </a>
          </strong>
        </div>

        <p>Last year, GitHub added a new cool feature for the user profile. You can now add a README file to it, and it will show up besides your profile pic. This is great for talking a bit about yourself and what you do, putting some contact info, or simply making a cool first impression.</p>
<h2 id="creating-the-repository">Creating the Repository</h2>
<p>For adding this to your profile, there’s a little secret. Instead of an option in your “Edit Profile” settings, this is achieved by ==creating a new repository with the same name as your GitHub username.==</p>
<p><img src="https://fantinel.dev/cms/media/articles/github-profile-readme/repo-creation.jpg" alt="Screenshot of GitHub repository creation" title="🎉️ Creating a new repo with the same name as your username immediatelly makes GitHub praise you for finding out this secret."></p>
<p>You can choose to initialize the repository with a README file already. After that, the content of this file will already start showing on your personal profile.</p>
<p>The README is just a Markdown file, which may make you think initially that you can only write in stuff with GitHub’s default styling. However, when you get creative, there’s a lot more you can do. You can add images and GIFs to it, sure, but SVGs are the real heros here. By adding SVG files to the Markdown file, you enable a lot of possibilities. SVG files support HTML tags and CSS styles (including animations!)</p>
<p>For adding HTML to the SVG, we can use the <code>&#x3C;foreignObject></code> tag. This is an element that can include elements from different XML namespaces. Which means that even though you’re in a SVG namespace, you can use XHTML elements and all the features it supports, including the <code>&#x3C;style></code> tag for adding CSS.</p>
<p>As an example, check out the SVG file I use to add the “tags” with technologies to my profile:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="html"><code><span class="line"><span style="color:#E1E4E8">&#x3C;</span><span style="color:#85E89D">svg</span><span style="color:#B392F0"> fill</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"none"</span><span style="color:#B392F0"> viewBox</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"0 0 300 120"</span><span style="color:#B392F0"> width</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"300"</span><span style="color:#B392F0"> height</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"120"</span><span style="color:#B392F0"> xmlns</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"http://www.w3.org/2000/svg"</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#E1E4E8">	&#x3C;</span><span style="color:#85E89D">foreignObject</span><span style="color:#B392F0"> width</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"100%"</span><span style="color:#B392F0"> height</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"100%"</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#E1E4E8">		&#x3C;</span><span style="color:#FDAEB7;font-style:italic">div</span><span style="color:#B392F0"> xmlns</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"http://www.w3.org/1999/xhtml"</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#E1E4E8">			&#x3C;</span><span style="color:#85E89D">style</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#E1E4E8">				.tags {</span></span>
<span class="line"><span style="color:#E1E4E8">					display: flex;</span></span>
<span class="line"><span style="color:#E1E4E8">					flex-wrap: wrap;</span></span>
<span class="line"><span style="color:#E1E4E8">					height: 100%;</span></span>
<span class="line"><span style="color:#E1E4E8">					width: 100%;</span></span>
<span class="line"><span style="color:#E1E4E8">				}</span></span>
<span class="line"><span style="color:#E1E4E8">				.tag {</span></span>
<span class="line"><span style="color:#E1E4E8">					background-color: #e3ffff;</span></span>
<span class="line"><span style="color:#E1E4E8">					border-radius: 0.25em;</span></span>
<span class="line"><span style="color:#E1E4E8">					color: #0ca4a5;</span></span>
<span class="line"><span style="color:#E1E4E8">					border: 1px solid #0ca4a5;</span></span>
<span class="line"><span style="color:#E1E4E8">					display: inline-block;</span></span>
<span class="line"><span style="color:#E1E4E8">					font-size: 0.75em;</span></span>
<span class="line"><span style="color:#E1E4E8">					line-height: 2em;</span></span>
<span class="line"><span style="color:#E1E4E8">					margin: 0.125em;</span></span>
<span class="line"><span style="color:#E1E4E8">					padding: 0 0.5em;</span></span>
<span class="line"><span style="color:#E1E4E8">					text-decoration: none;</span></span>
<span class="line"><span style="color:#E1E4E8">					font-family: sans-serif;</span></span>
<span class="line"><span style="color:#E1E4E8">				}</span></span>
<span class="line"><span style="color:#E1E4E8">			&#x3C;/</span><span style="color:#85E89D">style</span><span style="color:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#E1E4E8">			&#x3C;</span><span style="color:#FDAEB7;font-style:italic">div</span><span style="color:#B392F0"> class</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"tags"</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#E1E4E8">				&#x3C;</span><span style="color:#FDAEB7;font-style:italic">div</span><span style="color:#B392F0"> class</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"tag"</span><span style="color:#E1E4E8">>Angular&#x3C;/</span><span style="color:#FDAEB7;font-style:italic">div</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#E1E4E8">				&#x3C;</span><span style="color:#FDAEB7;font-style:italic">div</span><span style="color:#B392F0"> class</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"tag"</span><span style="color:#E1E4E8">>Vue(X)&#x3C;/</span><span style="color:#FDAEB7;font-style:italic">div</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#E1E4E8">				&#x3C;</span><span style="color:#FDAEB7;font-style:italic">div</span><span style="color:#B392F0"> class</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"tag"</span><span style="color:#E1E4E8">>JavaScript&#x3C;/</span><span style="color:#FDAEB7;font-style:italic">div</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#E1E4E8">				&#x3C;</span><span style="color:#FDAEB7;font-style:italic">div</span><span style="color:#B392F0"> class</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"tag"</span><span style="color:#E1E4E8">>TypeScript&#x3C;/</span><span style="color:#FDAEB7;font-style:italic">div</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#E1E4E8">			&#x3C;/</span><span style="color:#FDAEB7;font-style:italic">div</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#E1E4E8">			&#x3C;</span><span style="color:#FDAEB7;font-style:italic">div</span><span style="color:#B392F0"> class</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"tags"</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#E1E4E8">				&#x3C;</span><span style="color:#FDAEB7;font-style:italic">div</span><span style="color:#B392F0"> class</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"tag"</span><span style="color:#E1E4E8">>(S)CSS&#x3C;/</span><span style="color:#FDAEB7;font-style:italic">div</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#E1E4E8">				&#x3C;</span><span style="color:#FDAEB7;font-style:italic">div</span><span style="color:#B392F0"> class</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"tag"</span><span style="color:#E1E4E8">>Building UIs&#x3C;/</span><span style="color:#FDAEB7;font-style:italic">div</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#E1E4E8">				&#x3C;</span><span style="color:#FDAEB7;font-style:italic">div</span><span style="color:#B392F0"> class</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"tag"</span><span style="color:#E1E4E8">>Web Components&#x3C;/</span><span style="color:#FDAEB7;font-style:italic">div</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#E1E4E8">			&#x3C;/</span><span style="color:#FDAEB7;font-style:italic">div</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#E1E4E8">			&#x3C;</span><span style="color:#FDAEB7;font-style:italic">div</span><span style="color:#B392F0"> class</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"tags"</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#E1E4E8">				&#x3C;</span><span style="color:#FDAEB7;font-style:italic">div</span><span style="color:#B392F0"> class</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"tag"</span><span style="color:#E1E4E8">>Ionic&#x3C;/</span><span style="color:#FDAEB7;font-style:italic">div</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#E1E4E8">				&#x3C;</span><span style="color:#FDAEB7;font-style:italic">div</span><span style="color:#B392F0"> class</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"tag"</span><span style="color:#E1E4E8">>Electron&#x3C;/</span><span style="color:#FDAEB7;font-style:italic">div</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#E1E4E8">				&#x3C;</span><span style="color:#FDAEB7;font-style:italic">div</span><span style="color:#B392F0"> class</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"tag"</span><span style="color:#E1E4E8">>.NET&#x3C;/</span><span style="color:#FDAEB7;font-style:italic">div</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#E1E4E8">			&#x3C;/</span><span style="color:#FDAEB7;font-style:italic">div</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#E1E4E8">		&#x3C;/</span><span style="color:#FDAEB7;font-style:italic">div</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#E1E4E8">	&#x3C;/</span><span style="color:#85E89D">foreignObject</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#E1E4E8">&#x3C;/</span><span style="color:#85E89D">svg</span><span style="color:#E1E4E8">></span></span></code></pre>
<p>From there on, the possibilities are endless. On my profile, I added my personal logo SVG and the same drawing animation used on my own website.</p>
<p><img src="https://fantinel.dev/cms/media/articles/github-profile-readme/my-profile.gif" alt="Animation on my GitHub Profile" title="Using CSS animations inside the SVG, I was able to mimic the animation on my website."></p>
<p>Feel free to check out <a href="https://github.com/matfantinel/matfantinel">the source code</a> to find out how it works.</p>
<h2 id="inspiration">Inspiration</h2>
<p>Some people have compiled <a href="https://github.com/abhisheknaiidu/awesome-github-profile-readme">a list of amazing examples and inspirations for your profile</a>. Some are minimalistic, others more complex, and some even get data from APIs (!). My favorite is <a href="https://github.com/BrunnerLivio">this one from Livio Brunner</a>, which definitely brings all the best things from 2000’s web.</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/cms/media/articles/github-profile-readme/cover.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/cms/media/articles/github-profile-readme/cover.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/2020-year-in-review</guid>
      <title>Looking Back at 2020</title>
      <description>2020 definitely wasn&apos;t our best year... but let&apos;s remember what was good about it.</description>
      <link>https://fantinel.dev/blog/2020-year-in-review</link>
      <pubDate>Mon, 28 Dec 2020 00:00:00 +0000</pubDate>
      <category>Retrospective</category><category>Dev</category><category>Meta</category><category>Personal</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/2020-year-in-review">
              read on the site!
            </a>
          </strong>
        </div>

        <p>We all know this year hasn’t been easy. That doesn’t mean it had to be wasted. Regardless of how bad or good it has been for oneself individually, there are always lots of lessons we can learn from what we’ve been through.</p>
<h2 id="more-than-one-pandemic">More Than One Pandemic</h2>
<p>I think one of the most obvious takeaways from this year is that we are not living just one pandemic (COVID-19), but also one that’s equally worrisome and potentially even deadlier: misinformation. The sheer amount of false information and hatred being spread through both social media and real life is becoming bigger every passing day.</p>
<p>They can be shared by ignorance, personal or political interest, or simply to “watch the world burn”. It is painful to watch the world go backwards like this; especially when people close to you are affected as well.</p>
<h2 id="mental-health">Mental Health</h2>
<p>I am sure that regardless of any individual impact the pandemics have had, your mental health must have been impacted by all of this somehow. “Doomscrolling” became a thing, fueled by our time watching and reading terrible news while quarantining in the safety of our homes.</p>
<p>Initially, I was guilty of that too. Checking many times a day for news, how the pandemic was growing, getting angry at how our leaders were dealing with it, and more. Fortunately, I ended up realizing how harmful that was to my mental health, and that there was no good coming out of it. ==A few minutes a day is enough to catch up on important news, the rest of the day can be spent on myself.== So instead of mindlessly scrolling through the doom and gloom on the internet, I started to work on more personal projects, learning new stuff, and playing games.</p>
<h2 id="learning-with-personal-projects">Learning with Personal Projects</h2>
<p>This year I worked on a few personal projects, and tried different approaches with them so I could learn new things. I didn’t necessarily get really in-depth into most of the things I learned, but the knowledge I got definitely made me a better developer.</p>
<p><img src="https://fantinel.dev/cms/media/articles/2020-year-in-review/pocket-companion.jpg" alt="Two screenshots of the Pocket Companion app, side by side. One displays all menu options, and the other shows a page for a great white shark."></p>
<ul>
<li><a href="https://fantinel.dev/ionic-animal-crossing-companion/">I built an Animal Crossing companion app</a>, with great utilities for when playing the game. I focused a lot on CSS animations for this one, while trying to mimic the official Nintendo app in looks. Learned a lot of things while building it:
<ul>
<li>State management in Angular projects, with <a href="https://ngrx.io/">NgRx</a>;</li>
<li>Offline functionality with IndexedDB in the browser;</li>
<li>Improved CSS Animations;</li>
<li>Improved Ionic and PWA knowledge;</li>
<li>Improved performance knowledge, with Web Workers, Virtual Scrolling and Lazy Loading;</li>
</ul>
</li>
<li>I redesigned part of my website - I decided to change things up a bit, with an animated (but still lightweight) header background, a new logo, typography improvements, code highlighting, and more. I think it looks more professional than before, and represents my way of building things better as well;</li>
<li>I built <a href="https://github.com/matfantinel/resume">my Resume as a Web Component</a> - using StencilJS to build it, and CSS Grid for the layout. Both of those things were new to me, so it was pretty interesting;</li>
<li>I learned about <a href="https://fantinel.dev/css-scroll-snapping/">CSS Scroll Snapping</a> - a fantastic new CSS capability that helps with reducing the amount of JS in our websites;</li>
<li>I focused a lot on improving my design skills this year. While I’m still a developer (and plan on continuing to be), designing things is something I find very fun. I learned messing with a few design-related things this year:
<ul>
<li>Edit SVG files in Inkscape - still a lot to learn, but I’m already capable of doing some edits to existing files;</li>
<li>Build high-fidelity mockups in Figma - being able to design more quickly than I was by building it was great for speeding up the process, and being able to collect feedback earlier was fantastic as well;</li>
</ul>
</li>
<li>I started to contribute a bit more to Open Source projects, like <a href="https://elementary.io">elementary OS</a>. Mainly with a still-in-progress website rebuild, but also with tons of feedback, issue reports, offering support and participating on discussions.</li>
</ul>
<h2 id="fun">Fun</h2>
<p>Since I committed myself to stop seeing bad things and focus on good ones, I resorted to at-home entertainment a lot this year. My highlights:</p>
<h3 id="tv-and-movies">TV and Movies</h3>
<ul>
<li>
<p><strong>Mr. Robot</strong> was a jaw-dropping series. I started watching it just this year, and binged to the finale quickly. It was an incredibly powerful series, with genius writing and cinematography. Highly recommend it to anyone, even if not a fan of the tech/hacking theme;</p>
</li>
<li>
<p><strong>Community</strong> was a great find; It’s not a series I had ever heard of when it was still airing, as it never got popular here in Brazil. However, after hearing about it on the web and finding it on Netflix, I’m so glad I started watching it. Loads of fun, full of references, internal jokes and Dan Harmon genius;</p>
</li>
</ul>
<h3 id="games">Games</h3>
<ul>
<li>
<p><strong>Animal Crossing New Horizons</strong> was one of the best things to happen this year. It’s far from a perfect game, but it could not have been better for the current situation, providing an easy escape into a world free of our current burdens. It came out at the beginning of quarantine and still provides daily wholesomeness to this day;</p>
</li>
<li>
<p><strong>Dungeons &#x26; Dragons</strong> was a surprising source of fun considering my party only played online this year. But even with social distancing, we managed to have a lot of fun. With <a href="https://foundryvtt.com/">FoundryVTT</a>, things are much easier to handle and it’s not much harder to get immersed. I’m even trying to be a DM of my own campaign… I’m not sure it’s going as well as I hoped, but hey, at least I’m trying.</p>
</li>
<li>
<p><strong>Hollow Knight</strong>… I’m so glad I played it. Being a Metroidvania, a genre I’m not really a fan of, it completely hooked me with its worldbuilding, art style, atmosphere and frustratingly satisfying difficulty. A 10/10 for me, for sure;</p>
</li>
</ul>
<p><img src="https://fantinel.dev/cms/media/articles/2020-year-in-review/hollow-knight.jpg" alt="Official artwork of Hollow Knight, showing the main character, a humanoid shadow wearing a horned helmet and holding a small sword, standing in a dark cave." title="Hollow Knight official artwork, by Team Cherry"></p>
<h2 id="writing">Writing</h2>
<p>Well, since this is only the third blog post this year, I definitely did not do much writing. I haven’t felt inspired to do so, as I was often busy building nice things or winding down from all the mess that was going on. I’ll repeat my intention of trying to write more next year, of course, since it shouldn’t be hard to do at least more than in 2020.</p>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>If you’re still reading, thanks! You’re probably my mom or fiancée. Anyway, 2020 was a very exciting year, even if not in a good way. Let’s hope 2021 is a more boring one. See you then!</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/cms/media/articles/2020-year-in-review/cover.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/cms/media/articles/2020-year-in-review/cover.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/css-scroll-snapping</guid>
      <title>CSS Scroll Snapping - Improve Scrolling without JS</title>
      <description>Learn how to snap scroll positions with CSS only.</description>
      <link>https://fantinel.dev/blog/css-scroll-snapping</link>
      <pubDate>Mon, 03 Aug 2020 00:00:00 +0000</pubDate>
      <category>Front-End</category><category>Dev</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/css-scroll-snapping">
              read on the site!
            </a>
          </strong>
        </div>

        <p>CSS is evolving constantly and the past few years have brought us amazing stuff. It is getting easier to make smooth and complete experiences without having to use JavaScript at all. Case in point: Scroll Snapping.</p>
<p>It is an easy way to guarantee that users will scroll to the correct portion of your page or of a container. Picture a landing page that is easily swipable/scrollable between sections, or swipable cards. Take in mind how Android’s Recent Apps screen works, for example:</p>
<p><img src="https://fantinel.dev/cms/media/articles/css-scroll-snapping/android-scroll-snapping.gif" alt="Android&#x27;s Recent Apps screen has scroll mapping between app cards"></p>
<p>You can see that Android never allows the end of a scroll to be in a place between cards. There’s a certain threshold that determines that the scroll will snap into the nearest card and put it in the center. All that while still keeping scrolling momentum, allowing you to go from one edge of the list to another in a single scroll if you want to.</p>
<p>Until recently, you’d have to resort to JavaScript code to make a similar behavior on the web. But now, all you need is CSS, which makes things more performant, consistent and reliable! Browser support is already pretty good, with all major browser engines already supporting it fully. Check out browser support on <a href="https://caniuse.com/#feat=css-snappoints">caniuse.com</a>.</p>
<p>I’ve recently added scroll snapping to my own website. You can check it out on the “Work Experience” section in the <a href="https://fantinel.dev">Home page</a>. It’s specially great on mobile!</p>
<h2 id="how">How?</h2>
<p>There are two main CSS properties that make the magic happen, one for the parent element, and one for its children.</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="css"><code><span class="line"><span style="color:#B392F0">.parent</span><span style="color:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#79B8FF">	scroll-snap-type</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">y</span><span style="color:#79B8FF"> mandatory</span><span style="color:#E1E4E8">;</span></span>
<span class="line"><span style="color:#E1E4E8">}</span></span>
<span class="line"><span style="color:#B392F0">.child</span><span style="color:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#79B8FF">	scroll-snap-align</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">start</span><span style="color:#E1E4E8">;</span></span>
<span class="line"><span style="color:#E1E4E8">}</span></span></code></pre>
<h3 id="scroll-snap-type">scroll-snap-type</h3>
<p>This property tells the browser that the parent element uses scroll snapping. We are given some options on how snapping must work. <code>y</code> indicates that the scroll happens vertically, while <code>x</code> means horizontally. We can also pass the <code>mandatory</code> and <code>proximity</code> options.</p>
<p>We use <code>mandatory</code> to tell that the browser <em>must</em> snap to a snap point when the user stops scrolling. This means that if the next snap point becomes visible on the screen and scrolling stops, the browser will automatically snap to the next one. In the other end, with <code>proximity</code>, things are less strict. The browser will only snap to the next snap point if scrolling gets past a certain threshold. Both values are useful, their use will depend on the situation you’re applying it to.</p>
<iframe height="500" style="width: 100%;" scrolling="no" title="scroll-snap-type: Mandatory vs Proximity" src="https://codepen.io/matfantinel/embed/preview/LYNPdpE?default-tab=result&#x26;theme-id=dark" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true">
  See the Pen &#x3C;a href="https://codepen.io/matfantinel/pen/LYNPdpE">
  scroll-snap-type: Mandatory vs Proximity&#x3C;/a> by Matheus Fantinel (&#x3C;a href="https://codepen.io/matfantinel">@matfantinel&#x3C;/a>)
  on &#x3C;a href="https://codepen.io">CodePen&#x3C;/a>.
</iframe>
<h3 id="scroll-snap-align">scroll-snap-align</h3>
<p>This is a property you add to the children that specifies where the snap points will be in the element. Which means that, whenever the browser automatically snaps the scroll to the element, it will either go to the left/top edge (<code>start</code>), center (<code>center</code>), or right/bottom edge (<code>end</code>) of the element. This property pretty much only makes a difference if the children are bigger than the parent’s display size.</p>
<h3 id="scroll-padding-and-scroll-margin">scroll-padding and scroll-margin</h3>
<p>To make things even better, we can use the <code>scroll-padding</code> (for parent) and <code>scroll-margin</code> (for children) properties. They add some space before/after the elements that are only considered when scrolling. The best way to implement them is by trying it out after you build your scroll snapping goodiness.</p>
<p>You can check the documentation over on <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Scroll_Snap">Mozilla Developer Network</a> for more info.</p>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>Scroll snapping is a sign that the web is maturing. For years we’ve been bloating websites with a lot of JavaScript for very simple visual tasks, and now CSS is slowly evolving to take some space back. It’s an important evolution because Web technologies are being used to build every kind of application, and its native feature set needs to be good enough to compete with native or native-ish counterparts like Flutter, Swift, or Kotlin.</p>
<p>Thanks for reading!</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/cms/media/articles/css-scroll-snapping/cover.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/cms/media/articles/css-scroll-snapping/cover.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/ionic-animal-crossing-companion</guid>
      <title>Developing an Animal Crossing companion app with Ionic</title>
      <description>No bells were spent while building this app.</description>
      <link>https://fantinel.dev/blog/ionic-animal-crossing-companion</link>
      <pubDate>Tue, 28 Jul 2020 01:19:02 +0000</pubDate>
      <category>Dev</category><category>Front-End</category><category>Angular</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/ionic-animal-crossing-companion">
              read on the site!
            </a>
          </strong>
        </div>

        <p>This year I’ve played a lot of Animal Crossing New Horizons. It came out at the start of quarantine - and was extremely popular for providing an escape from that, with a virtual life that’s free of the problems we’re facing on the real one.</p>
<p>In the meantime, I was re-discovering <a href="https://ionicframework.com/">Ionic</a> and wanted to work on an app that would help me improve my knowledge of it, and as a bonus learn about other stuff I’ve been interested in. So, I decided to work on a companion app for the game I’ve been playing so much.</p>
<p>This post illustrates my experience developing it. It is not a tutorial or an in-depth guide. It’s just meant to describe my experience, what I learned and any bumps in the way. In any case, the <a href="https://github.com/matfantinel/acnh-pocket-companion">source code is available on GitHub</a> for all to see and modify. If it ends up helping anyone else in the way, even better!</p>
<p><a href="https://pocketcompanion.fantinel.dev">You can check out the finalized app here</a>.</p>
<h2 id="creating-the-app">Creating the app</h2>
<p>Since I’m testing Ionic (version 5 as of writing), I tried to check out as much of the experience they provide as I could. Even though you can create an app via their CLI, they also provide <a href="https://ionicframework.com/start">an online App Wizard</a> to do that visually. It’s a pretty cool way to display the starting templates, easily setting theme colors, default icon and the JS Framework you’ll use. I chose Angular for this one.</p>
<p><img src="https://fantinel.dev/cms/media/articles/ionic-animal-crossing-companion/wizard.jpg" alt="Screenshot of Ionic&#x27;s app creation wizard. It allows you to pick a name, an icon, a template, and a framework to start your app."></p>
<p>After setting it up, the wizard gives you a CLI command with an ID to run on your machine. Therefore it still uses the CLI in the end, but it’s a nice touch for the starting experience.</p>
<p><img src="https://fantinel.dev/cms/media/articles/ionic-animal-crossing-companion/all-set.jpg" alt="The wizard gives you a CLI command with an ID for you to run on your machine"></p>
<p>After running that command, the CLI prompted me if I wanted to integrate with <a href="https://capacitorjs.com/">Capacitor</a>, which is a tool that makes the app deployable to Android/iOS. I accepted, since I plan to release it on the Play Store at some point. The main goal is to release the app as a <a href="https://fantinel.dev/what-are-pwas-and-why-should-i-care-about-them/">PWA</a>, but an Android version wouldn’t hurt, since the publishing process is almost-free and there’s almost zero extra effort involved. Publishing it for iPhones is not a goal due to the cost and effort required to publish on the App Store.</p>
<p>After installation, I could run the app with <code>ionic serve</code>, which resulted in a template app with the settings I had set on the App Wizard.</p>
<p><img src="https://fantinel.dev/cms/media/articles/ionic-animal-crossing-companion/initial-template.jpg" alt="Screenshot of a browser with browser tools open, and a mobile website with Ionic&#x27;s default template."></p>
<p>The default folder structure is pretty straightforward and should be familiar to anyone who has ever worked with Angular before.</p>
<p><img src="https://fantinel.dev/cms/media/articles/ionic-animal-crossing-companion/default-folder-structure.jpg" alt="Default folder structure on an Ionic Angular app"></p>
<h2 id="starting-development">Starting development</h2>
<p>Since I am a visual person, I decided to start work by changing the looks of the app first. This helps me stay motivated since I can see progress faster (because everything I do results in a visual change), and it also allows me to visualize the final product earlier, and gives me more time to think of good features or UX improvements.</p>
<p>Even though I generated my project using the Tabs template, I decided that I don’t want tabs in my app. So I had to do a few changes to the routing configuration.</p>
<p>First, I created a new page that I can use as the initial screen for my app. Ionic CLI makes it easy to create pages and components. Just running <code>ionic generate page home</code> created a page called Home, and automatically adds its modules to the Angular config, and its route to the Angular Router.</p>
<p>After that, to remove the tabs, all I had to do was set the default route (<code>''</code>) to redirect to home, and then I could delete all the Tab pages.</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#F97583">~</span><span style="color:#E1E4E8">filename app</span><span style="color:#F97583">-</span><span style="color:#E1E4E8">routing.module.ts</span></span>
<span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { NgModule } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> '@angular/core'</span><span style="color:#E1E4E8">;</span></span>
<span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { PreloadAllModules, RouterModule, Routes } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> '@angular/router'</span><span style="color:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> routes</span><span style="color:#F97583">:</span><span style="color:#B392F0"> Routes</span><span style="color:#F97583"> =</span><span style="color:#E1E4E8"> [</span></span>
<span class="line"><span style="color:#E1E4E8">	{</span></span>
<span class="line"><span style="color:#E1E4E8">		path: </span><span style="color:#9ECBFF">''</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#E1E4E8">		redirectTo: </span><span style="color:#9ECBFF">'home'</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#E1E4E8">		pathMatch: </span><span style="color:#9ECBFF">'full'</span></span>
<span class="line"><span style="color:#E1E4E8">	},</span></span>
<span class="line"><span style="color:#E1E4E8">	{</span></span>
<span class="line"><span style="color:#E1E4E8">		path: </span><span style="color:#9ECBFF">'home'</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#B392F0">		loadChildren</span><span style="color:#E1E4E8">: () </span><span style="color:#F97583">=></span><span style="color:#F97583"> import</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">'./home/home.module'</span><span style="color:#E1E4E8">).</span><span style="color:#B392F0">then</span><span style="color:#E1E4E8">((</span><span style="color:#FFAB70">m</span><span style="color:#E1E4E8">) </span><span style="color:#F97583">=></span><span style="color:#E1E4E8"> m.HomePageModule)</span></span>
<span class="line"><span style="color:#E1E4E8">	}</span></span>
<span class="line"><span style="color:#E1E4E8">];</span></span>
<span class="line"><span style="color:#E1E4E8">@</span><span style="color:#B392F0">NgModule</span><span style="color:#E1E4E8">({</span></span>
<span class="line"><span style="color:#E1E4E8">	imports: [RouterModule.</span><span style="color:#B392F0">forRoot</span><span style="color:#E1E4E8">(routes, { preloadingStrategy: PreloadAllModules })],</span></span>
<span class="line"><span style="color:#E1E4E8">	exports: [RouterModule]</span></span>
<span class="line"><span style="color:#E1E4E8">})</span></span>
<span class="line"><span style="color:#F97583">export</span><span style="color:#F97583"> class</span><span style="color:#B392F0"> AppRoutingModule</span><span style="color:#E1E4E8"> {}</span></span></code></pre>
<h2 id="theming-and-layout">Theming and Layout</h2>
<p>For me, this is the best part. Ionic makes it SO easy to change themes! There’s a <code>variables.scss</code> file on the <code>theme</code> folder, where you can find Ionic’s default color palette and change anything. But they provide <a href="https://ionicframework.com/docs/theming/color-generator">an amazing Color Generator</a> that can make all the palette calculations for you. Just put your primary/secondary/success/danger/etc colors in there, and it will output the code for you to paste on your app.</p>
<p><img src="https://fantinel.dev/cms/media/articles/ionic-animal-crossing-companion/color-generator.jpg" alt="Screenshot of Ionic&#x27;s color generator. When you set primary, secondary, and more colors by giving a HEX value, and Ionic automatically generates shade variants and gives you a live preview."></p>
<p>After some time styling, I modified the layout of the two page skeletons I wanna have, the Home page (that mimics the Nookphone in the game), and I styled the header of the other pages.</p>
<p><img src="https://fantinel.dev/cms/media/articles/ionic-animal-crossing-companion/skeleton.gif" alt="Gif showing the home screen of my app. The home screen has 4 buttons: Passport, Island, To-Do and Daily Chores. Clicking on them opens a page."></p>
<p>Since I wanted to have the same header on all pages, I ended up creating a component for it. Since I had already two components and six pages, I decided to split them up in different folders (I honestly don’t know why Ionic doesn’t do that by default). My folder structure ended up like this:</p>
<p><img src="https://fantinel.dev/cms/media/articles/ionic-animal-crossing-companion/modified-folder-structure.jpg" alt="The folder structure after my modifications"></p>
<p>Also, to be able to import the same component into multiple Pages (each page with its own module), I had to create the ComponentsModule. This required a bit of searching because I wasn’t familiar with this approach yet. ComponentsModule by itself is very simple, I just import the Components there:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#F97583">~</span><span style="color:#E1E4E8">filename components.module.ts</span></span>
<span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { NgModule } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> '@angular/core'</span><span style="color:#E1E4E8">;</span></span>
<span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { CoolHeaderComponent } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> './cool-header/cool-header.component'</span><span style="color:#E1E4E8">;</span></span>
<span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { IonicModule } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> '@ionic/angular'</span><span style="color:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#E1E4E8">@</span><span style="color:#B392F0">NgModule</span><span style="color:#E1E4E8">({</span></span>
<span class="line"><span style="color:#E1E4E8">	declarations: [CoolHeaderComponent],</span></span>
<span class="line"><span style="color:#E1E4E8">	imports: [IonicModule.</span><span style="color:#B392F0">forRoot</span><span style="color:#E1E4E8">()],</span></span>
<span class="line"><span style="color:#E1E4E8">	exports: [CoolHeaderComponent]</span></span>
<span class="line"><span style="color:#E1E4E8">})</span></span>
<span class="line"><span style="color:#F97583">export</span><span style="color:#F97583"> class</span><span style="color:#B392F0"> ComponentsModule</span><span style="color:#E1E4E8"> {}</span></span></code></pre>
<p>The annoying part was having to add ComponentsModule to each of the pages. There probably is an easier way of handling this, but right now I decided to not dig too deep into it.</p>
<p>After making the skeleton of the app, I started working on some specific pages. I started with the home screen, that mimics a cell phone’s app grid. I’ll probably add more options as the app evolves, but for now this is good enough to build the layout with.</p>
<p>The next screen to be done was the <em>Island</em> one. It is a simple form with some text inputs, a datepicker and a couple of dropdowns. I used Ionic’s default components for them and styled them with CSS so they fit the app’s style better. Everything was pretty simple to do and worked great. Ionic’s docs made it super easy to find the available options and easier ways to theme.</p>
<p>I wanted to add another thing to this screen too, though: a section containing the island’s villagers, and a search box that allows the user to select them. Just adding another “card” to the UI and making it scroll would deliver what I want, but it wouldn’t look <em>awesome</em>. GitHub’s recently released mobile app has this bottom drawer that I personally found awesome:</p>
<p><img src="https://fantinel.dev/cms/media/articles/ionic-animal-crossing-companion/github-drawer.gif" alt="GitHub&#x27;s cool bottom drawer"></p>
<p>Luckily for me, someone’s already worked on something like that for Ionic, which means I don’t have to do it from scratch! <a href="https://github.com/AnthonyCifuentes/ion-slide-drawer">Click here</a> to check it on GitHub. Unfortunately it is not (at the time of writing) available as an installable component on NPM or somewhere else. But the code is simple and I just copied it over to my own project.</p>
<h2 id="state-management">State Management</h2>
<p>Since the entire point of this app is learning, I decided to use <a href="https://ngrx.io">NgRx</a>. It is a State Management library for Angular that works in a similar way to VueX or Redux. I’ve worked with VueX recently and really like the concept, so I decided to make use of it here.</p>
<p>I started by adding it to my application with <code>npm i @ngrx/store @ngrx/effects @ngrx/store-devtools @ngrx/schematics --save</code>. If you wanna learn more about NgRx, I used <a href="https://indepth.dev/how-to-start-flying-with-angular-and-ngrx/">this amazing tutorial</a> to learn it for this app.</p>
<h2 id="database">Database</h2>
<p>The best way to store data in an app like mine - supposed to work on Android, iOS and the Web, but still entirely offline - is using the browser’s IndexedDB. It’s basically a simpler SQL database for your web app that lives inside the browser. <a href="https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API">You can read more on MDN if you’d like</a>.</p>
<p>While useful, IndexedDB has a very low-level API, which makes it hard to get started with. Which is why I chose to use <a href="https://dexie.org/">Dexie.js</a> as a wrapper for IndexedDB. It allows me to use a simple API to query data, while already doing the needed optimizations.</p>
<p>Simply installing Dexie.js and a quick read on its documentation allowed me to successfully store and read persistent data for my app.</p>
<h2 id="putting-some-other-cool-things-in-practice">Putting some other cool things in practice</h2>
<p>Since the main purpose of developing this app is learning, I decided to use some cool stuff I’ve learned and put them in practice:</p>
<ul>
<li><a href="https://fantinel.dev/web-workers/">Web Workers</a>: on first run, the app uses a web worker to parse the game data (contained in large JSON files) in the background, so it can fill the database with the data used on Critterpedia</li>
<li>Virtual Scroll: on the Critterpedia listings, I used Virtual Scroll instead of pagination. I figured this was a better choice because while it can have a lot of items, it’s a fixed number so no need to paginate. This way it maintains good performance on scrolling even on lower-end devices. Luckily, <a href="https://ionicframework.com/docs/api/virtual-scroll">Ionic provides a built-in component for that</a>!</li>
<li>Lazy loading images: since all items in the list have an icon that’s displayed besides them, there are lots of images to download. Without lazy-loading, ALL images would be loaded right off the bat, even the ones way down the bottom of the list. With lazy loading, the browser automatically downloads only images as they would appear on screen by just setting the <code>loading</code> attribute: <code>&#x3C;img loading="lazy"></code></li>
</ul>
<h2 id="running-on-android">Running on Android</h2>
<p>After having a few parts of my app done, I decided it was time to run in on my Android phone, to have a better look of how it will look and feel on an actual device.</p>
<p>The first step for this is installing Android Studio. Install instructions may vary depending on your operating system. I’m on a Linux-based OS so I installed it from <a href="https://snapcraft.io/android-studio">Snapcraft</a>. You can follow the instructions from the <a href="https://ionicframework.com/docs/developing/android">Ionic docs</a> if you’d prefer.</p>
<p>After installing Android Studio, we need to add an environment variable that points to the Android SDK. In the following command (Linux, Mac), replace the directory with the one where the Sdk is installed in your PC, then add it to the end of your <code>.bashrc</code> file:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="shell"><code><span class="line"><span style="color:#F97583">export</span><span style="color:#E1E4E8"> ANDROID_SDK_ROOT</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">$HOME/Library/Android/sdk</span></span></code></pre>
<p>Adding these next lines to the <code>.bashrc</code> file will also give you a quick way to access these Android tools if needed:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="shell"><code><span class="line"><span style="color:#6A737D"># avdmanager, sdkmanager</span></span>
<span class="line"><span style="color:#F97583">export</span><span style="color:#E1E4E8"> PATH</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">$PATH:$ANDROID_SDK_ROOT/tools/bin</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D"># adb, logcat</span></span>
<span class="line"><span style="color:#F97583">export</span><span style="color:#E1E4E8"> PATH</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">$PATH:$ANDROID_SDK_ROOT/platform-tools</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D"># emulator</span></span>
<span class="line"><span style="color:#F97583">export</span><span style="color:#E1E4E8"> PATH</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">$PATH:$ANDROID_SDK_ROOT/emulator</span></span></code></pre>
<p>Now, we have to register Android as a platform for our app. Since we’re using Capacitor to make this bridge, we need only a single command:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="shell"><code><span class="line"><span style="color:#B392F0">ionic</span><span style="color:#9ECBFF"> capacitor</span><span style="color:#9ECBFF"> add</span><span style="color:#9ECBFF"> android</span></span></code></pre>
<p><em>If you get an error saying that “Capacitor could not find the web assets directory”, be sure to run <code>ionic build</code> to generate this directory first.</em></p>
<p>After that’s done, you can open the project in Android Studio (it will be on a folder called <code>Android</code> at the root of your project). If you still haven’t setup a device or emulator to run your app in, do it now. The <a href="https://ionicframework.com/docs/developing/android#android-studio">Ionic docs</a> show how to do both processes.</p>
<p>After a loooong while, Android Studio will have loaded all it needs to run your project. Just click on Run (Shift+F10). It should open on your phone/emulator!</p>
<h2 id="pwa-preparation">PWA preparation</h2>
<p>To be able to publish the app as a PWA, we need to add a Service Worker and a Manifest. Thankfully, Angular already has the CLI and the packages that allows us to do that easily. <code>ng add @angular/pwa</code> worked like a charm. This automatically added the needed files, and wired it up with the existing Angular modules automatically.</p>
<p>After that, I edited <code>ngsw-config.json</code> and <code>manifest.webmanifest</code> files with my app’s name and colors, and I replaced all generated icons with my own. For more details on how to create PWAs with Angular, <a href="https://fantinel.dev/angular-pwa-how-to/">check my specific post for that</a></p>
<h3 id="routing-issue">Routing issue</h3>
<p>One issue I was having after publishing a PWA like that is that when refreshing my app on the browser, it was throwing a 404 error. This happened because the route had changed and Angular lost track of what it was supposed to open. I fixed this by changing a line in my <code>app-routing.module.ts</code> and adding <code>useHash: true</code> to the settings:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#F97583">~</span><span style="color:#E1E4E8">filename app</span><span style="color:#F97583">-</span><span style="color:#E1E4E8">routing.module.ts</span></span>
<span class="line"><span style="color:#E1E4E8">@</span><span style="color:#B392F0">NgModule</span><span style="color:#E1E4E8">({</span></span>
<span class="line"><span style="color:#E1E4E8">  imports: [</span></span>
<span class="line"><span style="color:#E1E4E8">    RouterModule.</span><span style="color:#B392F0">forRoot</span><span style="color:#E1E4E8">(routes, { preloadingStrategy: PreloadAllModules, useHash: </span><span style="color:#79B8FF">true</span><span style="color:#E1E4E8"> })</span></span>
<span class="line"><span style="color:#E1E4E8">  ],</span></span>
<span class="line"><span style="color:#E1E4E8">  exports: [RouterModule]</span></span>
<span class="line"><span style="color:#E1E4E8">})</span></span></code></pre>
<h3 id="publishing">Publishing</h3>
<p>To host my PWA, I decided to use <a href="https://netlify.com">Netlify</a>. The publishing experience was quite easy and friction-free, and they also provide a free hosting plan which is perfect to host this demo-only-app. I will not document how the publishing proccess worked here because their UI is very intuitive and their docs would be way better than mine anyway.</p>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>Developing this definitely took me a while - but that’s because time’s been quite scarce for me. Most of the development time was spent learning NgRx, which was definitely the most complex part of this application. It also took me a while to find a good IndexedDB wrapper before I settled on Dexie. The rest was quite straightforward - the app logic is simple, and what took the longest was making the app pretty and the animations smooth.</p>
<p>I think the result is quite good - definitely not professional, but more than enough for a hobby app. I learned a lot working on it and would definitely do it again. In case you missed it, <a href="https://pocketcompanion.fantinel.dev">here’s another link to check out the result</a>, and its <a href="https://github.com/matfantinel/acnh-pocket-companion">source code</a>.</p>
<p>Thanks for reading!</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/cms/media/articles/ionic-animal-crossing-companion/cover.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/cms/media/articles/ionic-animal-crossing-companion/cover.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/2019-year-in-review</guid>
      <title>Cool things I did and learned in 2019</title>
      <description>A quick look back at 2019 and what I learned during this year.</description>
      <link>https://fantinel.dev/blog/2019-year-in-review</link>
      <pubDate>Sun, 29 Dec 2019 00:00:00 +0000</pubDate>
      <category>Retrospective</category><category>Dev</category><category>Meta</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/2019-year-in-review">
              read on the site!
            </a>
          </strong>
        </div>

        <p>Another year went by - Year 6 of my software development journey! I wanted to write this blog post to sum up stuff that I’ve done or learned this year. Keep in mind this is being written off the top of my head - I did not keep a journal throughout the year, but hopefully that’s something I’ll do in 2020.</p>
<h2 id="complexity-is-your-enemy">Complexity is your enemy</h2>
<p>As programmers, we are puzzle-solvers. We like to think outside the box, applying different concepts, putting all of those nice things we’ve learned everywhere. This… often backfires. Most of the time what works best are the simplest solutions, which are also easier to maintain in the future by you or others. I’ve seen this quote somewhere a while back: “Programmers spend the first years of their careers mastering complexity only to realize they should master simplicity instead”. I guess I’m not in my first years anymore?</p>
<p>I’ve changed my process in writing code to this:</p>
<ol>
<li>Understand the problem/goal;</li>
<li>Draw a simple mental picture of how to solve it (really simplify it);</li>
<li>Code in small steps and test them as you go;</li>
<li>Make it work. Code can look quite ugly in this step;</li>
<li>Optimize it, performance-wise;</li>
<li>⭐️ Try to explain how it works to yourself (mentally or by text), and then refactor the code to be as simple as possible;</li>
</ol>
<p>This last step might make the initial release take a little longer, but oh boy does it pay off whenever you or someone else has to maintain it.</p>
<h2 id="vanilla-js-is-the-best">Vanilla JS is the best</h2>
<p>Wait, I’m not saying you shouldn’t use any framework. If it helps you do your work better, go ahead! But “Vanilla” JS has come such a long way and there are so many amazing APIs coming out that using it solely is a valid option again. But more than that, those APIs are built in a way that popular frameworks can use them and communicate with each other with little effort. I’ve covered a bit of this on <a href="https://fantinel.dev/microfrontends/">my Microfrontends post</a>, but there are more APIs that I’ve discovered this year that are pretty awesome:</p>
<ul>
<li><a href="https://fantinel.dev/microfrontends/">Web Components/Shadow DOM/Custom Elements</a>;</li>
<li><a href="https://fantinel.dev/web-workers/">Web Workers</a>;</li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API">IntersectionObserver</a>;</li>
<li>Not exactly new, but <a href="https://fantinel.dev/what-are-pwas-and-why-should-i-care-about-them/">PWAs</a> are increasingly awesome;</li>
</ul>
<h2 id="jekyll">Jekyll</h2>
<p>I’m a bit late to the party, but this is still something I’ve found out about in 2019. This website/blog you’re seeing is built on top of Jekyll, a tool for making static websites.</p>
<p>Basically, you have HTML, CSS and JS. Add some special powers for using variables/loops inside that HTML code and ta-da! It works. Jekyll also allows you to create pages from Markdown (.md) files. I use it for blog posts like this one.</p>
<p>Since it’s a static website, it means the code that’s served to users/visitors is compiled just once and then served as HTML files. No servers or APIs necessary. It’s pretty cool. You can check more about Jekyll <a href="https://jekyllrb.com/">here</a> and can check the source code for my website <a href="https://github.com/matfantinel/fantinel.dev">here</a>.</p>
<h2 id="open-source-is-awesome">Open Source is awesome</h2>
<p>I have released two open-source apps this year for elementaryOS (<a href="https://github.com/matfantinel/reminduck">Reminduck</a> and <a href="https://github.com/matfantinel/moneta">Moneta</a>). I gotta say, besides the fact that I was learning an entirely different development stack (Vala, GTK, Desktop native), the best “new” thing I enjoyed was doing it in the open.</p>
<p>These are not complex, commercial apps or anything like that. They’re small utilities that I use on a daily basis and that were made open and released in hopes of helping anyone out there too. And I gotta say, having users like your software is great, but having them actively contribute to it is even better. As soon as they were released/made public, I started receiving pull requests, bug reports, feature requests, and translations from everywhere. <strong>It’s so amazing that people from all over the world took interest in my little idea and helped improve them any way they could.</strong></p>
<p>But open source is not just other people contributing to your projects, it’s also the opposite! I can improve other people’s apps that I use, I can report bugs openly and help out, or just download the source code, change a few things just to fit my workflow better and run it. And not only apps, I can do that with the entire OS I run on my PC! Not to mention having access to other people’s code allows you to learn from their work and saves you tons of time. Having a problem on your app and you know one that has solved that? Just check out what they did. Copy it, modify it, learn it, ship it. It’s all good. ==We wouldn’t have to spend so much time reinventing wheels if more of our code was open.==</p>
<p>Ah, this website is also open source. I used others as inspiration and took some code snippets too! So can you, feel free to fork it and modify anyway you want.</p>
<h2 id="working-remotely-is-fun-at-least-for-some-people">Working remotely is fun, at least for some people</h2>
<p>In August I started working remotely for the first time. I work from my home in Brazil with people from US, Europe and India. It was definitely refreshing to not have to commute and always be at a place that makes you feel good. Not only that, but the flexible hours allow me to work whenever I want and I can enjoy things that weren’t possible on a regular “9 to 5” routine.</p>
<p>Although… I end up working the same hours as I did before. It feels “right” to work at this time, and I can enjoy my family at night, since they also work during the day. But it’s nice to have the flexibility to have a break and a coffee somewhere else during the day if needed.</p>
<p>One of the main things people say about working from home is that sometimes it’s hard to separate work time from free time. Fortunately, I didn’t have that issue. My apartment is quite small, so I don’t have a proper office and instead work on a desk on my bedroom, which means the temptation of lying down and sleeping is always beside me. I’m so thankful I am able to resist that and get work done. And after work hours I can fully disconnect from it and not be stressed out, even if I stay on the computer. I totally understand why some people would not enjoy this and would be less productive, fortunately I was not the case.</p>
<p>The one thing I’d say is worse in my case is communication. I’m not an extremely social person - therefore I don’t miss chitchat during the day with colleagues or anything like that. It’s just that it’s harder to explain code and concepts over a call or screen share. Not everyone is always readily available to explain things because of different timezones, so you have to figure more things out on your own. It’s still doable though, and the days of “just coding” without interruptions are a dream. The pros outweight the cons.</p>
<h2 id="theres-never-enough-time">There’s never enough time</h2>
<p>This year, I started getting into writing more open source software and started working from home. The latter means I can work more on house chores since there’s no commute and I’m always around! Still, there’s never enough time for me to do what I want to do. I took time to write some elementaryOS apps, but I want to do some web apps too, and code some more, and play some games, watch series, movies, hang out with family, work, write on this blog, oof. This isn’t a new discovery by any means, but I feel like it’s getting worse every year. This is adult life, I guess.</p>
<h2 id="wrapping-up">Wrapping up</h2>
<p>Anyway, 2019 was a good year. I (of course) hope to do more and better in 2020, hopefully including more articles here. Hope you all have some nice holidays and a great new year!</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/cms/media/articles/2019-year-in-review/cover.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/cms/media/articles/2019-year-in-review/cover.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/web-workers</guid>
      <title>Web Workers - Bringing Multithreading to the Front-End</title>
      <description>Improve your client-side performance by breaking heavy operations into multiple threads - backend style.</description>
      <link>https://fantinel.dev/blog/web-workers</link>
      <pubDate>Wed, 14 Aug 2019 00:00:00 +0000</pubDate>
      <category>Front-End</category><category>Dev</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/web-workers">
              read on the site!
            </a>
          </strong>
        </div>

        <p>In times of complex, feature-filled front-ends, we often get reports of freezes, frame drops, or overall slugginess in our apps. This is due to JavaScript’s default behavior - a single thread is responsible for the interface, animations, and whatever CPU-intensive logic or I/O your app is doing.</p>
<p>This is because when JavaScript was created, back in 1995, it was never meant to be such a capable language, powering complex systems and heavy loads.</p>
<p>Usually, the best idea is to perform CPU-intensive work on the back-end, usually with a more performant language. This makes the experience similar to everyone, as it won’t require much power from the user’s machine. However, there are cases where we need to do this on the front-end, such as (but not limited to):</p>
<ul>
<li>Heavy filtering of data;</li>
<li>Conversions (like printing data to a PDF);</li>
<li>Deserializing huge and complex objects or arrays;</li>
<li>Complex calculations, like 3D objects for example.</li>
</ul>
<p>All of these heavy operations would cause the main thread - responsible for drawing your interface quickly and playing your animations smoothly - to not be that smooth.</p>
<p>This is where our star of the day comes in - Web Workers.</p>
<h2 id="what-do-these-web-workers-do">What do these Web Workers do?</h2>
<p>Imagine you are the boss (main thread). You call one of your employees (a worker) and tell them to do a specific task (a function). Then they go back to their own room (another thread) to do what you asked. In the meantime, you can do whatever you want, and as soon as your employee finishes the task, it comes back to you with the result. Now, in real life things aren’t that simple, but you get the main idea, right?</p>
<p>Let’s make something clear: Web Workers are an universal JavaScript feature, not limited to any specific framework. <a href="https://caniuse.com/#search=Worker">It even works on IE10+</a>!!! Pretty much the only requirement is that the worker logic stays in a different js file (worker.js, for example).</p>
<h2 id="lets-try-it-out">Let’s try it out</h2>
<p><a href="https://github.com/mdn/simple-web-worker">Mozilla Developer Network (MDN)</a> has a great, simple example of how to use them. I’ll explain it below:</p>
<p>First of all, on our main.js file, we need to create our Worker object, passing our Worker file as a parameter:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="javascript"><code><span class="line"><span style="color:#F97583">~</span><span style="color:#E1E4E8">filename main.js</span></span>
<span class="line"><span style="color:#F97583">if</span><span style="color:#E1E4E8"> (window.Worker) {</span></span>
<span class="line"><span style="color:#6A737D">	//just to make sure our browser supports it</span></span>
<span class="line"><span style="color:#F97583">	const</span><span style="color:#79B8FF"> myWorker</span><span style="color:#F97583"> =</span><span style="color:#F97583"> new</span><span style="color:#B392F0"> Worker</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">'worker.js'</span><span style="color:#E1E4E8">);</span></span>
<span class="line"><span style="color:#E1E4E8">}</span></span></code></pre>
<p>The main thread is able to communicate with workers using the <code>postMessage</code> and <code>onMessage</code> functions.</p>
<p>Let’s make our worker multiply two numbers for us!</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="javascript"><code><span class="line"><span style="color:#F97583">~</span><span style="color:#E1E4E8">filename main.js</span></span>
<span class="line"><span style="color:#6A737D">// we pass an array with all the parameters we want</span></span>
<span class="line"><span style="color:#E1E4E8">myWorker.</span><span style="color:#B392F0">postMessage</span><span style="color:#E1E4E8">([</span><span style="color:#79B8FF">123</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">456</span><span style="color:#E1E4E8">]);</span></span></code></pre>
<p>And then listen to a message sent back from our worker:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="javascript"><code><span class="line"><span style="color:#F97583">~</span><span style="color:#E1E4E8">filename main.js</span></span>
<span class="line"><span style="color:#E1E4E8">myWorker.</span><span style="color:#B392F0">onmessage</span><span style="color:#F97583"> =</span><span style="color:#F97583"> function</span><span style="color:#E1E4E8"> (</span><span style="color:#FFAB70">e</span><span style="color:#E1E4E8">) {</span></span>
<span class="line"><span style="color:#E1E4E8">	result.textContent </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> e.data;</span></span>
<span class="line"><span style="color:#E1E4E8">};</span></span></code></pre>
<p>We are now successfully passing and receiving data to/from our worker. Now, for the worker.js file:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="javascript"><code><span class="line"><span style="color:#F97583">~</span><span style="color:#E1E4E8">filename worker.js</span></span>
<span class="line"><span style="color:#B392F0">onmessage</span><span style="color:#F97583"> =</span><span style="color:#F97583"> function</span><span style="color:#E1E4E8"> (</span><span style="color:#FFAB70">e</span><span style="color:#E1E4E8">) {</span></span>
<span class="line"><span style="color:#F97583">	let</span><span style="color:#E1E4E8"> result </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> e.data[</span><span style="color:#79B8FF">0</span><span style="color:#E1E4E8">] </span><span style="color:#F97583">*</span><span style="color:#E1E4E8"> e.data[</span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">];</span></span>
<span class="line"><span style="color:#B392F0">	postMessage</span><span style="color:#E1E4E8">(result);</span></span>
<span class="line"><span style="color:#E1E4E8">};</span></span></code></pre>
<p>Very simple, right? The multiplication was done in a different thread, and therefore did not interfere with our main thread. Keep in mind though, that <strong>setting up a Worker, starting another thread and passing parameters still take up minor resources from the main thread</strong>. For simple examples like this, performance will likely be worse than before. <strong>Web Workers are made for heavy tasks, so don’t just use them everywhere</strong>.</p>
<h2 id="just-a-last-little-thing">Just a last little thing…</h2>
<p>When trying it out locally, you may find that your code… does not work. Neither Chrome nor Firefox allow running worker files from a local file system. This means that you have to be running your website in some sort of HTTP server in order for it to work.</p>
<p>If you have Python installed (most Linux or MacOS systems already do), just run: <code>python -m SimpleHTTPServer 8000</code> and you’re good to go. You can access your files through localhost:8000.</p>
<p>If you don’t, you can also use <a href="https://chrome.google.com/webstore/detail/web-server-for-chrome/ofhbbkphhbklhfoeikjpcbhemlocgigb">this Chrome extension</a> or any other method you prefer.</p>
<p>Thanks for reading!</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/cms/media/articles/web-workers/cover.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/cms/media/articles/web-workers/cover.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/mongodb-error-datadb-on-linux</guid>
      <title>MongoDB on Linux - Data directory /data/db not found</title>
      <description>Fix the error that occurs when trying to run freshly-installed MongoDB on a Linux machine.</description>
      <link>https://fantinel.dev/blog/mongodb-error-datadb-on-linux</link>
      <pubDate>Sun, 09 Jun 2019 00:00:00 +0000</pubDate>
      <category>Back-End</category><category>Dev</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/mongodb-error-datadb-on-linux">
              read on the site!
            </a>
          </strong>
        </div>

        <p>When setting up a Linux server or development machine, you might need to install MongoDB. However, every single time I’ve stumbled upon an error right when trying to start the <code>mongod</code> service, after following <a href="https://docs.mongodb.com/manual/tutorial/install-mongodb-on-ubuntu/">the official instructions</a>. The important line here is:</p>
<blockquote>
<p>[!error]
exception in initAndListen: NonExistentPath: Data directory /data/db not found., terminating</p>
</blockquote>
<p>This seems like an error on Mongo’s install script. The problem is that by default, Mongo points to that <code>/data/db</code> folder, and it either forgets to create or set ownership of it on installation.</p>
<p>Thankfully, the solution is quite simple. First, we’ll make sure that the folder in question exists. Run the following command from your Terminal:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="shell"><code><span class="line"><span style="color:#B392F0">sudo</span><span style="color:#9ECBFF"> mkdir</span><span style="color:#79B8FF"> -p</span><span style="color:#9ECBFF"> /data/db/</span></span></code></pre>
<p>And then, we’ll set the ownership of the folder to the user that’s going to start the mongod service. Since I only use if for local development in my computer, I set myself as the owner:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="shell"><code><span class="line"><span style="color:#B392F0">sudo</span><span style="color:#9ECBFF"> chown</span><span style="color:#9ECBFF"> `</span><span style="color:#B392F0">id</span><span style="color:#79B8FF"> -u</span><span style="color:#9ECBFF">`</span><span style="color:#B392F0"> /data/db</span></span></code></pre>
<p>Now, just running <code>mongod</code> should do the job.</p>
<p>Thanks for reading!</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/cms/media/articles/mongodb-error-datadb-on-linux/cover.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/cms/media/articles/mongodb-error-datadb-on-linux/cover.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/net-core-project-restored-different-version</guid>
      <title>.NET Core - Project version mismatch</title>
      <description>Solving this cryptic error might take you many hours. Hopefully this will help you out.</description>
      <link>https://fantinel.dev/blog/net-core-project-restored-different-version</link>
      <pubDate>Mon, 03 Jun 2019 00:00:00 +0000</pubDate>
      <category>Back-End</category><category>Dev</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/net-core-project-restored-different-version">
              read on the site!
            </a>
          </strong>
        </div>

        <p>This problem usually happens when you’re working on a solution with multiple projects that were created using different versions of the .NET SDK.</p>
<blockquote>
<p>[!error]
The project was restored using Microsoft.NETCore.App version x.x.x, but with current settings, version y.y.y would be used instead</p>
</blockquote>
<p>You may encounter this error when running <code>dotnet build</code> or maybe only on <code>dotnet publish</code>. In any case, the best way to get rid of this problem for good is the following:</p>
<ol>
<li>On the folder where your <code>.sln</code> file resides, create a new file named <code>Directory.Build.props</code>. If you’re not working with a solution (not using Visual Studio), you can use create it on the root directory of your repository (usually where <code>.gitignore</code> is;</li>
<li>With a text editor, add the following content to it:</li>
</ol>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="xml"><code><span class="line"><span style="color:#E1E4E8">~filename Directory.Build.props</span></span>
<span class="line"><span style="color:#E1E4E8">&#x3C;</span><span style="color:#85E89D">Project</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#E1E4E8">  &#x3C;</span><span style="color:#85E89D">PropertyGroup</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#E1E4E8">    &#x3C;</span><span style="color:#85E89D">TargetLatestRuntimePatch</span><span style="color:#E1E4E8">>true&#x3C;/</span><span style="color:#85E89D">TargetLatestRuntimePatch</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#E1E4E8">    &#x3C;</span><span style="color:#85E89D">GenerateFullPaths</span><span style="color:#E1E4E8">>true&#x3C;/</span><span style="color:#85E89D">GenerateFullPaths</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#E1E4E8">    &#x3C;</span><span style="color:#85E89D">LangVersion</span><span style="color:#E1E4E8">>latest&#x3C;/</span><span style="color:#85E89D">LangVersion</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#E1E4E8">  &#x3C;/</span><span style="color:#85E89D">PropertyGroup</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#E1E4E8">&#x3C;/</span><span style="color:#85E89D">Project</span><span style="color:#E1E4E8">></span></span></code></pre>
<p>What these properties do is make your projects always restore and compile using the latest version available.</p>
<p>Try running the command again. If all went right, it should be compiling successfully now!</p>
<blockquote>
<p>[!info]
In case it doesn’t work for you, you can add these lines manually on each of the <code>.csproj</code> files.</p>
</blockquote>
<p>Thanks for reading!</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/cms/media/articles/net-core-project-restored-different-version/cover.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/cms/media/articles/net-core-project-restored-different-version/cover.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/open-source-x-free-software</guid>
      <title>Open Source x Free Software: What&apos;s the difference?</title>
      <description>Learn the difference between these two terms that often get mixed up.</description>
      <link>https://fantinel.dev/blog/open-source-x-free-software</link>
      <pubDate>Sat, 23 Mar 2019 00:00:00 +0000</pubDate>
      <category>Misc</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/open-source-x-free-software">
              read on the site!
            </a>
          </strong>
        </div>

        <p>Free software is a concept that’s been around for a long time, and so is open source. At first glance, they may seem like the same thing with different names. However, they are very different in their philosophy. In this article, I’ll explain what each one is and how they are different.</p>
<h2 id="free-software">Free Software</h2>
<p>According to the <a href="https://www.fsf.org/">Free Software Foundation (FSF)</a>, free software is the software that respects the user’s freedom and sense of community. This means that the user has the freedom to run, copy, distribute, study, change and improve it.</p>
<p>A common phrase you may read somewhere is “Free as in freedom, not as in beer”. This means that the name doesn’t indicate that the software is free of charge, only that the user has the freedom to do whatever they want with it.</p>
<p>The FSF lists four essential freedoms that every free software must follow:</p>
<ul>
<li>The freedom to run the program as you wish, for any purpose (freedom 0);</li>
<li>The freedom to study how the program works, and change it so it does your computing as you wish (freedom 1). Access to the source code is a precondition for this;</li>
<li>The freedom to redistribute copies so you can help others (freedom 2);</li>
<li>The freedom to distribute copies of your modified versions to others (freedom 3). By doing this you can give the whole community a chance to benefit from your changes. Access to the source code is a precondition for this.</li>
</ul>
<p>You can read more about Free Software at gnu.org, <a href="https://www.gnu.org/philosophy/free-sw.en.html">here</a>.</p>
<h2 id="open-source">Open Source</h2>
<p>The <a href="https://opensource.org/">Open Source Initiative</a> defines 10 criteria for software to be considered open source. You can read them fully <a href="https://opensource.org/docs/definition.php">here</a>. I’ll summarize them below:</p>
<ul>
<li>Any person must be able to distribute or sell the software without restrictions;</li>
<li>The source code must be distributed along with the software, or at least link to it clearly;</li>
<li>A copy of the license must be distributed with the software;</li>
<li>People must be able to modify your software and redistribute it with the same license;</li>
<li>Modifications must only be redistributed with a different name or version than the original code;</li>
</ul>
<h2 id="the-difference">The Difference</h2>
<p>By reading each concept’s principles above, you can get the idea that Open Source is a more neutral concept when compared to Free Software. It does not deal with the user’s freedom, only with the technical aspects of the software. Richard Stallman, the founder on FSF, summarized really well the differences between the two concepts:</p>
<blockquote>
<p>The terms “free software” and “open source” stand for almost the same range of programs. However, they say deeply different things about those programs, based on different values. The free software movement campaigns for freedom for the users of computing; it is a movement for freedom and justice. By contrast, the open source idea values mainly practical advantage and does not campaign for principles. - Richard Stallman</p>
</blockquote>
<p>You can read more about it <a href="https://www.gnu.org/philosophy/open-source-misses-the-point.en.html">here</a>.</p>
<h2 id="tldr">TL;DR</h2>
<p>Open Source is a technical, practical concept. Free Software is a philosophical, broader concept. Since Free Software already considers the technical aspects of Open Source, we can say that <strong>Every Free Software is Open Source, but not every Open Source is Free Software</strong>.</p>
<h3 id="how-to-know-if-an-open-source-software-is-free-software">How to know if an Open Source software is Free Software?</h3>
<p>The easiest way is to check their license. <a href="https://www.gnu.org/licenses/license-list.html#SoftwareLicenses">The FSF lists all Free Software licenses here</a>, but usually most of them are under the GPL or MIT licenses.</p>
<p>Thanks for reading!</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/cms/media/articles/open-source-x-free-software/cover.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/cms/media/articles/open-source-x-free-software/cover.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/microfrontends</guid>
      <title>Micro Frontends: Solving the Legacy JavaScript Problem</title>
      <description>Finally a way to modernize that legacy project you keep complaining about!</description>
      <link>https://fantinel.dev/blog/microfrontends</link>
      <pubDate>Sun, 17 Mar 2019 00:00:00 +0000</pubDate>
      <category>Dev</category><category>Front-End</category><category>Angular</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/microfrontends">
              read on the site!
            </a>
          </strong>
        </div>

        <p>It’s a running joke in the development community that if a JS programmer goes into a coma for 6 months, he will need to learn everything again by the time he wakes up. This is a consequence of JavaScript’s open, decentralized nature. It’s both a blessing and a curse.</p>
<p>All the time there are new frameworks and libraries meant to make it easier to develop in JS, at least for a specific use. Sometimes they are effectively better, sometimes they just bring a new workflow, and as a developer it can be quite fun to learn them and explore new paradigms.</p>
<p>However, in the “enterprise world”, reality is different. We build big, complex applications that usually rely on a framework’s properties to work the way they do. Recent frameworks like React, Vue and Angular offer an easy way to break an application in smaller, more independent chunks that can be maintained separately, but they still rely on the main framework’s version.</p>
<p>Front-end development is becoming more and more like back-end development, with more complex architectures. Naturally, the concept of microservices was brought over and is being called Micro Frontends.</p>
<h2 id="what-do-micro-frontends-consist-of">What do Micro Frontends consist of?</h2>
<p>First, we must define what monolithic frontends are. You probably work on one of them, since they are basically every web app that is made with a specific framework and version, and only works by itself without resorting to redirects and iframes. Hence the monolith-derived name.</p>
<p>Micro Frontends, however, can be made of several pieces of different technologies - all in a “central” app that puts the puzzle pieces together.</p>
<p>Since frameworks like React and Angular are already very popular, the concept of componentizing our apps is well known, and it’s basically what Micro Frontends consist of. It uses the Custom Elements API, a web standard that is being implemented by all major browsers (with polyfills for older ones!), to provide a common ground between different components written in Angular, React, Vue, or even good old pure JS!</p>
<p>To do that, they work in a similar way modern frameworks do, with an Input/Output pattern and a very simple way to import them into the parent’s HTML. We’ll see more about this later.</p>
<h2 id="how-does-that-solve-the-legacy-problem">How does that solve the legacy problem?</h2>
<p>You’ve probably already worked on a big app that was made years ago, and used “ancient” technology. If you haven’t, lucky you! But in many enterprise environments, working with JQuery, KnockoutJS, AngularJS (v1.x) or even older JS technologies is the norm. While usually there is willingness to update the tech stack in order to gain more productivity and performance, it is usually not feasible to update an entire behemoth all at once. Add the fact that companies and their products must move fast with features and changes, and the chance to update is close to zero.</p>
<p>One alternative is to create a second app with a new technology and make the change in multiple steps, with routes handling which app is to be shown. This however, has many issues:</p>
<ul>
<li>You have to duplicate many things, like fixed menus, authentication handling, and all the base architecture. Even worse than duplicating is <strong>maintaining</strong> both of them afterwards;</li>
<li>The user experience will be severely harmed when your app redirects to a different one frequently;</li>
<li>When the new technology you use becomes obsolete, you’ll have to deal with the problem all over again.</li>
</ul>
<p>Other alternative is to use iframes, which brings a lot of problems since communication between both pages is not very straightforward and the experience feels clunky. Not very good.</p>
<p>However, when using Micro Frontends, you are able to:</p>
<ul>
<li>Migrate technologies in small steps:</li>
<li>Communication between both technologies is easy;</li>
<li>The user does not notice the use of two different stacks - they all merge seamlessly in the same experience;</li>
<li>There is no need to maintain more than one version of a component;</li>
<li>Encourages good use of components and code reuse.</li>
</ul>
<p>At my job, we had our main application written in AngularJS, and since it was a pretty huge application, migrating to a newer stack was just not feasible. We then had a project that included a complete overhaul of the design of the application, we felt it was a good time to use newer technologies.</p>
<p>Since we had a side project written in Angular 6 and most of us had knowledge of this framework, we decided to write new components for our main app in Angular 7. While the plan was to update the entire app, rewriting it entirely and publishing all at once would bring a lot of problems.</p>
<p>We then decided to use the Micro Frontends approach, rewriting our pages/components one by one and releasing it in “packages”. We did that by creating Custom Elements in Angular 7 and integrating them with the older AngularJS app. Since we would be modernizing entire pages at once, there wouldn’t be many changes on the older app. The tried-and-tested older app was still responsible for managing routes and authentication, and would just display our newer components as they were built. <em>This proved to be a successful approach that allowed us to both modernize our tech stack, minimize user impact, we kept delivering value to our customers and, since the project was broken in smaller pieces, we had flexibility to tackle other projects in between.</em></p>
<h2 id="how-do-they-work-in-practice">How do they work in practice?</h2>
<p>For this article, I am going to create a custom element with Angular 7 and use it in a simple html file. I’ve created a sample app on GitHub. <a href="https://github.com/matfantinel/custom-elements-sample">You can check the full code here.</a></p>
<h3 id="creating-the-custom-element">Creating the Custom Element</h3>
<p>First, we create a new Angular 7 app:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="shell"><code><span class="line"><span style="color:#B392F0">ng</span><span style="color:#9ECBFF"> new</span><span style="color:#9ECBFF"> angular-elements-sample</span><span style="color:#79B8FF"> --prefix</span><span style="color:#9ECBFF"> custom</span></span></code></pre>
<p>The CLI will ask you for some settings, you can choose whatever you like. Since we’re just using Custom Components in this example, I chose not to apply Angular routing.</p>
<p>Then, we need to add the Angular package that brings Custom Elements support:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="shell"><code><span class="line"><span style="color:#B392F0">ng</span><span style="color:#9ECBFF"> add</span><span style="color:#9ECBFF"> @angular/elements</span></span></code></pre>
<p>Now, on tsconfig.json file, change target to “es2015”.</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="json"><code><span class="line"><span style="color:#E1E4E8">{</span></span>
<span class="line"><span style="color:#79B8FF">  "compileOnSave"</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">false</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#79B8FF">  "compilerOptions"</span><span style="color:#E1E4E8">: {</span></span>
<span class="line"><span style="color:#FDAEB7;font-style:italic">    ...</span></span>
<span class="line"><span style="color:#79B8FF">    "target"</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">"es2015"</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#FDAEB7;font-style:italic">    ...</span></span>
<span class="line"><span style="color:#E1E4E8">  }</span></span>
<span class="line"><span style="color:#E1E4E8">}</span></span></code></pre>
<p>Then, create a new component in your app:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="shell"><code><span class="line"><span style="color:#B392F0">ng</span><span style="color:#9ECBFF"> g</span><span style="color:#9ECBFF"> component</span><span style="color:#9ECBFF"> button</span></span></code></pre>
<p>On this component, we should set its encapsulation to ShadowDom. This means that its styles will be limited to itself, and styles from the parent application won’t apply to the child component, or vice-versa. Also, we’ll be declaring an Input() property, that the element will receive from the parent, and a CustomEvent, a way to communicate events with other applications/components in the same page.</p>
<p>Below is the full component Typescript code:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#F97583">~</span><span style="color:#E1E4E8">filename button.component.ts</span></span>
<span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { Component, OnInit, ViewEncapsulation, Input, ElementRef } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> '@angular/core'</span><span style="color:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#E1E4E8">@</span><span style="color:#B392F0">Component</span><span style="color:#E1E4E8">({</span></span>
<span class="line"><span style="color:#E1E4E8">	selector: </span><span style="color:#9ECBFF">'custom-button'</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#E1E4E8">	templateUrl: </span><span style="color:#9ECBFF">'./button.component.html'</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#E1E4E8">	styleUrls: [</span><span style="color:#9ECBFF">'./button.component.scss'</span><span style="color:#E1E4E8">],</span></span>
<span class="line"><span style="color:#E1E4E8">	encapsulation: ViewEncapsulation.ShadowDom</span></span>
<span class="line"><span style="color:#E1E4E8">})</span></span>
<span class="line"><span style="color:#F97583">export</span><span style="color:#F97583"> class</span><span style="color:#B392F0"> ButtonComponent</span><span style="color:#F97583"> implements</span><span style="color:#B392F0"> OnInit</span><span style="color:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#E1E4E8">	@</span><span style="color:#B392F0">Input</span><span style="color:#E1E4E8">() </span><span style="color:#FFAB70">label</span><span style="color:#F97583"> =</span><span style="color:#9ECBFF"> 'Default Label'</span><span style="color:#E1E4E8">;</span></span>
<span class="line"><span style="color:#F97583">	private</span><span style="color:#FFAB70"> clicksCount</span><span style="color:#F97583">:</span><span style="color:#79B8FF"> number</span><span style="color:#F97583"> =</span><span style="color:#79B8FF"> 0</span><span style="color:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FFAB70">	htmlElement</span><span style="color:#F97583">:</span><span style="color:#B392F0"> HTMLElement</span><span style="color:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">	constructor</span><span style="color:#E1E4E8">(</span><span style="color:#F97583">public</span><span style="color:#FFAB70"> hostElement</span><span style="color:#F97583">:</span><span style="color:#B392F0"> ElementRef</span><span style="color:#E1E4E8">) {</span></span>
<span class="line"><span style="color:#79B8FF">		this</span><span style="color:#E1E4E8">.htmlElement </span><span style="color:#F97583">=</span><span style="color:#79B8FF"> this</span><span style="color:#E1E4E8">.hostElement.nativeElement </span><span style="color:#F97583">as</span><span style="color:#B392F0"> HTMLElement</span><span style="color:#E1E4E8">;</span></span>
<span class="line"><span style="color:#E1E4E8">	}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#B392F0">	ngOnInit</span><span style="color:#E1E4E8">() {}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#B392F0">	handleClick</span><span style="color:#F97583"> =</span><span style="color:#E1E4E8"> () </span><span style="color:#F97583">=></span><span style="color:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#79B8FF">		this</span><span style="color:#E1E4E8">.clicksCount</span><span style="color:#F97583">++</span><span style="color:#E1E4E8">;</span></span>
<span class="line"><span style="color:#F97583">		let</span><span style="color:#E1E4E8"> action </span><span style="color:#F97583">=</span><span style="color:#F97583"> new</span><span style="color:#B392F0"> CustomEvent</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">'action'</span><span style="color:#E1E4E8">, {</span></span>
<span class="line"><span style="color:#E1E4E8">			detail: {</span></span>
<span class="line"><span style="color:#E1E4E8">				clicksCount: </span><span style="color:#79B8FF">this</span><span style="color:#E1E4E8">.clicksCount</span></span>
<span class="line"><span style="color:#E1E4E8">			}</span></span>
<span class="line"><span style="color:#E1E4E8">		});</span></span>
<span class="line"><span style="color:#79B8FF">		this</span><span style="color:#E1E4E8">.htmlElement.</span><span style="color:#B392F0">dispatchEvent</span><span style="color:#E1E4E8">(action);</span></span>
<span class="line"><span style="color:#E1E4E8">	};</span></span>
<span class="line"><span style="color:#E1E4E8">}</span></span></code></pre>
<p>The HTML template is very simple:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="html"><code><span class="line"><span style="color:#E1E4E8">&#x3C;</span><span style="color:#85E89D">button</span><span style="color:#B392F0"> (click)</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"handleClick()"</span><span style="color:#E1E4E8">>{{ label }}&#x3C;/</span><span style="color:#85E89D">button</span><span style="color:#E1E4E8">></span></span></code></pre>
<p>Then, we must declare our Custom Element in our app.module.ts file:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#F97583">~</span><span style="color:#E1E4E8">filename app.module.ts</span></span>
<span class="line"><span style="color:#E1E4E8">@</span><span style="color:#B392F0">NgModule</span><span style="color:#E1E4E8">({</span></span>
<span class="line"><span style="color:#E1E4E8">	declarations: [</span><span style="color:#F97583">...</span><span style="color:#E1E4E8">ButtonComponent],</span></span>
<span class="line"><span style="color:#E1E4E8">	entryComponents: [ButtonComponent],</span></span>
<span class="line"><span style="color:#E1E4E8">	providers: [],</span></span>
<span class="line"><span style="color:#E1E4E8">	bootstrap: []</span></span>
<span class="line"><span style="color:#E1E4E8">})</span></span>
<span class="line"><span style="color:#F97583">export</span><span style="color:#F97583"> class</span><span style="color:#B392F0"> AppModule</span><span style="color:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#F97583">	constructor</span><span style="color:#E1E4E8">(</span><span style="color:#F97583">private</span><span style="color:#FFAB70"> injector</span><span style="color:#F97583">:</span><span style="color:#B392F0"> Injector</span><span style="color:#E1E4E8">) {}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#B392F0">	ngDoBootstrap</span><span style="color:#E1E4E8">() {</span></span>
<span class="line"><span style="color:#6A737D">		//Declares our component's Custom Element</span></span>
<span class="line"><span style="color:#6A737D">		//Then defines it in the DOM so it can be used in other projects</span></span>
<span class="line"><span style="color:#F97583">		const</span><span style="color:#79B8FF"> buttonElement</span><span style="color:#F97583"> =</span><span style="color:#B392F0"> createCustomElement</span><span style="color:#E1E4E8">(ButtonComponent, { injector: </span><span style="color:#79B8FF">this</span><span style="color:#E1E4E8">.injector });</span></span>
<span class="line"><span style="color:#E1E4E8">		customElements.</span><span style="color:#B392F0">define</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">'custom-button'</span><span style="color:#E1E4E8">, buttonElement);</span></span>
<span class="line"><span style="color:#E1E4E8">	}</span></span>
<span class="line"><span style="color:#E1E4E8">}</span></span></code></pre>
<p>To make it easier to use our component in another app, we can use some script magic to concat all the .js files produced by <code>ng build --prod</code> into a single properly-named file. To do that, I created the following script in package.json’s script session:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="json"><code><span class="line"><span style="color:#E1E4E8">~filename package.json</span></span>
<span class="line"><span style="color:#9ECBFF">"package"</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">"ng build --prod &#x26;&#x26; cat ./dist/runtime.js ./dist/polyfills.js ./dist/scripts.js ./dist/main.js > CustomElementsSample.js"</span></span></code></pre>
<p>If you’re on a Windows system, you won’t have access to cat. In that case install <code>jscat</code> from npm and change the cat command to jscat.</p>
<p>For that to work though, we need to disable output hashing on the angular.json file. This makes sure that the generated file names are always the same. Just change <code>"outputHashing": "all"</code> to <code>"outputHashing": "none"</code>.</p>
<p>Now we have a single js file that contains our custom element, and we can use it in our sample legacy app!</p>
<h3 id="using-the-custom-element">Using the Custom Element</h3>
<p>Check this sample html file:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="html"><code><span class="line"><span style="color:#E1E4E8">&#x3C;</span><span style="color:#85E89D">html</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#E1E4E8">	&#x3C;</span><span style="color:#85E89D">head</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#E1E4E8">		&#x3C;</span><span style="color:#85E89D">meta</span><span style="color:#B392F0"> charset</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"UTF-8"</span><span style="color:#E1E4E8"> /></span></span>
<span class="line"><span style="color:#E1E4E8">		&#x3C;</span><span style="color:#85E89D">title</span><span style="color:#E1E4E8">>This is a legacy app that uses Angular 7 custom elements&#x3C;/</span><span style="color:#85E89D">title</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#E1E4E8">		&#x3C;</span><span style="color:#85E89D">script</span><span style="color:#B392F0"> src</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"../angular-elements-sample/CustomElementsSample.js"</span><span style="color:#E1E4E8">>&#x3C;/</span><span style="color:#85E89D">script</span><span style="color:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#E1E4E8">		&#x3C;</span><span style="color:#85E89D">style</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#85E89D">			button</span><span style="color:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#79B8FF">				background-color</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">red</span><span style="color:#E1E4E8">;</span></span>
<span class="line"><span style="color:#79B8FF">				color</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">white</span><span style="color:#E1E4E8">;</span></span>
<span class="line"><span style="color:#E1E4E8">			}</span></span>
<span class="line"><span style="color:#E1E4E8">		&#x3C;/</span><span style="color:#85E89D">style</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#E1E4E8">	&#x3C;/</span><span style="color:#85E89D">head</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#E1E4E8">	&#x3C;</span><span style="color:#85E89D">body</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#E1E4E8">		&#x3C;</span><span style="color:#85E89D">custom-button</span><span style="color:#B392F0"> label</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"Legacy app label"</span><span style="color:#E1E4E8">>&#x3C;/</span><span style="color:#85E89D">custom-button</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#E1E4E8">		&#x3C;</span><span style="color:#85E89D">span</span><span style="color:#B392F0"> id</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"counter"</span><span style="color:#E1E4E8">>&#x3C;/</span><span style="color:#85E89D">span</span><span style="color:#E1E4E8">>&#x3C;</span><span style="color:#85E89D">span</span><span style="color:#E1E4E8">> clicks!&#x3C;/</span><span style="color:#85E89D">span</span><span style="color:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#E1E4E8">		&#x3C;</span><span style="color:#85E89D">script</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#F97583">			const</span><span style="color:#79B8FF"> button</span><span style="color:#F97583"> =</span><span style="color:#E1E4E8"> document.</span><span style="color:#B392F0">querySelector</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">'custom-button'</span><span style="color:#E1E4E8">);</span></span>
<span class="line"><span style="color:#E1E4E8">			button.</span><span style="color:#B392F0">addEventListener</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">'action'</span><span style="color:#E1E4E8">, (</span><span style="color:#FFAB70">event</span><span style="color:#E1E4E8">) </span><span style="color:#F97583">=></span><span style="color:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#E1E4E8">				document.</span><span style="color:#B392F0">getElementById</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">'counter'</span><span style="color:#E1E4E8">).innerHTML </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> event.detail.clicksCount;</span></span>
<span class="line"><span style="color:#E1E4E8">			});</span></span>
<span class="line"><span style="color:#E1E4E8">		&#x3C;/</span><span style="color:#85E89D">script</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#E1E4E8">	&#x3C;/</span><span style="color:#85E89D">body</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#E1E4E8">&#x3C;/</span><span style="color:#85E89D">html</span><span style="color:#E1E4E8">></span></span></code></pre>
<p>You can see that we are successfully being able to listen for events, pass the label parameter, and that the button style from the html does not apply to the component. Success!</p>
<h2 id="caveats">Caveats</h2>
<p>As with all things, there are some disadvantages in this approach:</p>
<h3 id="browser-support">Browser Support</h3>
<p>![Screenshot of Can I Use website, showing that Custom Elements work on current versions of Chrome and Firefox, but not IE and Edge, and is only partially supported on Safari.](/media/articles/microfrontends/Browser-Support.jpg ‘You can check this information on the <a href="https://caniuse.com/custom-elementsv1" target="_blank">Can I Use</a> website.’)</p>
<p>As of March 2019, only Firefox, Chrome and Chromium-based browsers fully support custom elements, while Safari implements them only partially. However, <a href="https://github.com/webcomponents/custom-elements">there’s a polyfill available</a> to bring support to older browsers.</p>
<h3 id="js-bundle-size">JS Bundle size</h3>
<p>Since you’re running components using other frameworks, you’ll still have to load them on the user’s side. If your app uses AngularJS, Angular, JQuery and React in different components, you’ll have to load all their runtimes before the app is fully functional. In this case, your better option is to minimize the bundle as much as possible, and use <a href="https://fantinel.dev/what-are-pwas-and-why-should-i-care-about-them">Service Workers and PWA capabilities</a> to improve caching on your app.</p>
<h3 id="communication-between-components-is-not-as-good-as-within-the-same-framework">Communication between components is not as good as within the same framework</h3>
<p>This should not be much of an issue unless you break a page into too many components with different technologies. Even then, communication with other components is very doable, just not as good as it would be between Angular-Angular or React-React components.</p>
<h2 id="wrapping-up">Wrapping up</h2>
<p>Micro Frontends are a consequence of the complexicity that front-end has developed in the last few years. It is very good to see technology move forward, and we may get lost amidst so much change, so it is good to know that we don’t have to be afraid to not migrate our apps to the latest and greatest. They bring us the advantages of newer frameworks while minimizing the disadvantages.</p>
<p>Thanks for reading!</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/cms/media/articles/microfrontends/cover.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/cms/media/articles/microfrontends/cover.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/dotnet-core-405-error</guid>
      <title>.NET Core - Method not allowed on PUT and DELETE requests</title>
      <description>See how to solve this annoying error after deploying your .NET Core API.</description>
      <link>https://fantinel.dev/blog/dotnet-core-405-error</link>
      <pubDate>Thu, 14 Mar 2019 00:00:00 +0000</pubDate>
      <category>Back-End</category><category>Dev</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/dotnet-core-405-error">
              read on the site!
            </a>
          </strong>
        </div>

        <p>Recently, I’ve been working on an Angular app, powered with a .NET Core API. Everything was working well and smoothly when running both on my local machine.</p>
<p>After pushing code to the beta server, most of it was working fine. The front-end was being able to call GET, POST and OPTIONS requests normally. When trying to DELETE or PUT entries, however, the following error would show up on the browser’s console:</p>
<p><img src="https://fantinel.dev/cms/media/articles/dotnet-core-405-error/post1.jpg" alt="405 (Method Not Allowed) | Access to XMLHttpRequest at &#x27;...&#x27; from origin &#x27;...&#x27; has been blocked by CORS policy: No &#x27;Access-Control-Allow-Origin&#x27; header is present on the requested resource."></p>
<p><em>Method not allowed? CORS error? What?</em></p>
<p>Initially, I was misled by the CORS error. Having had problems with this in the past, I thoroughly checked my API code for any possible problem in the configuration that could lead to this error only after deployed. I found none.</p>
<p>Then, it dawned on me that the CORS error could be not because my API wasn’t sending the ‘Access-Control-Allow-Origin’ on its response, but because it wasn’t being sent on the HTTP 405 error seen above.</p>
<h2 id="solution">Solution</h2>
<p>I ended up finding <a href="https://www.ryadel.com/en/error-405-methods-not-allowed-asp-net-core-put-delete-requests/">this article</a>. What happens is that, when published, .NET Core enables the <em>WebDAVModule</em>, which <em>disables PUT and DELETE requests</em> by default.</p>
<p>So, to solve the issue, I ended up disabling WebDAV in the whole application, by adding these lines to the auto-generated web.config:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="xml"><code><span class="line"><span style="color:#E1E4E8">~filename web.config</span></span>
<span class="line"><span style="color:#E1E4E8">&#x3C;</span><span style="color:#85E89D">system.webServer</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#E1E4E8">  &#x3C;</span><span style="color:#85E89D">modules</span><span style="color:#B392F0"> runAllManagedModulesForAllRequests</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"false"</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#E1E4E8">    &#x3C;</span><span style="color:#85E89D">remove</span><span style="color:#B392F0"> name</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"WebDAVModule"</span><span style="color:#E1E4E8"> /></span></span>
<span class="line"><span style="color:#E1E4E8">  &#x3C;/</span><span style="color:#85E89D">modules</span><span style="color:#E1E4E8">></span></span>
<span class="line"><span style="color:#E1E4E8">&#x3C;/</span><span style="color:#85E89D">system.webServer</span><span style="color:#E1E4E8">></span></span></code></pre>
<p>After restarting the API in IIS, @@TA-DA!@@ Everything (or at least your PUT and DELETE requests) should be working normally.</p>
<p>This issue seems to only occur when hosting .NET Core on Windows, which is why I could not simulate the issue by running my application in production mode on my Linux machine.</p>
<p>I hope this will be helpful to someone like it was for me!</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/cms/media/articles/dotnet-core-405-error/cover.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/cms/media/articles/dotnet-core-405-error/cover.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/what-are-pwas-and-why-should-i-care-about-them</guid>
      <title>What are PWAs and why should I care about them?</title>
      <description>We see a lot of talk about PWAs, but the concept is not that easy to understand.</description>
      <link>https://fantinel.dev/blog/what-are-pwas-and-why-should-i-care-about-them</link>
      <pubDate>Tue, 12 Mar 2019 00:00:00 +0000</pubDate>
      <category>Front-End</category><category>Dev</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/what-are-pwas-and-why-should-i-care-about-them">
              read on the site!
            </a>
          </strong>
        </div>

        <p>Progressive Web Apps are not new, but they are becoming more and more popular everyday. Big companies like Facebook, Twitter and Google have been using them for a while, and while they’re often designed for mobile, recent pushes from Microsoft and Chrome are making them a good option even for desktop.</p>
<p><a href="https://fantinel.dev/angular-pwa-how-to">I’ve covered how you can turn your Angular 6+ app into a PWA here</a>. Now, let’s see why you should do that.</p>
<h2 id="what-is-a-pwa">What is a PWA?</h2>
<p>Native apps, the ones that are designed to work specifically on Mobile/Desktop OSs, usually have some useful features, like access to the device’s functionalities like camera, GPS, sending notifications, working offline, and so on. They also are able to utilize the device’s full screen (on mobile), show up on the home screen, and are usually pretty fast to load.Web apps though, need to be accessed by a browser. This takes away some screen real estate, since the browser’s UI is also shown. They cannot work offline, can only show up on the home screen as a shortcut to the browser, and usually take longer to load, since its assets need to be loaded from the web.</p>
<p>PWAs, or Progressive Web Apps, are basically websites that have the ability to deliver a native app-like experience, by sending notifications, offering a snappier and more responsive experience, working offline, etc. This is achieved with the use of modern Web APIs, and a little thing called <strong>Service Worker</strong>.</p>
<p><em>Please keep in mind that hybrid apps developed with Ionic, Cordova, or similar frameworks <strong>are not PWAs</strong>, since they are installed like a real app and have full access to the device.</em></p>
<h3 id="service-worker">Service Worker?</h3>
<p>A Service Worker is like a client-side proxy, written in Javascript, that powers offline functionality, push notifications, caching, updating, and more.</p>
<p>They can, for instance, listen for any http requests and handle them nicely, like retrieving cached data if there’s no internet connection. To avoid problems with updates, since it caches content more aggressively, it can be configured to update itself everytime it sees there is a new version online. This avoids problems with users using older versions of our web apps (and therefore gets rid of native apps’ biggest nuisance).</p>
<p>This all may sound complicated, which is why there are many “recipes” of Service Workers around the web. They are still JavaScript files, so it’s not hard to adapt and improve them at your own pace.</p>
<p>You probably have already found some of them in the wild. In Chrome for Android, opening a certain website might trigger this panel at the bottom of the screen:</p>
<p><img src="https://fantinel.dev/cms/media/articles/what-are-pwas-and-why-should-i-care-about-them/install-prompt-android.jpg" alt="PWA Install Prompt on Android" title="Add Notepad to the home screen"></p>
<p>And on Desktop, this option shows up in the URL bar:</p>
<p><img src="https://fantinel.dev/cms/media/articles/what-are-pwas-and-why-should-i-care-about-them/install-prompt.jpg" alt="Chrome prompts you to install PWA"></p>
<p>With this, your app is able to be displayed on your phone or PC’s app list just like any other app. Cool!</p>
<p><em>Keep in mind that for now only a few Chromium-based browsers offer this functionality on Android. Firefox, sadly, is not allowed by Google to create WebAPKs (how these are called). Thanks, free market!</em> 🙄</p>
<h2 id="thats-nice-and-all-but-how-would-my-app-benefit-from-it">That’s nice and all… But how would my app benefit from it?</h2>
<p>Let’s get one thing out of the way first - <strong>PWAs are not a replacement for native apps</strong>. They have more limitations, and overall offer worse performance than a native app written in Java/Swift. Their purpose is to offer a better user experience for web apps that are used often by users. If you have a complex web app, and do not have the time/resources or simply don’t think it’s worth it to create a native counterpart, PWAs are for you. I will compare PWAs to hybrid apps, like the ones built on Ionic.</p>
<p>The first main advantage is that the entry barrier is lower. Research shows that <a href="https://techcrunch.com/2017/08/25/majority-of-u-s-consumers-still-download-zero-apps-per-month-says-comscore/">the majority of mobile users in the US download an average of 0 apps monthly</a>. That’s zero. None. That may be because searching for an app in the app store may be a bit counterintuitive depending on the situation. Let’s imagine that you have an hybrid app on the app store. Your new user probably found out about your app/service on its website, so they’re already in the browser:</p>
<ol>
<li>Logs in to your app on the browser;</li>
<li>You detect that they’re using a mobile phone, so you can show them a banner to download your app on the store;</li>
<li>They open the app store;</li>
<li>They try to download your app. Or, they might give up because they’re on their limited data plan, and avoids downloading apps on it.</li>
<li>They can either return to the web app on the browser, with all the disavantadges mentioned before, or use your hybrid app.</li>
</ol>
<p>If the user has installed your app, cool! However, you must still keep one thing in mind: your app updates independently from your web app, and the updates will take longer. Some users might even disable autoupdates altogether. Depending on how your app works, you might suffer from users using outdated versions of the code, which might result in issues for them and for you.</p>
<p>Now let’s imagine the same flow, but with a PWA:</p>
<ol>
<li>Logs in to your app on the browser;</li>
<li>A panel shows up on the bottom of their screen: “Hey, add this to your home screen!”;</li>
<li>The app is installed into the user’s phone and can now be accessed from the app list, with better screen estate, better performance (because of caching), and easier to open;</li>
<li>Your app is always on-par with the web version.</li>
</ol>
<p>Another worthy thing to mention is that you don’t have to go through the hassle of publishing an app on the app store!</p>
<h2 id="there-are-advantages-even-if-the-user-doesnt-install-your-web-app">There are advantages even if the user doesn’t install your web app</h2>
<p>So far I’ve covered how your app would benefit from users being able to install the app directly from the browser. But still, a lot of the PWA benefits still apply even if they don’t.</p>
<p>Even when opened in a browser, a PWA can still offer:</p>
<ul>
<li>Better speed after first load, due to the more aggressive cacheing of the static content;</li>
<li>Less jankiness and more responsiveness overall, since the content will be loaded from the user’s device instead of downloaded dynamically;</li>
<li>Won’t be affected by variations in the user’s connection.</li>
</ul>
<h2 id="how-can-i-convince-my-boss-to-let-me-work-on-this">How can I convince my boss to let me work on this?</h2>
<p>Well, every boss is different. But <a href="http://https//developers.google.com/web/progressive-web-apps/">Google provides some neat statistics that may help you in your endeavor</a>:</p>
<ul>
<li>Push notifications increase user engagement by up to 4 times;</li>
<li>Some use cases report that the increase in conversions after transitioning to a PWA was of 104%!</li>
</ul>
<h2 id="as-with-all-things-dont-overdo-it">As with all things, don’t overdo it</h2>
<p>This article covered the concept and advantages of PWAs, but keep in mind that they are not for every use case. <strong>Use it for web apps that users will find themselves coming back to frequently</strong>. There’s no point in doing it for your company’s website or static websites. It’s also worth remembering that <strong>PWAs do not replace native apps, they are just a lower-effort alternative for web apps without native counterparts</strong>.</p>
<p>It’s also worth mentioning that the advantages cited in this article are, as of March 2019, <strong>fully supported only by Google Chrome/Chromium</strong>. Google’s been pushing PWAs hardly lately and other companies still have some catching up to do. iOS 11.3 saw the addition of basic PWA capabilities on Safari, but some features still do not work. Firefox for Android also supports some PWA capabilities, but does not prompt the user the same way Chrome does (because Google won’t allow it, may I add).</p>
<p>Thanks for reading!</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/cms/media/articles/what-are-pwas-and-why-should-i-care-about-them/cover.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/cms/media/articles/what-are-pwas-and-why-should-i-care-about-them/cover.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/angular-pwa-how-to</guid>
      <title>How to transform your Angular 6+ app into a PWA</title>
      <description>Make your Angular app work like a native app on phones and desktop.</description>
      <link>https://fantinel.dev/blog/angular-pwa-how-to</link>
      <pubDate>Sun, 10 Mar 2019 00:00:00 +0000</pubDate>
      <category>Angular</category><category>Front-End</category><category>Dev</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/angular-pwa-how-to">
              read on the site!
            </a>
          </strong>
        </div>

        <p>Recently, I’ve converted my Angular 6 app into a PWA, allowing it to work offline and work like a native app on phones and desktop.</p>
<p>Although there are many tutorials out there about turning Angular apps into PWAs, I’ve decided to write one simply because I faced some issues that were a bit hard to solve, so if you end up having them you don’t lose as much time as I did!</p>
<blockquote>
<p>[!info]
This tutorial assumes you are using Angular CLI. Possible issues are listed at the end of the tutorial.</p>
</blockquote>
<h2 id="step-1-installing-the-angular-pwa-dependencies">Step 1. Installing the Angular PWA dependencies</h2>
<p>This is the easiest part. Run <code>ng add @angular/pwa</code> in the root of your project (the folder where <code>package.json</code> is. The CLI will automagically add <code>@angular/pwa</code> and <code>@angular/service-worker</code> as dependencies in your package.json file, and add some placeholder icons in your <code>assets</code> folder.</p>
<p>Besides that, it will create a file named <code>ngsw-config.json</code> in your root folder, as well as a <code>manifest.json</code> in your /src folder, and reference both in <code>index.html</code> and <code>app.module.ts</code>. These files are important as you’ll be using them to configure your PWA later.</p>
<h2 id="step-2-setting-up-your-manifestjson">Step 2. Setting up your manifest.json</h2>
<p>The <code>manifest.json</code> file is the one that tells the browser/OS about your app. You can set the full and short names, theme color, icons, and more. <a href="https://developers.google.com/web/fundamentals/web-app-manifest/">You can find more about its settings here.</a></p>
<p>Don’t forget to update the auto-generated icons on <code>/assets/icons</code> folder! These are necessary so that your app icon shows up for devices of all DPI settings.</p>
<h2 id="step-3-setting-up-ngsw-configjson">Step 3. Setting up ngsw-config.json</h2>
<p>This file is the one that can configure how your PWA works. You can set up different installation behaviors, as well as handle cache updates. <a href="https://angular.io/guide/service-worker-config">The Angular documentation has got all options covered here.</a></p>
<h2 id="step-4-test-it-out">Step 4. Test it out!</h2>
<p>If you want to test out your app’s new PWA capabilities locally, <code>ng serve</code> won’t help you. Instead, you can run a web server in your machine easily by installing the <code>http-server</code> package from npm:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="shell"><code><span class="line"><span style="color:#B392F0">npm</span><span style="color:#9ECBFF"> i</span><span style="color:#9ECBFF"> http-server</span><span style="color:#79B8FF"> -g</span></span></code></pre>
<p>Then, compile your app in production mode:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="shell"><code><span class="line"><span style="color:#B392F0">ng</span><span style="color:#9ECBFF"> build</span><span style="color:#79B8FF"> --prod</span></span></code></pre>
<p>And finally, start the web server:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="shell"><code><span class="line"><span style="color:#B392F0">http-server</span><span style="color:#79B8FF"> -p</span><span style="color:#79B8FF"> 8080</span><span style="color:#79B8FF"> -c-1</span><span style="color:#9ECBFF"> dist/</span><span style="color:#F97583">&#x3C;</span><span style="color:#9ECBFF">project-nam</span><span style="color:#E1E4E8">e</span><span style="color:#F97583">></span></span></code></pre>
<p>Your app should be up and running on <code>http://localhost:8080/</code> (or whichever port you chose). How do you know it’s a PWA? There are a few ways:</p>
<h3 id="check-if-the-service-worker-is-being-registered">Check if the service worker is being registered</h3>
<p><img src="https://fantinel.dev/cms/media/articles/angular-pwa-how-to/Service-Workers.jpg" alt="Screenshot of Chromium&#x27;s Service Workers UI" title="On dev console > Application > Service Workers, there should be a registered service worker for your app."></p>
<h3 id="use-chromes-audits-feature-to-validate-it-as-a-pwa">Use Chrome’s Audits feature to validate it as a PWA</h3>
<p><img src="https://fantinel.dev/cms/media/articles/angular-pwa-how-to/Audits.jpg" alt="Screenshot of Chromium&#x27;s Audits tab, displaying options on how to test your page as different devices and for different metrics. The options &#x22;mobile&#x22; and &#x22;Progressive Web App&#x22; are selected." title="On dev console > Audits, test it for Progressive Web Apps."></p>
<p><img src="https://fantinel.dev/cms/media/articles/angular-pwa-how-to/Passed-Audits.jpg" alt="Screenshot of a list of passed audits, with two highlighted: &#x22;User can be prompted to install the Web App&#x22; and &#x22;Registers a service worker&#x22;" title="If everything went well, you should see the highlighted results on the &#x27;Passed audits&#x27; section."></p>
<p>You may see some errors regarding the app not running over HTTPS. Don’t worry, that’s only because it’s running locally.</p>
<h2 id="step-5-install-it">Step 5. Install it!</h2>
<p>On Chrome/Edge, you may also see the option to install the app appear in the URL bar:</p>
<p><img src="https://fantinel.dev/cms/media/articles/angular-pwa-how-to/install-prompt.jpg" alt="Screenshot of a browser window, showing an &#x22;Install app?&#x22; prompt on the navigation bar."></p>
<p>After this, the app should already open on a separate window, and show up on your OS’s installed apps list!</p>
<p><img src="https://fantinel.dev/cms/media/articles/angular-pwa-how-to/dock-icon.jpg" alt="Screenshot of the dock of the operating system, showing the PWA icon besides other apps&#x27;, as if it were a &#x22;real&#x22; app."></p>
<h2 id="issues-encountered">Issues Encountered</h2>
<p>Even though the proccess is quite straight-forward, I still faced some issues when running it in my app. They were because of Angular tools versions, and I probably had these issues because I’m still running an Angular 6 app (and Angular 7 is out as of now).</p>
<h3 id="the-ng-add-angularpwa-command-doesnt-create-relevant-files">The <code>ng add @angular/pwa</code> command doesn’t create relevant files</h3>
<p>This was a <a href="https://github.com/angular/angular-cli/issues/11914">reported bug</a> on the CLI. I had a bugged version (6.0.8) installed on my app. I updated it by running:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="shell"><code><span class="line"><span style="color:#B392F0">npm</span><span style="color:#9ECBFF"> uninstall</span><span style="color:#9ECBFF"> @angular/cli</span><span style="color:#79B8FF"> --save</span></span>
<span class="line"><span style="color:#B392F0">npm</span><span style="color:#9ECBFF"> install</span><span style="color:#9ECBFF"> @angular/cli@latest</span><span style="color:#79B8FF"> --save</span></span></code></pre>
<p>It updated itself to version 7.1.4 and worked flawlessly.</p>
<p><em>Notice: this is related to the local cli version, the one displayed on your <code>package.json</code> file, not the global one installed in your machine.</em></p>
<h3 id="failed-to-register-a-serviceworker-a-bad-http-response-code-404-was-received-when-fetching-the-script">Failed to register a ServiceWorker: A bad HTTP response code (404) was received when fetching the script.</h3>
<p>This error shows up on the browser console after opening the app running on http-server. It happens because, when running <code>ng build --prod</code>, Angular isn’t sending the service worker’s files along with the ones from the app. You can confirm this by searching for the <code>ngsw-worker.js</code> file on the <code>/dist</code> folder of your app. If it’s not there, then you have this issue.</p>
<p>This one took me the longest to find out. Another bug related to outdated versions of Angular tools. Simply changing <code>@angular-devkit/build-angular</code> version in devDependencies to <code>~0.10.0</code> solved it.</p>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>I hope you had success on setting up your PWA! Remember that, when published, it will only work if you serve your app through HTTPS.</p>
<p>Thanks for reading!</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/cms/media/articles/angular-pwa-how-to/cover.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/cms/media/articles/angular-pwa-how-to/cover.jpg"/>          
    </item>
  
    <item>
      <guid>https://fantinel.dev/purpose-of-this-blog</guid>
      <title>The purpose of this blog</title>
      <description>Why does it exist?</description>
      <link>https://fantinel.dev/blog/purpose-of-this-blog</link>
      <pubDate>Fri, 08 Mar 2019 00:00:00 +0000</pubDate>
      <category>Meta</category>
      <content:encoded><![CDATA[
        <div style="margin: 50px 0; font-style: italic;">
          If anything looks wrong, 
          <strong>
            <a href="https://fantinel.dev/blog/purpose-of-this-blog">
              read on the site!
            </a>
          </strong>
        </div>

        <p>There are many developer blogs out there, probably because developers like to ramble about the tech they enjoy. I’m guilty of that too! This blog was created mainly for that - sharing about development stuff that I like or that I’m learning.</p>
<p>The purpose is to help myself learn about the subjects better, since there’s no better way to learn than trying to reach someone. Of course, it won’t be made of tutorials only - I intend to write about some theoretical concepts, and maybe tech stuff not related to development.</p>
<p>I consider myself a tech enthusiast. Which means I like to talk about software and gadgets in a way that most other people would find me annoying. This can be a great place to be able to share this with people that may care, hopefully you!</p>
<p>I hope to see you here in future posts, and I hope you enjoy reading what I have to say!</p>
      ]]></content:encoded>
      <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://fantinel.dev/cms/media/articles/purpose-of-this-blog/cover.jpg"/>
      <media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://fantinel.dev/cms/media/articles/purpose-of-this-blog/cover.jpg"/>          
    </item>
  
  </channel>
</rss>