<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://mystie.dev/feed.xml" rel="self" type="application/atom+xml" /><link href="https://mystie.dev/" rel="alternate" type="text/html" /><updated>2026-08-11T13:35:28+02:00</updated><id>https://mystie.dev/feed.xml</id><title type="html">arfy’s cozy place :3</title><subtitle>a teen puppygirl barking around the internet</subtitle><author><name>arfypup</name></author><entry><title type="html">Website updates: August 2026</title><link href="https://mystie.dev/2026/08/11/website-updates-august-2026/" rel="alternate" type="text/html" title="Website updates: August 2026" /><published>2026-08-11T13:01:07+02:00</published><updated>2026-08-11T13:01:07+02:00</updated><id>https://mystie.dev/2026/08/11/website-updates-august-2026</id><content type="html" xml:base="https://mystie.dev/2026/08/11/website-updates-august-2026/"><![CDATA[<p>In this post I am going to go over all the things that got added, changed, and removed on this website in August of 2026.</p>

<h2 id="added-things">Added things</h2>

<h3 id="htmx-navigation-seamless-navigation">HTMX navigation (seamless navigation)</h3>

<p>HTMX navigation (or ajax/seamless navigation/however you wanna call it) avoids a full page refresh and only loads things that are needed. You can toggle it in the settings modal, which I’ll cover next.</p>

<h3 id="settings">Settings</h3>

<p>These are local settings aimed at improving accessibility and also letting people change the style of this website! They can be found in the website’s footer.
Here’s how they look:</p>

<p><img src="/st/img/blog/settings.webp" alt="&quot;A settings modal with settings such as toggling seamless navigation, changing text/heading font, font size, etc.&quot;" /></p>

<p>The settings use JavaScript’s localStorage to save/load settings.</p>

<p>There are 7 settings in total (as of writing this):</p>

<ul>
  <li>a toggle for window blur effect;</li>
  <li>a toggle for seamless navigation;</li>
  <li>a toggle for grayscale mode;</li>
  <li>a slider for corner radius;</li>
  <li>a slider for font size;</li>
  <li>a dropdown for preferred text font;</li>
  <li>and a dropdown for preferred heading font.</li>
</ul>

<h2 id="changed-things">Changed things</h2>

<h3 id="new-backgrounds">New backgrounds</h3>

<p>New backgrounds got added for both light and dark themes.</p>

<p>Light theme:</p>

<p><img src="/st/img/wallpapers/microsoft_232.webp" alt="&quot;A light purple wallpaper of an urban city with many buildings, near a sea at sunset.&quot;" class="rounded" /></p>

<p>Dark theme:</p>

<p><img src="/st/img/wallpapers/windows_stock_photos_56.webp" alt="&quot;A dark purple/pink wallpaper of a brightly-lit bridge. In the distance is an urban city with many buildings. In the sky, the Moon can be seen in its waning crescent phase.&quot;" class="rounded" /></p>

<h2 id="miscellaneous">Miscellaneous</h2>

<p>These things aren’t that important to have their own sections, so I’ll cover them briefly here:</p>

<ul>
  <li>added text in the footer that’s shown when I’m (probably) sleeping;</li>
  <li>added a fallback font which loads if certain Unicode characters aren’t supported by the system;</li>
  <li>replaced the old mystie button with a new arfypup one;</li>
  <li>blog pages no longer have the left sidebar;</li>
  <li>updated the 404 page;</li>
  <li>added a “valid atom” button;</li>
  <li>removed the unicode warning in the blog;</li>
  <li>88x31 buttons are now automatically generated from a YAML file (<code class="language-plaintext highlighter-rouge">_data/buttons.yml</code>);</li>
  <li>added delta chat and jami to the list of socials;</li>
</ul>]]></content><author><name>arfypup</name></author><category term="website" /><category term="updates" /><summary type="html"><![CDATA[Things that got added/changed/removed on this website in August of 2026]]></summary></entry><entry><title type="html">Switching from Last.fm: rewriting my “Now playing” widget to use ListenBrainz</title><link href="https://mystie.dev/2026/07/12/rewriting-the-now-playing-widget/" rel="alternate" type="text/html" title="Switching from Last.fm: rewriting my “Now playing” widget to use ListenBrainz" /><published>2026-07-12T09:38:00+02:00</published><updated>2026-08-07T18:49:56+02:00</updated><id>https://mystie.dev/2026/07/12/rewriting-the-now-playing-widget</id><content type="html" xml:base="https://mystie.dev/2026/07/12/rewriting-the-now-playing-widget/"><![CDATA[<p><strong>This is not a tutorial! It’s just me sharing my own experience with rewriting the widget!</strong></p>

<hr />

<p>Today I switched from using <a href="https://last.fm">Last.fm</a> for my “Now playing” widget to <a href="https://listenbrainz.org">ListenBrainz</a> (+ <a href="https://musicbrainz.org">MusicBrainz</a> and <a href="https://coverartarchive.org">Cover Art Archive</a>).</p>

<p>The reason for switching is simple: Last.fm stopped working on my computer, giving me “Access Denied” errors in the player (is it a geo block? VPN block? I don’t know).</p>

<p>I had to change around <strong>80% of the code</strong> to get the ListenBrainz version working, and it was honestly kinda a pain (since i now needed to get data from three sources instead of just one), but in the end, it was really worth it. Here’s how i did it:</p>

<h2 id="step-1-connecting-my-music-player-to-listenbrainz">Step 1: Connecting my music player to ListenBrainz</h2>

<p>This was fairly easy, since the music player i use (<a href="https://www.strawberrymusicplayer.org/">Strawberry</a>) already supports this feature. I just needed to log into ListenBrainz, get my user token, and authorize Strawberry to access ListenBrainz data.</p>

<h2 id="step-2-getting-data-from-listenbrainz">Step 2: Getting data from ListenBrainz</h2>

<p>This step was also pretty easy, though i ran into an issue of the player not sending listening data, but it randomly fixed itself, so i guess it’s all good now ¯\_(ツ)_/¯</p>

<p>Getting the data is as easy as making a GET request to <code class="language-plaintext highlighter-rouge">https://api.listenbrainz.org/1/user/USERNAME/playing-now</code>:</p>

<figure class="highlight"><pre><code class="language-js" data-lang="js"><span class="kd">const</span> <span class="nx">username</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">arfypup</span><span class="dl">"</span>
<span class="kd">const</span> <span class="nx">url</span> <span class="o">=</span> <span class="s2">`https://api.listenbrainz.org/1/user/</span><span class="p">${</span><span class="nx">username</span><span class="p">}</span><span class="s2">/playing-now`</span>

<span class="kd">const</span> <span class="nx">request</span> <span class="o">=</span> <span class="k">await</span> <span class="nf">fetch</span><span class="p">(</span><span class="nx">url</span><span class="p">);</span>
<span class="kd">const</span> <span class="nx">data</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">request</span><span class="p">.</span><span class="nf">json</span><span class="p">();</span>
<span class="nx">console</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="nx">data</span><span class="p">);</span>
<span class="c1">// {"payload":{"count":1,"listens":[{"playing_now":true,"track_metadata":{"additional_info":{"duration_ms":...,"media_player":"...","media_player_version":"...","submission_client":"...","submission_client_version":"...","tracknumber":...},"artist_name":"...","release_name":"...","track_name":"..."},"user_name":"arfypup"}],"playing_now":true,"user_id":"arfypup"}}</span></code></pre></figure>

<h2 id="step-3-using-the-data-to-show-currently-playing-track">Step 3: Using the data to show currently playing track</h2>

<p>For this, i made the structure of the widget with unique HTML id’s to show the data visually. It’s nothing special really, so i’ll skip most of it.</p>

<p>What <em>is</em> special though (and different from Last.fm) is that ListenBrainz includes player metadata in the response, so we can use that to show the player that’s being used to play the current track.</p>

<p>I could of course just show the raw response, but i opted to also show the player icon to make it more visually appealing.</p>

<p>This is done by utilizing the player name in the response to create a player name to icon filename map. Here’s how it looks:</p>

<figure class="highlight"><pre><code class="language-js" data-lang="js"><span class="kd">const</span> <span class="nx">icon_map</span> <span class="o">=</span> <span class="p">{</span>
	<span class="dl">"</span><span class="s2">musicolet</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">musicolet.png</span><span class="dl">"</span><span class="p">,</span>
	<span class="dl">"</span><span class="s2">listenbrainz-android</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">listenbrainz_icon.svg</span><span class="dl">"</span><span class="p">,</span>
	<span class="dl">"</span><span class="s2">brainzplayer</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">listenbrainz_icon.svg</span><span class="dl">"</span><span class="p">,</span>
	<span class="dl">"</span><span class="s2">strawberry</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">strawberry.png</span><span class="dl">"</span>
<span class="p">}</span></code></pre></figure>

<p>It’s pretty simple, but it does the job.</p>

<h2 id="step-4-getting-the-cover-art">Step 4: Getting the cover art</h2>

<h3 id="step-41-getting-the-tracks-mbid">Step 4.1: Getting the track’s <abbr title="MusicBrainz ID">MBID</abbr></h3>

<p>This was, by far, the hardest part in creating the widget.</p>

<p>To even get to the point of getting the cover art, you need to make 2 requests: one to MusicBrainz Search API (to get the <abbr title="MusicBrainz ID">MBID</abbr> of the track), and another to Cover Art Archive to find cover art based on the track <abbr title="MusicBrainz ID">MBID</abbr>.</p>

<p>First, I started trying to get the track <abbr title="MusicBrainz ID">MBID</abbr>. Since I never used this API before, I didn’t know that you can use the <code class="language-plaintext highlighter-rouge">fmt=json</code> parameter to make the API return JSON instead of XML.</p>

<p>So I wasted a few minutes trying to parse the XML response (and I actually succeeded) before seeing the <code class="language-plaintext highlighter-rouge">fmt=json</code> parameter in the documentation and rewriting this part of the code to parse JSON instead.</p>

<p>Next, I needed to find the correct query to use to get the correct results. With a lot of trial &amp; error, I eventually found the right one: <code class="language-plaintext highlighter-rouge">artist:"${artist_name}" recording:"${track_name}" AND status:official</code></p>

<p>Then, I needed to get the <abbr title="MusicBrainz ID">MBID</abbr> of the track (or rather of the album, since that yields good results more often). I ended up using the <abbr title="MusicBrainz ID">MBID</abbr> of the first track’s album like this:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">mbid</span> <span class="o">=</span> <span class="nx">res</span><span class="p">.</span><span class="nx">recordings</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">releases</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">id</span><span class="p">;</span>
</code></pre></div></div>

<h3 id="step-42-getting-the-cover-art-itself">Step 4.2: Getting the cover art itself</h3>

<p>This step wasn’t that hard, as I already had the <abbr title="MusicBrainz ID">MBID</abbr> from the previous step. Getting the cover art data is as easy as:</p>

<figure class="highlight"><pre><code class="language-js" data-lang="js"><span class="kd">const</span> <span class="nx">c_url</span> <span class="o">=</span> <span class="s2">`https://coverartarchive.org/release/</span><span class="p">${</span><span class="nx">mbid</span><span class="p">}</span><span class="s2">`</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">c_req</span> <span class="o">=</span> <span class="k">await</span> <span class="nf">fetch</span><span class="p">(</span><span class="nx">c_url</span><span class="p">);</span>
<span class="kd">const</span> <span class="nx">c_res</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">c_req</span><span class="p">.</span><span class="nf">json</span><span class="p">();</span>

<span class="kd">const</span> <span class="nx">c_thumb_250</span> <span class="o">=</span> <span class="nx">c_res</span><span class="p">.</span><span class="nx">images</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">thumbnails</span><span class="p">.</span><span class="nx">small</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">c_thumb_500</span> <span class="o">=</span> <span class="nx">c_res</span><span class="p">.</span><span class="nx">images</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">thumbnails</span><span class="p">.</span><span class="nx">large</span><span class="p">;</span>
<span class="nx">coverArtEl</span><span class="p">.</span><span class="nx">src</span> <span class="o">=</span> <span class="nb">window</span><span class="p">.</span><span class="nx">devicePixelRatio</span> <span class="o">&gt;</span> <span class="mi">1</span> <span class="p">?</span> <span class="nx">c_thumb_500</span> <span class="p">:</span> <span class="nx">c_thumb_250</span><span class="p">;</span></code></pre></figure>

<p>The <code class="language-plaintext highlighter-rouge">c_thumb_250</code> and <code class="language-plaintext highlighter-rouge">c_thumb_500</code> variables store the thumbnail URLs for the cover art. I won’t explain what <code class="language-plaintext highlighter-rouge">window.devicePixelRatio</code> is in this post, but here it determines whether to use the smaller (250) or the bigger (500) thumbnail.</p>

<p>I am omitting error handling and other stuff, but it’s not relevant to this blog post.</p>

<h2 id="step-5-optimizing-the-widget">Step 5: Optimizing the widget</h2>

<p>Since I’ve previously set the widget to update every 5 seconds, that means it’s now making at least 3 requests every 5 seconds, even if the data hasn’t changed. We can reduce it to 1 request by only executing the other 2 requests if the track name has changed.</p>

<p>For this, we can create a variable called <code class="language-plaintext highlighter-rouge">track_name</code> to store the track name and compare it with the API’s track name:</p>

<figure class="highlight"><pre><code class="language-js" data-lang="js"><span class="kd">let</span> <span class="nx">track_name</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span>

<span class="k">async</span> <span class="kd">function</span> <span class="nf">getCurrentTrack</span><span class="p">()</span> <span class="p">{</span>
	<span class="kd">const</span> <span class="nx">currentTrack</span> <span class="o">=</span> <span class="p">...;</span>
	<span class="k">if </span><span class="p">(</span><span class="nx">currentTrack</span><span class="p">.</span><span class="nx">track_name</span> <span class="o">!==</span> <span class="nx">track_name</span><span class="p">)</span> <span class="p">{</span>
		<span class="nx">track_name</span> <span class="o">=</span> <span class="nx">currentTrack</span><span class="p">.</span><span class="nx">track_name</span><span class="p">;</span>
		<span class="c1">// do stuff</span>
	<span class="p">}</span>
<span class="p">}</span></code></pre></figure>

<h2 id="step-6-making-it-prettier">Step 6: Making it prettier</h2>

<p>Right now, the cover art changes without any sort of loading indicator, leading to previous cover art being briefly visible on a new track.</p>

<p>To fix this, I added a loading spinner SVG to the cover art that’s visible while new cover art is loading.</p>

<p>The logic of it is pretty simple: it stays hidden until the cover art is being loaded.</p>

<p>To make it look good, I overlayed the spinner on top of the cover art image like this:</p>

<figure class="highlight"><pre><code class="language-html" data-lang="html"><span class="nt">&lt;div</span> <span class="na">style=</span><span class="s">"position: relative;"</span><span class="nt">&gt;</span>
	<span class="nt">&lt;img</span> <span class="na">src=</span><span class="s">"/st/img/cover.svg"</span> <span class="na">id=</span><span class="s">"cover-art"</span> <span class="na">width=</span><span class="s">"64"</span> <span class="na">height=</span><span class="s">"64"</span><span class="nt">&gt;</span>
	<span class="nt">&lt;svg</span> <span class="na">id=</span><span class="s">"cover-loading"</span> <span class="na">class=</span><span class="s">"d-none"</span> <span class="na">style=</span><span class="s">"position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%) !important;"</span> <span class="na">fill=</span><span class="s">"var(--primary)"</span> <span class="na">viewBox=</span><span class="s">"0 0 24 24"</span> <span class="na">xmlns=</span><span class="s">"http://www.w3.org/2000/svg"</span><span class="nt">&gt;&lt;path</span> <span class="na">d=</span><span class="s">"..."</span><span class="nt">&gt;&lt;animateTransform</span> <span class="na">attributeName=</span><span class="s">"transform"</span> <span class="na">type=</span><span class="s">"rotate"</span> <span class="na">dur=</span><span class="s">"0.75s"</span> <span class="na">values=</span><span class="s">"0 12 12;360 12 12"</span> <span class="na">repeatCount=</span><span class="s">"indefinite"</span><span class="nt">/&gt;&lt;/path&gt;&lt;/svg&gt;</span>
<span class="nt">&lt;/div&gt;</span></code></pre></figure>

<h2 id="the-final-result">The final result</h2>

<p>The final widget looks like this:</p>

<p><img src="/st/img/blog/now-playing-widget.webp" alt="&quot;Now playing&quot; widget, showing the currently playing track and the player that's playing it" /></p>

<p>Thanks for reading this blog post! I hope to write more in the future, so stay tuned!</p>

<hr />

<p><strong>UPDATE 19/07/26:</strong> it turns out that you don’t have to use MusicBrainz’s Search API to get the recording/release <abbr title="MusicBrainz ID">MBID</abbr>!</p>

<p>You can use the ListenBrainz API instead to get user listens data, and that includes both the recording and the release <abbr title="MusicBrainz ID">MBID</abbr> (and also <abbr title="Cover Art Archive">CAA</abbr> ID, but more about that later), and in my experience also gets the right <abbr title="MusicBrainz ID">MBID</abbr> for cover art more often.</p>

<figure class="highlight"><pre><code class="language-js" data-lang="js"><span class="kd">const</span> <span class="nx">url</span> <span class="o">=</span> <span class="s2">`https://api.listenbrainz.org/1/user/</span><span class="p">${</span><span class="nx">username</span><span class="p">}</span><span class="s2">/listens?count=1`</span>
<span class="kd">const</span> <span class="nx">request</span> <span class="o">=</span> <span class="k">await</span> <span class="nf">fetch</span><span class="p">(</span><span class="nx">url</span><span class="p">);</span>
<span class="kd">const</span> <span class="nx">res</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">request</span><span class="p">.</span><span class="nf">json</span><span class="p">();</span>

<span class="kd">const</span> <span class="nx">meta</span> <span class="o">=</span> <span class="nx">res</span><span class="p">.</span><span class="nx">payload</span><span class="p">.</span><span class="nx">listens</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">track_metadata</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">mbid</span> <span class="o">=</span> <span class="nx">meta</span><span class="p">.</span><span class="nx">mbid_mapping</span><span class="p">.</span><span class="nx">caa_release_mbid</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">caa_id</span> <span class="o">=</span> <span class="nx">meta</span><span class="p">.</span><span class="nx">mbid_mapping</span><span class="p">.</span><span class="nx">caa_id</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">r_mbid</span> <span class="o">=</span> <span class="nx">meta</span><span class="p">.</span><span class="nx">mbid_mapping</span><span class="p">.</span><span class="nx">recording_mbid</span><span class="p">;</span></code></pre></figure>

<p>I also found out that you don’t need Cover Art Archive at all to get the cover art URL - you can just craft the link yourself with the release <abbr title="MusicBrainz ID">MBID</abbr> and <abbr title="Cover Art Archive">CAA</abbr> ID:</p>

<figure class="highlight"><pre><code class="language-js" data-lang="js"><span class="c1">// set cover art url based on mbid and caa_id</span>
<span class="nx">coverArtEl</span><span class="p">.</span><span class="nx">src</span> <span class="o">=</span> <span class="s2">`https://archive.org/download/mbid-</span><span class="p">${</span><span class="nx">mbid</span><span class="p">}</span><span class="s2">/mbid-</span><span class="p">${</span><span class="nx">mbid</span><span class="p">}</span><span class="s2">-</span><span class="p">${</span><span class="nx">caa_id</span><span class="p">}</span><span class="s2">_thumb250.jpg`</span><span class="p">;</span></code></pre></figure>

<p>That’s one of the reasons I decided to switch to ListenBrainz API for getting the recording/release <abbr title="MusicBrainz ID">MBID</abbr>’s.</p>

<p>In my experience, skipping Cover Art Archive decreases the cover art loading time by a lot.</p>

<p>I also removed the logic for using a 500px thumbnail, since the image element is 64x64 anyway.</p>

<p>Until next time!</p>]]></content><author><name>arfypup</name></author><category term="general" /><category term="webdev" /><category term="frontend" /><summary type="html"><![CDATA[In this post, I'm gonna detail the steps I took to rewrite this widget.]]></summary></entry><entry><title type="html">Making toasts with the Popover API</title><link href="https://mystie.dev/2026/07/11/making-toasts-with-popover-api/" rel="alternate" type="text/html" title="Making toasts with the Popover API" /><published>2026-07-11T12:09:00+02:00</published><updated>2026-07-11T12:09:00+02:00</updated><id>https://mystie.dev/2026/07/11/making-toasts-with-popover-api</id><content type="html" xml:base="https://mystie.dev/2026/07/11/making-toasts-with-popover-api/"><![CDATA[<p>So the new <a href="https://developer.mozilla.org/en-US/docs/Web/API/Popover_API">Popover API</a> is now available in most<sup id="fnref:1"><a href="#fn:1" class="footnote" rel="footnote" role="doc-noteref">1</a></sup> browsers. It lets you easily create non-modal popovers. But what if we pushed it further than that? What if we could create toasts with zero libraries, using just the Popover API? That’s what this guide is about.</p>

<p>Let’s start with creating a function called <code class="language-plaintext highlighter-rouge">makeToast()</code> and initializing variables for the toast parts:</p>

<figure class="highlight"><pre><code class="language-js" data-lang="js"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
</pre></td><td class="code"><pre><span class="kd">const</span> <span class="nx">t_anim_duration</span> <span class="o">=</span> <span class="mi">200</span><span class="p">;</span>

<span class="kd">function</span> <span class="nf">makeToast</span><span class="p">(</span><span class="nx">title</span><span class="o">=</span><span class="dl">"</span><span class="s2">Toast</span><span class="dl">"</span><span class="p">,</span> <span class="nx">msg</span><span class="o">=</span><span class="dl">"</span><span class="s2">Hello World</span><span class="dl">"</span><span class="p">,</span> <span class="nx">color</span><span class="o">=</span><span class="dl">"</span><span class="s2">white</span><span class="dl">"</span><span class="p">,</span> <span class="nx">bg</span><span class="o">=</span><span class="dl">"</span><span class="s2">DarkSlateBlue</span><span class="dl">"</span><span class="p">)</span> <span class="p">{</span>
	<span class="c1">// initializing toast parts</span>
	<span class="kd">const</span> <span class="nx">t</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nf">createElement</span><span class="p">(</span><span class="dl">'</span><span class="s1">div</span><span class="dl">'</span><span class="p">);</span>
	<span class="kd">const</span> <span class="nx">t_head</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nf">createElement</span><span class="p">(</span><span class="dl">'</span><span class="s1">div</span><span class="dl">'</span><span class="p">);</span>
	<span class="kd">const</span> <span class="nx">t_head_title</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nf">createElement</span><span class="p">(</span><span class="dl">'</span><span class="s1">b</span><span class="dl">'</span><span class="p">);</span>
	<span class="kd">const</span> <span class="nx">t_head_close</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nf">createElement</span><span class="p">(</span><span class="dl">'</span><span class="s1">button</span><span class="dl">'</span><span class="p">);</span>
	<span class="kd">const</span> <span class="nx">t_msg</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nf">createTextNode</span><span class="p">(</span><span class="nx">msg</span><span class="p">);</span>
	<span class="kd">const</span> <span class="nx">t_timeout</span> <span class="o">=</span> <span class="mi">3000</span><span class="p">;</span>
</pre></td></tr></tbody></table></code></pre></figure>

<details>
  <summary>Variable explanation:</summary>

  <ul>
    <li><code class="language-plaintext highlighter-rouge">t</code> - the toast itself;</li>
    <li><code class="language-plaintext highlighter-rouge">t_head</code> - toast head (title + close button);</li>
    <li><code class="language-plaintext highlighter-rouge">t_head_title</code> - toast title;</li>
    <li><code class="language-plaintext highlighter-rouge">t_head_close</code> - the close button;</li>
    <li><code class="language-plaintext highlighter-rouge">t_msg</code> - toast message.</li>
    <li><code class="language-plaintext highlighter-rouge">t_timeout</code> - how much to wait before removing the toast (in milliseconds)</li>
    <li><code class="language-plaintext highlighter-rouge">t_anim_duration</code> - duration of the closing animation</li>
  </ul>

</details>

<p>Notice that we define <code class="language-plaintext highlighter-rouge">t_anim_duration</code> <strong>outside</strong> of the <code class="language-plaintext highlighter-rouge">makeToast</code> function, that way we can use it in other functions later.</p>

<p>Next, we need to create the toast’s structure and style it:</p>

<figure class="highlight"><pre><code class="language-js" data-lang="js"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
</pre></td><td class="code"><pre>	<span class="nx">t</span><span class="p">.</span><span class="nx">classList</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="dl">"</span><span class="s2">toast</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">newest</span><span class="dl">"</span><span class="p">);</span>
	<span class="nx">t_head</span><span class="p">.</span><span class="nx">classList</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="dl">"</span><span class="s2">toast-head</span><span class="dl">"</span><span class="p">);</span>
	<span class="nx">t_head_close</span><span class="p">.</span><span class="nx">classList</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="dl">"</span><span class="s2">toast-close</span><span class="dl">"</span><span class="p">);</span>

	<span class="nx">t</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">color</span> <span class="o">=</span> <span class="nx">color</span><span class="p">;</span>
	<span class="nx">t_head_close</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">color</span> <span class="o">=</span> <span class="nx">color</span><span class="p">;</span>
	<span class="nx">t</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">background</span> <span class="o">=</span> <span class="nx">bg</span><span class="p">;</span>

	<span class="nx">t_head</span><span class="p">.</span><span class="nf">appendChild</span><span class="p">(</span><span class="nx">t_head_title</span><span class="p">);</span>
	<span class="nx">t_head</span><span class="p">.</span><span class="nf">appendChild</span><span class="p">(</span><span class="nx">t_head_close</span><span class="p">);</span>

	<span class="nx">t</span><span class="p">.</span><span class="nf">appendChild</span><span class="p">(</span><span class="nx">t_head</span><span class="p">);</span>
	<span class="nx">t</span><span class="p">.</span><span class="nf">appendChild</span><span class="p">(</span><span class="nx">t_msg</span><span class="p">);</span>

	<span class="nb">document</span><span class="p">.</span><span class="nx">body</span><span class="p">.</span><span class="nf">appendChild</span><span class="p">(</span><span class="nx">t</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></figure>

<p>This does four things:</p>

<ol>
  <li>Adds classes to toast’s parts for styling the toast with CSS</li>
  <li>Sets the color and background to the <code class="language-plaintext highlighter-rouge">color</code> and <code class="language-plaintext highlighter-rouge">bg</code> arguments that are passed to the function</li>
  <li>Appends the toast’s parts to the toast itself</li>
  <li>Appends the toast to the <code class="language-plaintext highlighter-rouge">&lt;body&gt;</code> of the document</li>
</ol>

<p>Next, we need to add content to the toast and make it functional:</p>

<figure class="highlight"><pre><code class="language-js" data-lang="js"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
</pre></td><td class="code"><pre>	<span class="c1">// ensuring the toast doesn't get closed with a click</span>
	<span class="nx">t</span><span class="p">.</span><span class="nx">popover</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">manual</span><span class="dl">"</span><span class="p">;</span>

	<span class="nx">t_head_title</span><span class="p">.</span><span class="nx">textContent</span> <span class="o">=</span> <span class="nx">title</span><span class="p">;</span>
	<span class="c1">// you can use other symbols if you want</span>
	<span class="nx">t_head_close</span><span class="p">.</span><span class="nx">textContent</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">✕</span><span class="dl">"</span><span class="p">;</span>

	<span class="c1">// remove toast upon clicking the close button</span>
	<span class="nx">t_head_close</span><span class="p">.</span><span class="nf">addEventListener</span><span class="p">(</span><span class="dl">'</span><span class="s1">click</span><span class="dl">'</span><span class="p">,</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
		<span class="kd">const</span> <span class="nx">target</span> <span class="o">=</span> <span class="nx">t_head_close</span><span class="p">.</span><span class="nx">parentElement</span><span class="p">.</span><span class="nx">parentElement</span><span class="p">;</span> <span class="c1">// =&gt; &lt;div class="toast"&gt;&lt;/div&gt;</span>
		<span class="nf">removeToast</span><span class="p">(</span><span class="nx">target</span><span class="p">);</span>
	<span class="p">});</span>

	<span class="nx">t</span><span class="p">.</span><span class="nf">showPopover</span><span class="p">();</span>

	<span class="c1">// old toasts should be pushed down when new ones spawn</span>
	<span class="nf">requestAnimationFrame</span><span class="p">(()</span> <span class="o">=&gt;</span> <span class="p">{</span>
		<span class="nf">moveToastsDown</span><span class="p">();</span>
	<span class="p">});</span>

	<span class="c1">// remove toast after (timeout - animation_duration) ms</span>
	<span class="kd">let</span> <span class="nx">tm</span> <span class="o">=</span> <span class="nf">setTimeout</span><span class="p">(()</span> <span class="o">=&gt;</span> <span class="p">{</span>
		<span class="nf">removeToast</span><span class="p">(</span><span class="nx">t</span><span class="p">);</span>
	<span class="p">},</span> <span class="nx">t_timeout</span> <span class="o">-</span> <span class="nx">t_anim_duration</span><span class="p">);</span>

	<span class="c1">// stop timeout if mouse is hovering over the toast</span>
	<span class="nx">t</span><span class="p">.</span><span class="nf">addEventListener</span><span class="p">(</span><span class="dl">"</span><span class="s2">mouseover</span><span class="dl">"</span><span class="p">,</span> <span class="nf">function </span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
		<span class="nf">clearTimeout</span><span class="p">(</span><span class="nx">tm</span><span class="p">);</span>
		<span class="nx">tm</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
	<span class="p">},</span> <span class="kc">false</span><span class="p">);</span>

	<span class="c1">// start timeout when mouse leaves toast</span>
	<span class="nx">t</span><span class="p">.</span><span class="nf">addEventListener</span><span class="p">(</span><span class="dl">"</span><span class="s2">mouseleave</span><span class="dl">"</span><span class="p">,</span> <span class="nf">function </span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
		<span class="nx">tm</span> <span class="o">=</span> <span class="nf">setTimeout</span><span class="p">(()</span> <span class="o">=&gt;</span> <span class="p">{</span>
			<span class="nf">removeToast</span><span class="p">(</span><span class="nx">t</span><span class="p">);</span>
		<span class="p">},</span> <span class="nx">t_timeout</span> <span class="o">-</span> <span class="nx">t_anim_duration</span><span class="p">);</span>
	<span class="p">},</span> <span class="kc">false</span><span class="p">);</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>

<p>And finally, we need to add helper functions:</p>

<figure class="highlight"><pre><code class="language-js" data-lang="js"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
</pre></td><td class="code"><pre><span class="kd">function</span> <span class="nf">removeToast</span><span class="p">(</span><span class="nx">t</span><span class="p">)</span> <span class="p">{</span>
	<span class="nx">t</span><span class="p">.</span><span class="nx">classList</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="dl">"</span><span class="s2">closing</span><span class="dl">"</span><span class="p">);</span>

	<span class="nf">setTimeout</span><span class="p">(()</span> <span class="o">=&gt;</span> <span class="p">{</span>
		<span class="nx">t</span><span class="p">.</span><span class="nf">hidePopover</span><span class="p">();</span>
		<span class="nx">t</span><span class="p">.</span><span class="nf">remove</span><span class="p">();</span>
	<span class="p">},</span> <span class="nx">t_anim_duration</span><span class="p">);</span>
<span class="p">}</span>

<span class="kd">function</span> <span class="nf">moveToastsDown</span><span class="p">()</span> <span class="p">{</span>
	<span class="kd">const</span> <span class="nx">toasts</span> <span class="o">=</span> <span class="p">[...</span><span class="nb">document</span><span class="p">.</span><span class="nf">querySelectorAll</span><span class="p">(</span><span class="dl">"</span><span class="s2">.toast</span><span class="dl">"</span><span class="p">)].</span><span class="nf">reverse</span><span class="p">();</span>
	<span class="kd">const</span> <span class="nx">initialOffset</span> <span class="o">=</span> <span class="mi">16</span><span class="p">;</span>
	<span class="kd">let</span> <span class="nx">offset</span> <span class="o">=</span> <span class="nx">initialOffset</span><span class="p">;</span>

	<span class="nx">toasts</span><span class="p">.</span><span class="nf">forEach</span><span class="p">((</span><span class="nx">toast</span><span class="p">,</span> <span class="nx">i</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
		<span class="nx">toast</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">top</span> <span class="o">=</span> <span class="s2">`</span><span class="p">${</span><span class="nx">offset</span><span class="p">}</span><span class="s2">px`</span><span class="p">;</span>
		<span class="nx">offset</span> <span class="o">+=</span> <span class="nx">toast</span><span class="p">.</span><span class="nx">offsetHeight</span> <span class="o">+</span> <span class="nx">initialOffset</span><span class="p">;</span>
	<span class="p">});</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>

<p>Here’s how the final code looks like:</p>

<figure class="highlight"><pre><code class="language-js" data-lang="js"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
</pre></td><td class="code"><pre><span class="kd">const</span> <span class="nx">t_anim_duration</span> <span class="o">=</span> <span class="mi">200</span><span class="p">;</span>
<span class="kd">function</span> <span class="nf">makeToast</span><span class="p">(</span><span class="nx">title</span><span class="o">=</span><span class="dl">"</span><span class="s2">toast</span><span class="dl">"</span><span class="p">,</span> <span class="nx">msg</span><span class="o">=</span><span class="dl">"</span><span class="s2">Hello World</span><span class="dl">"</span><span class="p">,</span> <span class="nx">color</span><span class="o">=</span><span class="dl">"</span><span class="s2">white</span><span class="dl">"</span><span class="p">,</span> <span class="nx">bg</span><span class="o">=</span><span class="dl">"</span><span class="s2">DarkSlateBlue</span><span class="dl">"</span><span class="p">)</span> <span class="p">{</span>
	<span class="c1">// initializing toast parts</span>
	<span class="kd">const</span> <span class="nx">t</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nf">createElement</span><span class="p">(</span><span class="dl">'</span><span class="s1">div</span><span class="dl">'</span><span class="p">);</span>
	<span class="kd">const</span> <span class="nx">t_head</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nf">createElement</span><span class="p">(</span><span class="dl">'</span><span class="s1">div</span><span class="dl">'</span><span class="p">);</span>
	<span class="kd">const</span> <span class="nx">t_head_title</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nf">createElement</span><span class="p">(</span><span class="dl">'</span><span class="s1">b</span><span class="dl">'</span><span class="p">);</span>
	<span class="kd">const</span> <span class="nx">t_head_close</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nf">createElement</span><span class="p">(</span><span class="dl">'</span><span class="s1">button</span><span class="dl">'</span><span class="p">);</span>
	<span class="kd">const</span> <span class="nx">t_msg</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nf">createTextNode</span><span class="p">(</span><span class="nx">msg</span><span class="p">);</span>
	<span class="kd">const</span> <span class="nx">t_timeout</span> <span class="o">=</span> <span class="mi">3000</span><span class="p">;</span>

	<span class="c1">// adding classes</span>
	<span class="nx">t</span><span class="p">.</span><span class="nx">classList</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="dl">"</span><span class="s2">toast</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">newest</span><span class="dl">"</span><span class="p">);</span>
	<span class="nx">t_head</span><span class="p">.</span><span class="nx">classList</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="dl">"</span><span class="s2">toast-head</span><span class="dl">"</span><span class="p">);</span>
	<span class="nx">t_head_close</span><span class="p">.</span><span class="nx">classList</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="dl">"</span><span class="s2">toast-close</span><span class="dl">"</span><span class="p">);</span>

	<span class="c1">// styling</span>
	<span class="nx">t</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">color</span> <span class="o">=</span> <span class="nx">color</span><span class="p">;</span>
	<span class="nx">t_head_close</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">color</span> <span class="o">=</span> <span class="nx">color</span><span class="p">;</span>
	<span class="nx">t</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">background</span> <span class="o">=</span> <span class="nx">bg</span><span class="p">;</span>

	<span class="c1">// structuring</span>
	<span class="nx">t_head</span><span class="p">.</span><span class="nf">appendChild</span><span class="p">(</span><span class="nx">t_head_title</span><span class="p">);</span>
	<span class="nx">t_head</span><span class="p">.</span><span class="nf">appendChild</span><span class="p">(</span><span class="nx">t_head_close</span><span class="p">);</span>
	<span class="nx">t</span><span class="p">.</span><span class="nf">appendChild</span><span class="p">(</span><span class="nx">t_head</span><span class="p">);</span>
	<span class="nx">t</span><span class="p">.</span><span class="nf">appendChild</span><span class="p">(</span><span class="nx">t_msg</span><span class="p">);</span>
	<span class="nb">document</span><span class="p">.</span><span class="nx">body</span><span class="p">.</span><span class="nf">appendChild</span><span class="p">(</span><span class="nx">t</span><span class="p">);</span>

	<span class="c1">// ensuring the toast doesn't get closed with a click</span>
	<span class="nx">t</span><span class="p">.</span><span class="nx">popover</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">manual</span><span class="dl">"</span><span class="p">;</span>

	<span class="nx">t_head_title</span><span class="p">.</span><span class="nx">textContent</span> <span class="o">=</span> <span class="nx">title</span><span class="p">;</span>
	<span class="c1">// you can use other symbols if you want</span>
	<span class="nx">t_head_close</span><span class="p">.</span><span class="nx">textContent</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">✕</span><span class="dl">"</span><span class="p">;</span>

	<span class="c1">// remove toast upon clicking the close button</span>
	<span class="nx">t_head_close</span><span class="p">.</span><span class="nf">addEventListener</span><span class="p">(</span><span class="dl">'</span><span class="s1">click</span><span class="dl">'</span><span class="p">,</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
		<span class="nf">removeToast</span><span class="p">(</span><span class="nx">t_head_close</span><span class="p">.</span><span class="nx">parentElement</span><span class="p">.</span><span class="nx">parentElement</span><span class="p">);</span>
	<span class="p">});</span>

	<span class="nx">t</span><span class="p">.</span><span class="nf">showPopover</span><span class="p">();</span>

	<span class="c1">// old toasts should be pushed down</span>
	<span class="nf">requestAnimationFrame</span><span class="p">(()</span> <span class="o">=&gt;</span> <span class="p">{</span>
		<span class="nf">moveToastsDown</span><span class="p">();</span>
	<span class="p">});</span>

	<span class="c1">// remove toast after (timeout - animation_duration) ms</span>
	<span class="kd">let</span> <span class="nx">tm</span> <span class="o">=</span> <span class="nf">setTimeout</span><span class="p">(()</span> <span class="o">=&gt;</span> <span class="p">{</span>
		<span class="nf">removeToast</span><span class="p">(</span><span class="nx">t</span><span class="p">);</span>
	<span class="p">},</span> <span class="nx">t_timeout</span> <span class="o">-</span> <span class="nx">t_anim_duration</span><span class="p">);</span>

	<span class="c1">// stop timeout if mouse is hovering over the toast</span>
	<span class="nx">t</span><span class="p">.</span><span class="nf">addEventListener</span><span class="p">(</span><span class="dl">"</span><span class="s2">mouseover</span><span class="dl">"</span><span class="p">,</span> <span class="nf">function </span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
		<span class="nf">clearTimeout</span><span class="p">(</span><span class="nx">tm</span><span class="p">);</span>
		<span class="nx">tm</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
	<span class="p">},</span> <span class="kc">false</span><span class="p">);</span>

	<span class="c1">// start timeout when mouse leaves toast</span>
	<span class="nx">t</span><span class="p">.</span><span class="nf">addEventListener</span><span class="p">(</span><span class="dl">"</span><span class="s2">mouseleave</span><span class="dl">"</span><span class="p">,</span> <span class="nf">function </span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
		<span class="nx">tm</span> <span class="o">=</span> <span class="nf">setTimeout</span><span class="p">(()</span> <span class="o">=&gt;</span> <span class="p">{</span>
			<span class="nf">removeToast</span><span class="p">(</span><span class="nx">t</span><span class="p">);</span>
		<span class="p">},</span> <span class="nx">t_timeout</span> <span class="o">-</span> <span class="nx">t_anim_duration</span><span class="p">);</span>
	<span class="p">},</span> <span class="kc">false</span><span class="p">);</span>
<span class="p">}</span>

<span class="kd">function</span> <span class="nf">removeToast</span><span class="p">(</span><span class="nx">t</span><span class="p">)</span> <span class="p">{</span>
	<span class="nx">t</span><span class="p">.</span><span class="nx">classList</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="dl">"</span><span class="s2">closing</span><span class="dl">"</span><span class="p">);</span>

	<span class="nf">setTimeout</span><span class="p">(()</span> <span class="o">=&gt;</span> <span class="p">{</span>
		<span class="nx">t</span><span class="p">.</span><span class="nf">hidePopover</span><span class="p">();</span>
		<span class="nx">t</span><span class="p">.</span><span class="nf">remove</span><span class="p">();</span>
	<span class="p">},</span> <span class="nx">t_anim_duration</span><span class="p">);</span>
<span class="p">}</span>

<span class="kd">function</span> <span class="nf">moveToastsDown</span><span class="p">()</span> <span class="p">{</span>
	<span class="kd">const</span> <span class="nx">toasts</span> <span class="o">=</span> <span class="p">[...</span><span class="nb">document</span><span class="p">.</span><span class="nf">querySelectorAll</span><span class="p">(</span><span class="dl">"</span><span class="s2">.toast</span><span class="dl">"</span><span class="p">)].</span><span class="nf">reverse</span><span class="p">();</span>
	<span class="kd">const</span> <span class="nx">initialOffset</span> <span class="o">=</span> <span class="mi">16</span><span class="p">;</span>
	<span class="kd">let</span> <span class="nx">offset</span> <span class="o">=</span> <span class="nx">initialOffset</span><span class="p">;</span>

	<span class="nx">toasts</span><span class="p">.</span><span class="nf">forEach</span><span class="p">((</span><span class="nx">toast</span><span class="p">,</span> <span class="nx">i</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
		<span class="nx">toast</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">top</span> <span class="o">=</span> <span class="s2">`</span><span class="p">${</span><span class="nx">offset</span><span class="p">}</span><span class="s2">px`</span><span class="p">;</span>
		<span class="nx">offset</span> <span class="o">+=</span> <span class="nx">toast</span><span class="p">.</span><span class="nx">offsetHeight</span> <span class="o">+</span> <span class="nx">initialOffset</span><span class="p">;</span>
	<span class="p">});</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>

<p>Here’s an example style for the toasts:</p>

<figure class="highlight"><pre><code class="language-css" data-lang="css"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
</pre></td><td class="code"><pre><span class="nd">:root</span> <span class="p">{</span>
	<span class="py">--t-background</span><span class="p">:</span> <span class="nx">DarkSlateBlue</span><span class="p">;</span>
	<span class="py">--t-radius</span><span class="p">:</span> <span class="m">5px</span><span class="p">;</span>
	<span class="py">--t-color</span><span class="p">:</span> <span class="nx">white</span><span class="p">;</span>
	<span class="py">--t-font-size</span><span class="p">:</span> <span class="m">14px</span><span class="p">;</span>
	<span class="py">--t-font-family</span><span class="p">:</span> <span class="n">system-ui</span><span class="p">,</span> <span class="o">-</span><span class="n">apple-system</span><span class="p">,</span> <span class="s1">"Segoe UI"</span><span class="p">,</span> <span class="n">Roboto</span><span class="p">,</span> <span class="s1">"Helvetica Neue"</span><span class="p">,</span> <span class="s1">"Noto Sans"</span><span class="p">,</span> <span class="s1">"Liberation Sans"</span><span class="p">,</span> <span class="n">Arial</span><span class="p">,</span> <span class="nb">sans-serif</span><span class="p">,</span> <span class="s1">"Apple Color Emoji"</span><span class="p">,</span> <span class="s1">"Segoe UI Emoji"</span><span class="p">,</span> <span class="s1">"Segoe UI Symbol"</span><span class="p">,</span> <span class="s1">"Noto Color Emoji"</span><span class="p">;</span>
<span class="p">}</span>
<span class="nc">.toast</span> <span class="p">{</span>
	<span class="nl">padding</span><span class="p">:</span> <span class="m">1em</span><span class="p">;</span>
	<span class="nl">position</span><span class="p">:</span> <span class="nb">fixed</span><span class="p">;</span>
	<span class="nl">z-index</span><span class="p">:</span> <span class="m">9999</span><span class="p">;</span>
	<span class="nl">background</span><span class="p">:</span> <span class="nf">var</span><span class="p">(</span><span class="l">--t-background</span><span class="p">);</span>
	<span class="nl">border-radius</span><span class="p">:</span> <span class="nf">var</span><span class="p">(</span><span class="l">--t-radius</span><span class="p">);</span>
	<span class="nl">border</span><span class="p">:</span> <span class="nb">none</span><span class="p">;</span>
	<span class="nl">color</span><span class="p">:</span> <span class="nf">var</span><span class="p">(</span><span class="l">--t-color</span><span class="p">);</span>
	<span class="nl">min-width</span><span class="p">:</span> <span class="m">240px</span><span class="p">;</span>
	<span class="nl">max-width</span><span class="p">:</span> <span class="m">40%</span><span class="p">;</span>
	<span class="nl">opacity</span><span class="p">:</span> <span class="m">0</span><span class="p">;</span>
	<span class="nl">top</span><span class="p">:</span> <span class="m">0</span><span class="p">;</span>
	<span class="nl">transition</span><span class="p">:</span> <span class="nf">opacity</span> <span class="m">200ms</span> <span class="nb">ease-out</span><span class="p">,</span> <span class="nb">top</span> <span class="m">.25s</span> <span class="nb">ease-in-out</span><span class="p">;</span>
	<span class="nl">font-size</span><span class="p">:</span> <span class="nf">var</span><span class="p">(</span><span class="l">--t-font-size</span><span class="p">);</span>
	<span class="nl">font-family</span><span class="p">:</span> <span class="nf">var</span><span class="p">(</span><span class="l">--t-font-family</span><span class="p">);</span>
<span class="p">}</span>

<span class="nc">.toast.newest</span> <span class="p">{</span>
	<span class="nl">top</span><span class="p">:</span> <span class="m">-2em</span><span class="p">;</span>
<span class="p">}</span>

<span class="nc">.toast</span><span class="nd">:popover-open</span> <span class="p">{</span>
	<span class="nl">opacity</span><span class="p">:</span> <span class="m">1</span><span class="p">;</span>
	<span class="nl">inset</span><span class="p">:</span> <span class="n">unset</span><span class="p">;</span>
	<span class="nl">right</span><span class="p">:</span> <span class="m">1em</span><span class="p">;</span>
	<span class="nl">top</span><span class="p">:</span> <span class="m">1em</span><span class="p">;</span>
<span class="p">}</span>

<span class="nc">.toast.closing</span> <span class="p">{</span>
	<span class="nl">opacity</span><span class="p">:</span> <span class="m">0</span><span class="p">;</span>
<span class="p">}</span>

<span class="nc">.toast-head</span> <span class="p">{</span>
	<span class="nl">display</span><span class="p">:</span> <span class="nb">flex</span><span class="p">;</span>
	<span class="nl">justify-content</span><span class="p">:</span> <span class="nb">space-between</span><span class="p">;</span>
	<span class="nl">align-items</span><span class="p">:</span> <span class="nb">center</span><span class="p">;</span>
<span class="p">}</span>

<span class="nc">.toast-close</span> <span class="p">{</span>
	<span class="nl">border</span><span class="p">:</span> <span class="nb">none</span><span class="p">;</span>
	<span class="nl">background</span><span class="p">:</span> <span class="nb">transparent</span><span class="p">;</span>
	<span class="nl">cursor</span><span class="p">:</span> <span class="nb">pointer</span><span class="p">;</span>
	<span class="nl">font-size</span><span class="p">:</span> <span class="nf">var</span><span class="p">(</span><span class="l">--t-font-size</span><span class="p">);</span>
	<span class="nl">color</span><span class="p">:</span> <span class="nf">var</span><span class="p">(</span><span class="l">--t-color</span><span class="p">);</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>

<p>And here’s the final result:</p>

<p class="codepen" data-height="450" data-pen-title="Popover API Toasts" data-default-tab="html,result" data-slug-hash="OPbxZjO" data-user="arfypup" style="height: 450px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border-radius: var(--radius); margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/arfypup/pen/OPbxZjO">
  Popover API Toasts</a> by arfypup (<a href="https://codepen.io/arfypup">@arfypup</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async="" src="https://public.codepenassets.com/embed/index.js"></script>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1">
      <p>most browsers released after January 2025: <a href="https://developer.mozilla.org/en-US/docs/Web/API/Popover_API">Popover API - MDN Web Docs</a> <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>arfypup</name></author><category term="guides" /><category term="webdev" /><category term="frontend" /><summary type="html"><![CDATA[So the new Popover API is now available in most1 browsers. It lets you easily create non-modal popovers. But what if we pushed it further than that? What if we could create toasts with zero libraries, using just the Popover API? That’s what this guide is about. most browsers released after January 2025: Popover API - MDN Web Docs &#8617;]]></summary></entry><entry><title type="html">Set Flatpak to use system cursor icons</title><link href="https://mystie.dev/2024/09/04/flatpak-system-icons/" rel="alternate" type="text/html" title="Set Flatpak to use system cursor icons" /><published>2024-09-04T20:03:33+02:00</published><updated>2024-09-04T20:03:33+02:00</updated><id>https://mystie.dev/2024/09/04/flatpak-system-icons</id><content type="html" xml:base="https://mystie.dev/2024/09/04/flatpak-system-icons/"><![CDATA[<h2 id="via-flatseal-gui">via flatseal gui</h2>

<p>download <a href="https://flathub.org/apps/com.github.tchx84.Flatseal">flatseal</a></p>

<p>in flatseal, go to <strong>All applications</strong>, scroll to <strong>Filesystem -&gt; Other files</strong>, click on the + folder icon, then add this into the text box:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/home/&lt;your username&gt;/.icons:ro
</code></pre></div></div>

<p>changes are saved automatically</p>

<h2 id="via-terminal">via terminal</h2>

<p>run this command:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>flatpak <span class="nt">--user</span> override <span class="nt">--filesystem</span><span class="o">=</span>/home/<span class="nv">$USER</span>/.icons/:ro
</code></pre></div></div>]]></content><author><name>arfypup</name></author><category term="guides" /><category term="flatpak" /><summary type="html"><![CDATA[Fix cursor icons being wrong in Flatpak]]></summary></entry></feed>