Troy Vassalotti | troyv.devTroy writes about web development and being a person.2024-03-22T00:00:00Zhttps://www.troyv.dev/Troy Vassalottifqa560de@anonaddy.meWebmentions Web Component2024-03-22T00:00:00Zhttps://www.troyv.dev/2024/03/22/webmentions-web-component/
<!-- @format -->
<p>I've been giving my site a fresh look recently. It mostly involved stylesheet updates with a touch of dependency management and site simplification, but I got overwhelmed by the unknowns in how my existing webmention system worked<sup class="footnote-ref"><a href="https://www.troyv.dev/2024/03/22/webmentions-web-component/#fn1" id="fnref1">[1]</a></sup> and wanted to do something that felt more comfortable.</p>
<p><a href="https://chrisburnell.com/eleventy-cache-webmentions/">Chris Burnell's</a> <code>eleventy-cache-webmentions</code> has been my plugin-of-choice for handling webmentions up to this point because it gave me exactly what I needed with little configuration necessary (thanks Chris!). But I wanted to try something different; something that I can build myself.</p>
<p>I also need to thank <a href="https://mxb.dev/">Max Böck</a> for inspiration as well from his own webmention setup.</p>
<p>As a full-time Web Component Maker, overhauling my webmention setup felt like a good time to make a new component; a component that other people can use on sites that <em>aren't</em> <a href="https://11ty.dev/">Eleventy</a>. Plus, it helps that a one-time Google search came up with no results for a Webmentions Web Component.</p>
<p>Converting a build-time process into a run-time process does have its downsides:</p>
<ul>
<li>I now maintain a new web component.</li>
<li>The API call for webmentions happens on the client. I haven't tried this yet, but it seems like a good use for the <a href="https://www.11ty.dev/docs/plugins/is-land/">Eleventy <code>is-land</code> component</a>.</li>
</ul>
<p>Though the upsides felt worth it:</p>
<ul>
<li>I get to create a new web component and learn something in the process.</li>
<li>Anyone can take this web component and put it on their site.</li>
</ul>
<p>The catch to all this is that I use <a href="https://webmention.io/">webmention.io</a>, so the code involved in my component is tightly coupled in some ways (more on that later).</p>
<p>The way it works is it makes an API call to the webmention.io API for the current URL, grabs any and all mentions, and renders them in-place in two possible variants: a <strong>feed</strong> or a <strong>facepile</strong>.</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>web-mentions</span> <span class="token attr-name">variant</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>feed (default) || facepile<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>web-mentions</span><span class="token punctuation">></span></span></code></pre>
<p>There are other options too:</p>
<ul>
<li>While the component by default has no styles and <em>doesn't use shadow DOM</em>, giving it the <code>loadstyles</code> attribute will adopt some starter styles to your document: <code><web-mentions loadstyles></web-mentions></code>.</li>
<li>Both the domain and page path are configurable so you can show webmentions of a fully different site. I used the <code>domain</code> property myself for local development since otherwise it attempts to fetch mentions on <code>localhost</code>: <code><web-mentions domain="https://example.com" path="some/path.html"></web-mentions></code>.</li>
<li>You can filter by the type of mentions you want to show, which means you can mix and match showing some types as a facepile and others in a feed:</li>
</ul>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>web-mentions</span>
<span class="token attr-name">variant</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>facepile<span class="token punctuation">"</span></span>
<span class="token attr-name">filters</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>likes<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>web-mentions</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>web-mentions</span>
<span class="token attr-name">variant</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>feed<span class="token punctuation">"</span></span>
<span class="token attr-name">filters</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>bookmarks, replies, reposts, mentions<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>web-mentions</span><span class="token punctuation">></span></span></code></pre>
<p>The component is not on <code>npm</code> yet, but you can take a look at it for yourself by <a href="https://www.troyv.dev/assets/js/web-mentions.js">viewing the source</a>. I consider this like a beta-of-sorts and if enough people find this useful then I'll expedite the process of publishing it as a package.</p>
<p>As I said, I use webmention.io so support for any other services is completely a mystery to me since I don't know them. I planned ahead somewhat by making the API service it's own attribute/property, but that doesn't change the actual object key's the component looks for in the data which are coupled to webmention.io.</p>
<p>At the time of publishing this post, it will have no webmentions, so <a href="https://www.troyv.dev/2022/07/28/redesign-2022/">look at this other post of mine</a> to see it in action.</p>
<p>What do you think? Love it? Hate it? Let me know!</p>
<hr class="footnotes-sep" />
<section class="footnotes">
<ol class="footnotes-list">
<li id="fn1" class="footnote-item"><p>This isn't surprising considering webmentions as a whole are not a straightforward system. <a href="https://www.troyv.dev/2024/03/22/webmentions-web-component/#fnref1" class="footnote-backref">↩︎</a></p>
</li>
</ol>
</section>
<a rel="syndication noreferrer" class="u-syndication" href="https://brid.gy/publish/mastodon"></a>Physical Media2023-12-26T00:00:00Zhttps://www.troyv.dev/2023/12/26/physical-media/
<!-- @format -->
<p>CDs, vinyl records, DVDs, VHS tapes, video games. Blockbuster, Best Buy, your local video store.</p>
<p>I miss physical media. I don't personally want to own items of each format I listed above; I have long stopped owning CDs in favor of vinyl (hopefully with an accompanying digital download) or purchases from Bandcamp. I'm moreso talking about the industry and culture of physical media, of collections that can be made unique or rare, tangible, not on-demand.</p>
<p>Music streaming has made listening to music as a whole more accessible to the globe. You can listen to almost every piece of music from anywhere on your preferred streaming service. Small artists <a href="https://www.frontroyalband.com/">like myself</a> can reach audiences easier than ever before. But what has streaming caused us to miss out on?</p>
<ul>
<li><strong>Deluxe editions and bonus tracks</strong>: There's no point to calling anything a bonus track when it is readily available on Spotify under the "deluxe" release which sits directly next to the "standard" release in that artist's discography.</li>
<li><strong>Region-specific tracks</strong>: I remember scouring the internet - legally, of couse - for the Japan-exclusive songs from Less Than Jake, Zebrahead, and the like. Now what tends to happen is an album from one of your favorite bands can suddenly be "unavailable in your country" on streaming even when it was previously in your library, playable.</li>
<li><strong>Ownership</strong>: You don't have to look far to see why <a href="https://www.theverge.com/2023/12/5/23989290/playstation-digital-ownership-sucks">digital ownership sucks</a>.</li>
</ul>
<p>I mentioned being able to find "almost every piece of music" online because there's a benefit of having collected my music library my entire life in a series of hard drives and cloud providers: I have entire discographies from <a href="https://www.thisisa.band/lower-lands">bands that no longer exist</a> (or existed before streaming was readily available) where you can't find their music anywhere save a YouTube video or two. I cherish this collection and freedom to own my music without limits.</p>
<p>I'm not a movie person which means DVDs and such don't apply to me, but I enjoyed listening to <a href="https://changelog.com/friends/16">this episode of Changelog</a> where they go into more detail about why physical media matters in the movie industry.</p>
<a rel="syndication noreferrer" class="u-syndication" href="https://brid.gy/publish/mastodon"></a>How to Fix Eleventy Serverless Functions on Netlify2023-10-03T00:00:00Zhttps://www.troyv.dev/2023/10/03/how-to-fix-eleventy-serverless-functions-on-netlify/
<!-- @format -->
<p>I started receiving this error on my site when trying to view a page generated with an Eleventy Serverless function:</p>
<pre class="language-shell"><code class="language-shell">Error - Cannot <span class="token function">find</span> module <span class="token string">'/var/task/netlify/functions/teapot/eleventy-serverless-map.json'</span> Require stack: - /var/task/node_modules/@11ty/eleventy/src/Serverless.js - /var/task/node_modules/@11ty/eleventy/src/Eleventy.js - /var/task/netlify/functions/teapot/index.js - /var/task/teapot.js - /var/runtime/index.mjs</code></pre>
<p>It's referencing a specific function of mine - <code>teapot</code> - but the general message is there: Netlify couldn't find something and thus it broke. This error impacted both my serverless pages.</p>
<p>I hadn't made any changes to my site in a few weeks because life has been busy, so I know it couldn't have been triggered on my end as a result of bad code or an upgrade or anything.</p>
<p>So, I checked the Eleventy Discord server to see if someone else has seen this. It turns out people have <strong>and</strong> there's a solution! Add the following snippet to your <code>netlify.toml</code> configuration file:</p>
<pre class="language-toml"><code class="language-toml"><span class="token punctuation">[</span><span class="token table class-name">functions</span><span class="token punctuation">]</span>
<span class="token comment"># Directory could be different for you</span>
<span class="token key property">directory</span> <span class="token punctuation">=</span> <span class="token string">"netlify/functions/"</span>
<span class="token key property">node_bundler</span> <span class="token punctuation">=</span> <span class="token string">"zisi"</span></code></pre>
<p>I don't know what <code>zisi</code> is but adding that line fixed by functions.</p>
<a rel="syndication noreferrer" class="u-syndication" href="https://brid.gy/publish/mastodon"></a>TroyGPT2023-04-30T00:00:00Zhttps://www.troyv.dev/2023/04/30/troygpt/
<!-- @format -->
<p>Need a blog post <em><strong>right now</strong></em>? Look no further! I made a web component called <a href="https://github.com/troyvassalotti/word-salad"><code>word-salad</code></a>.</p>
<p>The way it works is you give it a set of words and it gives you a new set of words. You can ask it for single words mashed up into a mess, or full sentences generated from single words to create a multi-mess.</p>
<p>I'm here to help you write your next blog post in <em>my voice</em> as <a href="https://www.troyv.dev/blog-maker">I've already supplied</a> <code>word-salad</code> with every word from all my blogs. Is it useful? Absolutely not. Does it write anything that sounds at all coherent? You wish. Will it almost certainly spit out Nunjucks code and a few stray HTML artifacts? You bet, and that's because I don't know regular expressions well enough to replace all that stuff.</p>
<p>Isn't AI great?</p>
<a rel="syndication noreferrer" class="u-syndication" href="https://brid.gy/publish/mastodon"></a>Website Cheatcodes2023-04-22T00:00:00Zhttps://www.troyv.dev/2023/04/22/website-cheatcodes/
<!-- @format -->
<p>The internet, am I right? It's a scary place full of terror, ads, and people you wish would log off every once in a while. Where did all the fun go?</p>
<p>I've been seeing personal blogs get redesigned with an old school aesthetic, embracing the flashy graphics of a web long forgotten, and I want that for this here website. I'm not a super talented graphic designer though, but maybe that's okay if I want visitors to feel like they've just been transported back to a time when <a href="https://greatist.com/connect/friendships-were-easier-with-aim">AIM status messages</a> meant something.</p>
<p>There is something I <em>can</em> do though, and that's give my website cheat codes.</p>
<p>What does that mean? I'm glad you asked!</p>
<p>Cheat codes is a bit of javascript <a href="https://github.com/troyvassalotti/cheatcodes">you can import</a> on your site to listen to events that will trigger the prize: a stream of confetti cannons.</p>
<p>If you're reading this post on my site (not in your RSS reader), have you tried the Konami code? <em>hint hint</em>.</p>
<p>Oh hey, and if you have a Guitar Hero guitar lying around, plug it in. Try this out: <strong>Green, Red, Green, Yellow, Green, Blue, Green, Orange, Tilt</strong>. Star Power!</p>
<h2 id="how-does-it-work%3F" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2023/04/22/website-cheatcodes/#how-does-it-work%3F">How Does It Work?</a></h2>
<p>Cheat codes is a simple <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes">class</a> that can be configured to listen for a specific pattern, with a time limit for how quickly you need to enter it, a duration for the confetti cannons to fire, and customizable confetti options. Possible listeners are key presses and gamepads.</p>
<p>As you press keys (or buttons on your gamepad), each one is stored in an array that resets itself after the time limit. The codes in the array must match the pattern being listened for in order for the confetti to launch.</p>
<blockquote>
<p>For now, it does only support <a href="https://www.npmjs.com/package/canvas-confetti"><code>canvas-confetti</code></a> but contributions are welcome to extend the prize to other visual effects.</p>
</blockquote>
<p>The default cheat code is the Konami code and it's a few lines of code away from being activated on your site.</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">import</span> CheatCode <span class="token keyword">from</span> <span class="token string">"@troyv/cheatcodes"</span><span class="token punctuation">;</span>
<span class="token keyword">new</span> <span class="token class-name">CheatCode</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">listen</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Konami code is ready!</span></code></pre>
<p>The arguments list goes like this:</p>
<ol>
<li>A specified pattern to listen for, such as "a b c d" or "1 2 3 4".</li>
<li>Type of listener - "keyboard" or "gamepad".</li>
<li>The time limit before the current input is reset and you need to start over.</li>
<li>How long (in ms) the confetti cannons last.</li>
<li>Custom confetti options if you wish to change the default effect.</li>
</ol>
<p>I mentioned my site uses both the Konami code and a Guitar Hero pattern at this time. This is what it looks like to hook up a Guitar Hero (gamepad) code:</p>
<pre class="language-js"><code class="language-js"><span class="token comment">// Green, Red, Green, Yellow, Green, Blue, Green, Orange, Tilt</span>
<span class="token keyword">new</span> <span class="token class-name">CheatCode</span><span class="token punctuation">(</span><span class="token string">"7 1 7 0 7 2 7 3 6"</span><span class="token punctuation">,</span> <span class="token string">"gamepad"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">listen</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>I listed the mappings for both a PS2 controller as well as a guitar controller <a href="https://github.com/troyvassalotti/cheatcodes/blob/main/src/enums.ts">in the repo</a>.</p>
<p>What do you think? Are you going to try it out yourself? Let me know! Let's make the web fun together.</p>
<a rel="syndication noreferrer" class="u-syndication" href="https://brid.gy/publish/mastodon"></a>Simultaneous Drinking2023-04-18T00:00:00Zhttps://www.troyv.dev/2023/04/18/simultaneous-drinking/
<!-- @format -->
<p>Think about the last time you were out with friends or family and there were drinks in front of each of you. When slight breaks in a conversation happened, did you all reach for and take a drink at the same time in that moment of stillness? It's possible the venn diagram for <em>people reading this post</em> and <em>people who have noticed this</em> is two separate circles.</p>
<p>I imagine we instinctually take that time to collect our thoughts or attempt to transition topics so you don't encounter awkward silence. If the latter is the case, I also believe maybe awkward silences aren't that bad.</p>
<a rel="syndication noreferrer" class="u-syndication" href="https://brid.gy/publish/mastodon"></a>I Made a Headache Tracker Starter Kit2023-02-16T00:00:00Zhttps://www.troyv.dev/2023/02/16/i-made-a-headache-tracker-starter-kit/
<!-- @format -->
<p>I've been getting migraines my whole life, but only started keeping a regular log of them in 2019. The process went through many iterations: from as a spreadsheet, to tracked in Migraine Buddy, to a JSON file on a single HTML page, to Markdown files in Obsidian, to now Markdown files in a repository hooked up to <a href="https://www.vitejs.dev/">Vite</a>, <a href="https://www.lit.dev/">Lit</a>, and data visualization libraries.</p>
<p>I'm calling it <a href="https://github.com/troyvassalotti/aura-log"><strong>Aura Log</strong></a>, named after the <a href="https://www.mayoclinic.org/diseases-conditions/migraine-with-aura/symptoms-causes/syc-20352072">aura</a> associated with migraines like mine. Key features include:</p>
<ul>
<li>Vite, web components, and cool charts.</li>
<li>Streamlined logging process through <code>plop</code>.</li>
<li>(almost) Full customization in a single config file.</li>
</ul>
<p>The idea to open it up came last weekend after a conversation at work, and I thought I might make it an <code>npm</code> package with binaries and all the bells and whistles of a mini framework... then I realized I <a href="https://daverupert.com/2023/01/so-you-want-to-make-a-new-js-framework/">don't want to make a framework</a>. I settled on this config file-based starter kit because it is simpler, and people could fork it and extend/customize it all they want.</p>
<p>The process looks something like this:</p>
<ol>
<li>Fork and clone the repo.</li>
<li><code>npm install</code>.</li>
<li><code>npm run log</code> starts an interactive CLI to create a new entry (Markdown file) by answering a short prompts.</li>
<li>That file is saved to a folder that Vite reads to parse the front matter which all together make up the global data store.</li>
<li><code>npm start</code> goes through the steps of reading, transforming, and rendering an <code>index.html</code> with web components of <a href="https://d3js.org/">D3</a> and <a href="https://www.highcharts.com/">Highcharts</a>.</li>
</ol>
<p>The components are fairly restrictive, but that's only because they fit exactly my needs only. Anyone can feel free to fork and customize as they see fit.</p>
<p><a href="https://troyvassalotti.github.io/aura-log/">Check it out</a>!</p>
<a rel="syndication noreferrer" class="u-syndication" href="https://brid.gy/publish/mastodon"></a>How My "Now Playing" Feature Works2023-02-05T00:00:00Zhttps://www.troyv.dev/2023/02/05/how-my-now-playing-feature-works/
<!-- @format -->
<p>I mentioned <a href="https://www.troyv.dev/2022/07/28/redesign-2022/#see-what's-playing-now">in a previous post</a> how I display any song I'm currently listening to on my site<sup class="footnote-ref"><a href="https://www.troyv.dev/2023/02/05/how-my-now-playing-feature-works/#fn1" id="fnref1">[1]</a></sup>, but I want to set the record straight about something in particluar with that feature.</p>
<blockquote>
<p>TL;DR I fetch data from the ListenBrainz API, but that might not mean the song you see is a song I'm choosing to hear.</p>
</blockquote>
<p>Everything I've ever listened to since 2021 (that I've been able to scrobble) is sent to <a href="https://listenbrainz.org/user/actionhamilton/">ListenBrainz</a>. They provide a simple API for sending, pulling, and even deleting listens. That API is how the various browser extensions and apps I use scrobble what I'm listening to. An app called Pano Scrobbler is specifically what scrobbles listens from my phone. The app looks out for the media notification on my Google Pixel for media coming from any apps I select - Deezer, Pocketcasts, YouTube, etc. - and can also use the phone's Now Playing feature as a source.</p>
<p>Pixels have what is essentially an always-on Shazam (yeah, a little creepy, but it's a sacrifice I make in the name of logging my listening habits). It can tell me what song I'm playing from my laptop if the volume is loud enough, but it can also tell me what song is playing at the gym or my local coffee shop. I love that because it provides insight into the music I hear <strong>passively</strong>, but the catch is that at any moment in time someone can visit my website and think I'm <em>actually choosing</em> to listen to Imagine Dragons.</p>
<p>Next time you see what's playing, consider for a moment that I might not be aware of the song I'm hearing (unless that song makes me exceptionally cooler, in which case I'm definitely choosing to play it).</p>
<hr class="footnotes-sep" />
<section class="footnotes">
<ol class="footnotes-list">
<li id="fn1" class="footnote-item"><p>It used to be done with edge functions, but I created a web component instead after seeing <a href="https://andy-bell.co.uk/">Andy Bell's version</a>. <a href="https://www.troyv.dev/2023/02/05/how-my-now-playing-feature-works/#fnref1" class="footnote-backref">↩︎</a></p>
</li>
</ol>
</section>
<a rel="syndication noreferrer" class="u-syndication" href="https://brid.gy/publish/mastodon"></a>My 2022 Year In Music2023-01-14T00:00:00Zhttps://www.troyv.dev/2023/01/14/my-2022-year-in-music/
<!-- @format -->
<p>I'm finally getting around to reviewing my <a href="https://listenbrainz.org/user/actionhamilton/year-in-music/2022">ListenBrainz 2022 Year In Music</a> and - to no one's surprise - it contains embarrassing results.</p>
<p>If you weren't aware, I was in a musical rut and thought it would revitalize my relationship with music by listening to <a href="https://www.thisisa.band/limp-bizkit/">bands I purposefully avoided</a>. This meant I spent months listening to the entire discographies of bands like Limp Bizkit, Korn, Pearl Jam, and more.</p>
<p>That experiment was initially aimed at bands I didn't think I'd like, but turned into bands I did like enough but wanted a deeper understanding of. This resulted in a year in music dominated by those experimented-on bands.</p>
<p><img src="https://res.cloudinary.com/dpmchqezv/image/upload/c_scale,f_auto,q_auto:eco/v1646349103/blog/actionhamilton-top-albums-2022_x1cdpi.webp" alt="A grid of my top nine albums, including Vasudeva, Korn, Limp Bizkit, Edamame, Their They're There, Taking Meds, Young Legionnaire, and Jawbox" /></p>
<p>My top nine albums were:</p>
<ol>
<li>Vasudeva - <em>Generator</em></li>
<li>Korn - <em>Issues</em></li>
<li>Limp Bizkit - <em>Still Sucks</em></li>
<li>Edamame - <em>Periderm</em></li>
<li>Their / They're / There - <em>Their / They're / Three</em></li>
<li>Taking Meds - <em>Terrible News From Wonderful Men</em></li>
<li>Young Legionnaire - <em>Zero Worship</em></li>
<li>Limp Bizkit - <em>Significant Other</em></li>
<li>Jawbox - <em>Jawbox</em></li>
</ol>
<blockquote>
<p>Note: I chose the top nine because that's how many albums fit on the image above.</p>
</blockquote>
<p>Among that list, the Taking Meds, Their / They're / There, and Jawbox albums were my favorites by enjoyment. The Vasudeva and Edamame albums are perfect instrumental jams though, so check those out if you're into that sort of thing.</p>
<p>My top 10 artists are a slightly different story based on me listening to them a whole lot but not any particular album frequently enough.</p>
<p><img src="https://res.cloudinary.com/dpmchqezv/image/upload/c_scale,f_auto,q_auto:eco/v1646349103/blog/actionhamilton-top-artists-2022_oemttc.webp" alt="A list of my top 10 artists, including Korn, Limp Bizkit, Jimmy Eat World, Less Than Jake, Pearl Jam, Lower Than Atlantis, Bloc Party, Norma Jean, Jawbreaker, Edamame" /></p>
<p>This list is influenced by me reading <a href="https://www.danozzi.com/books/sellout">Dan Ozzi's <em>Sellout</em></a> as you can tell by the Jimmy Eat World and Jawbreaker inclusions.</p>
<p>Other fun stats include Wednesdays being my most music-filled day, and listening to 1319 different artists. That last stat might be so high because I scrobble all my <em>passive</em> listens too; if my phone detected a song while at the gym or the store, it would be scrobbled. I like this feature because it gives me insight into how much different music is around me without me actively knowing.</p>
<p>Did your 2022 year in music line up with mine at all? Let me know!</p>
<a rel="syndication noreferrer" class="u-syndication" href="https://brid.gy/publish/mastodon"></a>Being Filmed In Public2023-01-09T00:00:00Zhttps://www.troyv.dev/2023/01/09/being-filmed-in-public/
<!-- @format -->
<p>I read this article from The Verge - <a href="https://www.theverge.com/2022/12/26/23519605/tiktok-viral-videos-privacy-surveillance-street-interviews-vlogs">"Please don't film me in 2023"</a> - and would also like to not be filmed for content without my consent.</p>
<p>In one of the examples, it took a creepier path where being filmed and posted online unknowingly about something innocuous led to a <strong>stranger</strong> online identifying and notifying the subject.</p>
<p>This quote from the article compares the TikTok format to other recognizable examples:</p>
<blockquote>
<p>The man-on-the-street genre is a well-worn format — before Billy Eichner was writing and starring in movies, he was bothering normal, unsuspecting people about La La Land. Journalists have long used the form to get first-hand accounts and opinions for news hits. In the case of more professional operations, there’s likely at least some level of getting permission, whether that’s having subjects sign release forms or identifying clearly who’s filming and why. In the case of random TikTok creators, it’s clear the level of consent and notice runs the gamut.</p>
</blockquote>
<p>One of my favorite shows is <a href="https://www.trutv.com/shows/impractical-jokers">Impractical Jokers</a>. This topic of making strangers uncomfortable for entertainment comes up when discussing the show and I'll admit I don't know the ins and outs of TV show production. But I like to think there's still more care being taken to making sure permission is granted and it's not done in ill will than the average viral video of the same format.</p>
<a rel="syndication noreferrer" class="u-syndication" href="https://brid.gy/publish/mastodon"></a>Error is : 04-B02022-12-30T00:00:00Zhttps://www.troyv.dev/2022/12/30/error-is-04-b0/
<!-- @format -->
<p>As seen on a bike at the gym:</p>
<blockquote>
<p>Error is : 04-B0</p>
</blockquote>
<p>I searched the internet for this code plus keywords related to workout bikes and got <em>nothing</em>. I didn't manage to get a picture of the bike this time, but I hope someone out there knows immediately what I'm talking about and can let me know what this means.</p>
<a rel="syndication noreferrer" class="u-syndication" href="https://brid.gy/publish/mastodon"></a>Thoughts on Blogging2022-12-08T00:00:00Zhttps://www.troyv.dev/2022/12/08/thoughts-on-blogging/
<!-- @format -->
<p>I tend to get hung up on creating the perfect blogging experience. Like "oh, a CMS is designed for that, so I should set one up." But that's a lot of work. More work than I ultimately care to take on.</p>
<p>I can continue on with my current workflow of using markdown, but sometimes I do need HTML that isn't covered by the spec. Luckily, markdown accepts HTML! But then it's no longer in the perfect, human-readable text file format.</p>
<p>A CMS doesn't solve that problem though. A WordPress post is compromised of blocks and you can see visually how those blocks will render; It's all still HTML in the end. I can install markdown plugins in there, but it's all still WordPress blocks which are still HTML. Even then, it's all WP-specific. I can't open up Notepad and write WP blocks. At least, not at all efficiently.</p>
<p>At this point I'm wondering if what I'm chasing doesn't exist? The intended target of the raw post isn't a human - it's a <strong>web browser</strong>. The human is meant to read the final product on the <strong>website</strong>. As such, it shouldn't matter if I write a blog post that looks like a mashup of plain text, formatted text, and HTML... Right?</p>
<p>Letting go of the idea that my post needs to be the most efficient and readable thing is hard, but I am realizing it's fine if I don't have a CMS with all the fancy features, or if my markdown has code in it. What matters is I use the tools at my disposal to craft a piece of work I'm proud of.</p>
<a rel="syndication noreferrer" class="u-syndication" href="https://brid.gy/publish/mastodon"></a>Embracing The Indie Web2022-12-04T00:00:00Zhttps://www.troyv.dev/2022/12/04/embracing-the-indie-web/
<!-- @format -->
<p>The tinkerer in me - the same one who enjoys spending hours of his weekend trying out a fancy new framework even if he knows he'll never really use it - has latched onto the idea of embracing the <a href="https://indieweb.org/">indie web</a>.</p>
<p>It's a lovely concept/movement/way of life, but it isn't without its complications. You can't blame it much for not being too user friendly. The fact of the matter is there's (currently) a lot of technical knowhow required to properly <a href="https://indieweb.org/POSSE">publish on your own site and syndicate elsewhere</a>.</p>
<p>It wasn't too long ago that I added webmentions to my blog, and even more recently that I accidentally broke them by deleting (for obvious reasons) my Twitter account. I'm now left with figuring out how to properly start over from the Fediverse.</p>
<p><a href="https://brid.gy/">Bridgy</a> is a cool tool I used to publish my posts to Twitter that I'll be diving deeper into now. You can even interact with GitHub on it! That's wild. I can't even imagine creating issues on repos through a post on my site, but you can bet I'm going to try.</p>
<blockquote>
<p>This very post is going to be a test of publishing to Mastodon using Bridgy, so if you <a href="https://fosstodon.org/@rest">follow me</a> over there then you'll know if it works.</p>
</blockquote>
<p>The technical knowledge isn't the only barrier here. I'm confident I can figure this all out, but the time and effort it asks of me is hard to balance, let alone for someone who doesn't hold the web as a career or hobby.</p>
<p>A CMS like WordPress can go a long way in helping the average blog owner embrace the indie web and I hope that continues to get better, but I don't use a CMS so I'm building it all from scratch. I'm writing this post from my phone as an experiment with a mobile non-CMS blog-on-the-go solution. Every few weeks I get the idea that I'll move my content to a headless CMS but that is another time suck I'm not interested in starting.</p>
Free Podcast Idea No. 12022-12-03T00:00:00Zhttps://www.troyv.dev/2022/12/03/free-podcast-idea-no-1/
<!-- @format -->
<p>"This Is Why We Don't Have A Podcast": a host talks to a different acquaintance every week for an hour on a podcast with no format. Together, they (and the audience) learn over this hour why they haven't already started a podcast through the awkwardness of talking to someone you don't know very well.</p>
<p>This assumes the two of you have known of each other for enough time that it's safe to say you'd already have been friends by now if it was meant to be.</p>
Practicing Restraint2022-10-09T00:00:00Zhttps://www.troyv.dev/2022/10/09/practicing-restraint/
<!-- @format -->
<p>I decided to try and refactor <a href="https://github.com/troyvassalotti/plvylist">Plvylist</a> using <a href="https://lit.dev/">Lit</a> the other day. This is the second time I've attempted such a project. The first time was a mistake because I had no prior experience using Lit and I barely understood how my own web component worked to begin with. I've been working with Lit <em>a lot</em> at my current position at TRP, and with time to kill waiting for a flight home I figured I'd make another attempt. "How hard could it be?" I thought. It turns out, still as hard as it was before. Trying to transition this component into Lit truly shines light on a lot of underlying problems with its structure.</p>
<p>On one hand, I view those problems as good problems to have since the nuances and errors show me how fragile the component is and force me to rethink how data is handled and passed. On the other hand, it feeds this inner desire of mine to fix everything at once and immediately. I have a hard time of putting things down when I'm so intent on finishing them. It's a legitimate problem and I'm trying to be aware of it when it happens. This project is one where I'm aware of a few things now:</p>
<ol>
<li>How long it will take me: <em>too long</em>,</li>
<li>The level of effort: <em>too much</em>,</li>
<li>The impact it will have: <em>minimal</em>.</li>
</ol>
<p>Changing Plvylist like this serves a single person: me. Even then, it doesn't solve any real problems I have. I'm hoping that by writing this down, I can suppress that obession to finish what I started and convince myself it isn't worth the time and effort.</p>
1942 Data Error2022-09-17T00:00:00Zhttps://www.troyv.dev/2022/09/17/1942-data-error/
<!-- @format -->
<p>We stayed in a house at the beach this past week as part of the family's yearly vacation. This year's house had both a theater and arcade room, and I'm here to talk about an error message I saw displayed on one of its arcade machines.</p>
<blockquote>
<p>1942 Data Error</p>
</blockquote>
<p><img src="https://res.cloudinary.com/dpmchqezv/image/upload/c_scale,f_auto,q_auto:eco/v1646349103/blog/1942_c0u9hv.webp" alt="An arcade machine showing the text "1942 Data Error"" /></p>
<p>That's all it said. I did the first thing anyone in my position would have done: took to DuckDuckGo for the answer. You know what I found? <strong>Nothing</strong>. References to <em>Battlefield: 1942</em> - a completely separate game, sure - but not what "1942 Data Error" means for this particular machine.</p>
<p>Then it dawned on me how niche of a situation I found myself in: A vintage arcade machine encountering an error that the internet has not seemed to document and that only other renters of this house will stumble upon, who will presumably reach the same conclusion I have.</p>
<p>Someone out there knows what it means, and that person has probably spent years studying arcade machines and could tell me everything I need to know - including how to fix it - as if I asked them what color the sky is.</p>
<p>If you're that person, or you know that person, I'm waiting for your email.</p>
<p>P.S. Here's another machine's friendly "WINNERS DON'T USE DRUGS" message from the FBI.</p>
<p><img src="https://res.cloudinary.com/dpmchqezv/image/upload/c_scale,f_auto,q_auto:eco/v1646349103/blog/dont_use_drugs_gzylzg.webp" alt="An arcade machine showing the text "WINNERS DON'T USE DRUGS" - William S. Sessions, Director, FBI" /></p>
Redesign 20222022-07-28T00:00:00Zhttps://www.troyv.dev/2022/07/28/redesign-2022/
<!-- @format -->
<p>Websites are hard.</p>
<p><em>Roll credits</em>.</p>
<p>I started this site in... (checks git history) I don't know, a few years ago.<sup class="footnote-ref"><a href="https://www.troyv.dev/2022/07/28/redesign-2022/#fn1" id="fnref1">[1]</a></sup> It's nice to think back on the initial designs and see how far I've come. The CSS, the performance, the <strong>fonts</strong>! Oh, the decisions a young, aspiring developer makes.</p>
<p>In 2020, I landed on the previous iteration of the site: a dark, straightforward blog focused heavily on optimized code. It suffered from a lack of clear vision or goal, and over time complexity got the better of it. That complexity wasn't so much seen from a viewer's perspective, but I was feeling the quirks as the maintainer.</p>
<p>My skills grew exponentially<sup class="footnote-ref"><a href="https://www.troyv.dev/2022/07/28/redesign-2022/#fn2" id="fnref2">[2]</a></sup> since then, what with me diving deeper into <a href="https://www.11ty.dev/">Eleventy's</a> features, building a few web components, and broadening my knowledge of how Node even works. It's only natural that I take those learnings and apply them to this site.</p>
<h2 id="the-move-towards-simplicity" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2022/07/28/redesign-2022/#the-move-towards-simplicity">The Move Towards Simplicity</a></h2>
<h3 id="bloated-code" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2022/07/28/redesign-2022/#bloated-code">Bloated Code</a></h3>
<p>When I learned how collections work in 11ty, I decided to make some non-post layouts and templates revolving around tags, such as "work" or "projects." I was able to create some cool features with that, but it also meant I was adding more levels of management to the code. It also meant I was trying to shoehorn content into categories they had no right to be in.</p>
<h3 id="nunjucks" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2022/07/28/redesign-2022/#nunjucks">Nunjucks</a></h3>
<p>A while back I realized I could make my own attempt at <a href="https://www.troyv.dev/2021/11/13/single-file-components-in-nunjucks/">single file components</a> in Nunjucks. Well, I shortly afterwards realized I was making a built-in feature more complicated than it needed to be in the name of <em>performance optimization</em>. I rolled back that idea and decided to use Nunjucks' <code>extends</code> and <code>block</code> as they were (more correctly) intended.</p>
<h3 id="excess-domains" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2022/07/28/redesign-2022/#excess-domains">Excess Domains</a></h3>
<p>I also had a few one-off Netlify sites lying around that didn't need to be alone, so I consolidated them onto this domain<sup class="footnote-ref"><a href="https://www.troyv.dev/2022/07/28/redesign-2022/#fn3" id="fnref3">[3]</a></sup>.</p>
<h3 id="simplicity-by-abstraction" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2022/07/28/redesign-2022/#simplicity-by-abstraction">Simplicity By Abstraction</a></h3>
<p>My <code>.eleventy.js</code> config file was getting bulky, so I moved my collections, filters, plugins, shortcodes, and transforms into their own files<sup class="footnote-ref"><a href="https://www.troyv.dev/2022/07/28/redesign-2022/#fn4" id="fnref4">[4]</a></sup> under a <code>utils</code> directory. It looks something like this when utilized:</p>
<pre class="language-js"><code class="language-js"><span class="token comment">// Plugins</span>
Object<span class="token punctuation">.</span><span class="token function">keys</span><span class="token punctuation">(</span>plugins<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">plugin</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
eleventyConfig<span class="token punctuation">.</span><span class="token function">addPlugin</span><span class="token punctuation">(</span>plugins<span class="token punctuation">[</span>plugin<span class="token punctuation">]</span><span class="token punctuation">.</span>name<span class="token punctuation">,</span> plugins<span class="token punctuation">[</span>plugin<span class="token punctuation">]</span><span class="token operator">?.</span>options<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// Filters</span>
Object<span class="token punctuation">.</span><span class="token function">keys</span><span class="token punctuation">(</span>filters<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">filterName</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
eleventyConfig<span class="token punctuation">.</span><span class="token function">addFilter</span><span class="token punctuation">(</span>filterName<span class="token punctuation">,</span> filters<span class="token punctuation">[</span>filterName<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// Collections</span>
Object<span class="token punctuation">.</span><span class="token function">keys</span><span class="token punctuation">(</span>collections<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">collectionName</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
eleventyConfig<span class="token punctuation">.</span><span class="token function">addCollection</span><span class="token punctuation">(</span>collectionName<span class="token punctuation">,</span> collections<span class="token punctuation">[</span>collectionName<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// Transforms</span>
Object<span class="token punctuation">.</span><span class="token function">keys</span><span class="token punctuation">(</span>transforms<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">transformName</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
eleventyConfig<span class="token punctuation">.</span><span class="token function">addTransform</span><span class="token punctuation">(</span>transformName<span class="token punctuation">,</span> transforms<span class="token punctuation">[</span>transformName<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<h2 id="web-components" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2022/07/28/redesign-2022/#web-components">Web Components</a></h2>
<p>Web components are rad as heck, so I'm using them all over the place. Inspect this site's code and see how many you can find!</p>
<p>I was initially worried about adding too many web components since this is a static site after all, but convinced myself (with the help of <a href="https://shoptalkshow.com/511/#t=07:48">ShopTalk Show</a>) the enjoyment/benefit in making/using them outweighs the potential client-side/back end overhead. I'm a developer and part of this site's purpose is to show what I can do, so why restrict myself from using JavaScript (when applicable) on my own website?</p>
<h2 id="focus-on-posts" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2022/07/28/redesign-2022/#focus-on-posts">Focus on Posts</a></h2>
<p><a href="https://www.w3.org/Provider/Style/URI.html">Cool URIs don't change</a>, so I changed some URIs in an attempt to make them cool. Posts were previously at the route <code>posts/title-of-post</code> but now they live at <code>year/month/day/title-of-post</code>. Pretty cool, right?</p>
<p>Writing is great, and I believe in the power of learning in public, so I wanted this <em>blog</em> to look more like a <em>blog</em>. I now generate tag pages<sup class="footnote-ref"><a href="https://www.troyv.dev/2022/07/28/redesign-2022/#fn5" id="fnref5">[5]</a></sup> and altered the design to bring posts to the forefront a bit more.</p>
<h2 id="serverless-and-edge-functions" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2022/07/28/redesign-2022/#serverless-and-edge-functions">Serverless and Edge Functions</a></h2>
<p>I'm aware of the irony in writing an entire section on simplicity while also embracing serverless and edge functions. I couldn't help myself from trying something new! Besides, I feel as though my use cases for each are worth it.</p>
<h3 id="see-what's-playing-now" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2022/07/28/redesign-2022/#see-what's-playing-now">See What's Playing Now</a></h3>
<p>If you check out my <a href="https://www.troyv.dev/music/">music pages</a>, you'll notice a few things:</p>
<ol>
<li>A web component that plays my music (unrelated to serverless or edge, but worth a callout)</li>
<li>A block of text above the H1 that tells you what I'm currently listening to, if anything.</li>
<li>A <a href="https://www.troyv.dev/music/stats/">series of data</a> from my <a href="https://listenbrainz.org/user/actionhamilton/">ListenBrainz</a> account where I scrobble all my listening habits to.</li>
</ol>
<p>To see my now playing, I make a request to ListenBrainz <em>on the edge</em> to check if it's receiving anything<sup class="footnote-ref"><a href="https://www.troyv.dev/2022/07/28/redesign-2022/#fn6" id="fnref6">[6]</a></sup>. If you happen to visit that page while I'm in the middle of a song or podcast, you'll know what it is!</p>
<p>Edge functions aren't the simplest of things coming from someone who's never done anything like it, but the <a href="https://www.11ty.dev/docs/plugins/edge/">11ty edge</a> plugin helped me get there.</p>
<p>A call to the ListenBrainz API returns a JSON payload of track data. The <code>now playing</code> data is slightly different from other track and artist data, so it needs a specific function for parsing:</p>
<pre class="language-js"><code class="language-js"><span class="token comment">// netlify/edge-functions/now-playing.js</span>
<span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">getNowPlaying</span><span class="token punctuation">(</span><span class="token parameter">api<span class="token punctuation">,</span> auth</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">try</span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> response <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">fetch</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>api<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">/user/actionhamilton/playing-now</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">,</span> <span class="token punctuation">{</span>
<span class="token literal-property property">headers</span><span class="token operator">:</span> auth<span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> data <span class="token operator">=</span> <span class="token keyword">await</span> response<span class="token punctuation">.</span><span class="token function">json</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> <span class="token punctuation">{</span>payload<span class="token punctuation">}</span> <span class="token operator">=</span> data<span class="token punctuation">;</span>
<span class="token keyword">const</span> metadata <span class="token operator">=</span> payload<span class="token punctuation">.</span>listens<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">.</span>track_metadata<span class="token punctuation">;</span>
<span class="token keyword">const</span> <span class="token punctuation">{</span>
<span class="token literal-property property">artist_name</span><span class="token operator">:</span> artist<span class="token punctuation">,</span>
<span class="token literal-property property">track_name</span><span class="token operator">:</span> song<span class="token punctuation">,</span>
<span class="token literal-property property">release_name</span><span class="token operator">:</span> release<span class="token punctuation">,</span>
<span class="token punctuation">}</span> <span class="token operator">=</span> metadata<span class="token punctuation">;</span>
<span class="token keyword">return</span> <span class="token punctuation">{</span>artist<span class="token punctuation">,</span> song<span class="token punctuation">,</span> release<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span>error<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token boolean">false</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>Data fetched like this needs to be added to the data cascade, so I had to remember the following lines in the <code>export default</code> function:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> nowPlaying <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">getNowPlaying</span><span class="token punctuation">(</span>listenBrainzEndpoint<span class="token punctuation">,</span> headers<span class="token punctuation">)</span><span class="token punctuation">;</span>
edge<span class="token punctuation">.</span><span class="token function">config</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">eleventyConfig</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
eleventyConfig<span class="token punctuation">.</span><span class="token function">addGlobalData</span><span class="token punctuation">(</span><span class="token string">"nowPlaying"</span><span class="token punctuation">,</span> nowPlaying<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>In the template, that data can be accessed in Nunjucks as normal data.</p>
<pre class="language-twig"><code class="language-twig">
<span class="token twig language-twig"><span class="token delimiter punctuation">{%</span> <span class="token tag-name keyword">edge</span> <span class="token string"><span class="token punctuation">"</span>njk<span class="token punctuation">"</span></span> <span class="token delimiter punctuation">%}</span></span>
<span class="token twig language-twig"><span class="token delimiter punctuation">{%</span> <span class="token tag-name keyword">if</span> nowPlaying <span class="token delimiter punctuation">%}</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dl</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>c-dataList u-font--code<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>c-dataList__item<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dt</span><span class="token punctuation">></span></span>Artist<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dt</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dd</span><span class="token punctuation">></span></span><span class="token twig language-twig"><span class="token delimiter punctuation">{{</span> nowPlaying<span class="token punctuation">.</span>artist <span class="token delimiter punctuation">}}</span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dd</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>c-dataList__item<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dt</span><span class="token punctuation">></span></span>Track<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dt</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dd</span><span class="token punctuation">></span></span><span class="token twig language-twig"><span class="token delimiter punctuation">{{</span> nowPlaying<span class="token punctuation">.</span>song <span class="token delimiter punctuation">}}</span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dd</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>c-dataList__item<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dt</span><span class="token punctuation">></span></span>Release<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dt</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dd</span><span class="token punctuation">></span></span><span class="token twig language-twig"><span class="token delimiter punctuation">{{</span> nowPlaying<span class="token punctuation">.</span>release <span class="token delimiter punctuation">}}</span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dd</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dl</span><span class="token punctuation">></span></span>
<span class="token twig language-twig"><span class="token delimiter punctuation">{%</span> <span class="token tag-name keyword">else</span> <span class="token delimiter punctuation">%}</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>u-font--code<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Silence (nothing)<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span>
<span class="token twig language-twig"><span class="token delimiter punctuation">{%</span> <span class="token tag-name keyword">endif</span> <span class="token delimiter punctuation">%}</span></span>
<span class="token twig language-twig"><span class="token delimiter punctuation">{%</span> <span class="token tag-name keyword">endedge</span> <span class="token delimiter punctuation">%}</span></span>
</code></pre>
<h3 id="see-my-listening-habits" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2022/07/28/redesign-2022/#see-my-listening-habits">See My Listening Habits</a></h3>
<p>My listening habits are retrieved with a <a href="https://www.11ty.dev/docs/plugins/serverless/">serverless function</a>. Instead of putting this data in the serverless function itself, the API calls are in my <code>stats.11tydata.js</code> file. The way it works is that data gets <a href="https://www.11ty.dev/docs/plugins/fetch/">fetched</a> on the initial load, Netlify caches that build for a set amount of time, then after that time is reached it will fetch new data on page load.</p>
<pre class="language-js"><code class="language-js"><span class="token comment">// stats.11tydata.js</span>
<span class="token comment">// Example function for getting my top artists for the month</span>
<span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">getTopArtists</span><span class="token punctuation">(</span>
api<span class="token punctuation">,</span>
auth<span class="token punctuation">,</span>
fetchDir<span class="token punctuation">,</span>
count <span class="token operator">=</span> <span class="token number">10</span><span class="token punctuation">,</span>
range <span class="token operator">=</span> <span class="token string">"this_month"</span><span class="token punctuation">,</span>
<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">try</span> <span class="token punctuation">{</span>
<span class="token keyword">let</span> options <span class="token operator">=</span> <span class="token punctuation">{</span>
<span class="token literal-property property">type</span><span class="token operator">:</span> <span class="token string">"json"</span><span class="token punctuation">,</span>
<span class="token literal-property property">fetchOptions</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token literal-property property">headers</span><span class="token operator">:</span> auth<span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token literal-property property">directory</span><span class="token operator">:</span> fetchDir<span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token comment">// I don't know if these options are actually doing anything, but it looks like it works (mostly)</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">ELEVENTY_SERVERLESS</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
options<span class="token punctuation">.</span>duration <span class="token operator">=</span> <span class="token string">"30m"</span><span class="token punctuation">;</span>
options<span class="token punctuation">.</span>directory <span class="token operator">=</span> <span class="token string">"/tmp/.cache/"</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">const</span> data <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">EleventyFetch</span><span class="token punctuation">(</span>
<span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>api<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">/stats/user/actionhamilton/artists?count=</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>count<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">&range=</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>range<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">,</span>
options<span class="token punctuation">,</span>
<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> <span class="token punctuation">{</span>payload<span class="token punctuation">}</span> <span class="token operator">=</span> data<span class="token punctuation">;</span>
<span class="token keyword">const</span> <span class="token punctuation">{</span>artists<span class="token punctuation">}</span> <span class="token operator">=</span> payload<span class="token punctuation">;</span>
<span class="token keyword">return</span> artists<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">artist</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> <span class="token punctuation">{</span><span class="token literal-property property">artist_name</span><span class="token operator">:</span> name<span class="token punctuation">,</span> <span class="token literal-property property">listen_count</span><span class="token operator">:</span> listens<span class="token punctuation">}</span> <span class="token operator">=</span> artist<span class="token punctuation">;</span>
<span class="token keyword">return</span> <span class="token punctuation">{</span>name<span class="token punctuation">,</span> listens<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span>error<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token boolean">false</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>I didn't do any major editing of the auto-generated serverless functions from the 11ty Serverless plugin aside from setting it up for Netlify's On-Demand builder.</p>
<h2 id="the-bugs-encountered" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2022/07/28/redesign-2022/#the-bugs-encountered">The Bugs Encountered</a></h2>
<p>A major redesign isn't without its bugs.</p>
<ul>
<li>It turns out changing URIs means you lose webmentions since they were pointing to the old URIs... oops.</li>
<li>If you minify your HTML output like I do, be sure to disable comment removal because Eleventy Edge relies on an HTML comment to inject content.</li>
<li>In order for the serverless functions to work, I had to change some constants in my <code>.eleventy.js</code> config:</li>
</ul>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> utilsDir <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>process<span class="token punctuation">.</span><span class="token function">cwd</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">/utils</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>
<span class="token keyword">const</span> srcDir <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">./src</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span></code></pre>
<p>I might've missed a bug or two, but thanks for reading!</p>
<hr class="footnotes-sep" />
<section class="footnotes">
<ol class="footnotes-list">
<li id="fn1" class="footnote-item"><p>If you know a simple way to find the first commit of a repo, please hit me up. <a href="https://www.troyv.dev/2022/07/28/redesign-2022/#fnref1" class="footnote-backref">↩︎</a></p>
</li>
<li id="fn2" class="footnote-item"><p>Source needed. <a href="https://www.troyv.dev/2022/07/28/redesign-2022/#fnref2" class="footnote-backref">↩︎</a></p>
</li>
<li id="fn3" class="footnote-item"><p>Check out <a href="https://www.troyv.dev/projects/">my projects</a>. <a href="https://www.troyv.dev/2022/07/28/redesign-2022/#fnref3" class="footnote-backref">↩︎</a></p>
</li>
<li id="fn4" class="footnote-item"><p>Inspiration from <a href="https://mxb.dev/">Max Böck's</a> website. <a href="https://www.troyv.dev/2022/07/28/redesign-2022/#fnref4" class="footnote-backref">↩︎</a></p>
</li>
<li id="fn5" class="footnote-item"><p>Find them on <a href="https://www.troyv.dev/archive/">the archive</a>. <a href="https://www.troyv.dev/2022/07/28/redesign-2022/#fnref5" class="footnote-backref">↩︎</a></p>
</li>
<li id="fn6" class="footnote-item"><p>I won't go into detail about all the methods I'm using to scrobble my music, but send me a message if you're interested. <a href="https://www.troyv.dev/2022/07/28/redesign-2022/#fnref6" class="footnote-backref">↩︎</a></p>
</li>
</ol>
</section>
Use Node Modules in Eleventy2022-04-01T00:00:00Zhttps://www.troyv.dev/2022/04/01/use-node-modules-in-eleventy/
<!-- @format -->
<p>When I started my Eleventy journey, I was still <em>very</em> new to web development. I'd see posts about cool <a href="https://piccalil.li/blog/a-modern-css-reset/">CSS resets</a> or script packages and want to use them, only to be stumped on how to get from <code>npm i</code> to using it in my static site website.</p>
<p>Well, this month I was doing some Googling on something I've already forgotten and found an <a href="https://github.com/11ty/eleventy/issues/768">issue in the 11ty repo</a> where someone asked this exact question, months before I needed it.</p>
<p>The solution? Eleventy's <a href="https://www.11ty.dev/docs/copy/">Passthrough File Copy</a>.</p>
<pre class="language-js"><code class="language-js"><span class="token comment">// Zach's answer from the GitHub issue</span>
eleventyConfig<span class="token punctuation">.</span><span class="token function">addPassthroughCopy</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
<span class="token string-property property">"node_modules/chartist/dist/chartist.min.css"</span><span class="token operator">:</span> <span class="token string">"assets/chartist.min.css"</span><span class="token punctuation">,</span>
<span class="token string-property property">"node_modules/chartist/dist/chartist.min.js"</span><span class="token operator">:</span> <span class="token string">"assets/chartist.min.js"</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>If only I knew how to properly Google code questions back then. Hopefully someone else may find this useful.</p>
Making a Sudoku App with Vue2022-03-03T00:00:00Zhttps://www.troyv.dev/2022/03/03/making-a-sudoku-app-with-vue/
<!-- @format -->
<p>I was surprised by how difficult it was to find a full-blown tutorial for making a Sudoku app with Vue. On the contrary, the internet is overflowing with React versions of Sudoku, so I decided to follow one of those and <em>port</em> it into Vue.</p>
<p>Matt Biilmann of Netlify fame <a href="https://www.youtube.com/watch?v=GytUZLK4kwA">made a Sudoku React app</a> that can be found over on the freeCodeCamp YouTube channel, and it's this video that was the entry point to my app, <a href="https://github.com/troyvassalotti/sudoku">Vuedoku</a>. I got it started with Vite because I've been using that a lot lately and think it's rad.</p>
<h2 id="getting-started" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2022/03/03/making-a-sudoku-app-with-vue/#getting-started">Getting Started</a></h2>
<p>It's important to first understand how Sudoku works. I'm not qualified to define it for you, but Wikipedia is:</p>
<blockquote>
<p>Sudoku is a logic-based, combinatorial number-placement puzzle. The objective is to fill a 9 x 9 grid with digits so that each column, each row, and each of the nine 3 x 3 subgrids that compose the grid (also called "boxes", "blocks", or "regions") contain all of the digits from 1 to 9. The puzzle setter provides a partially completed grid, which for a well-posed puzzle has a single solution.</p>
</blockquote>
<p>Makes sense, right?</p>
<p>At its core, the app needed the following features:</p>
<ol>
<li>A way to generate a playable board and store its solution in the background.</li>
<li>The ability to add guesses to blank spaces <em>and only</em> the blank spaces.</li>
<li>A shortcut to fill in the solution when you get stuck, or generate a brand new puzzle if you'd like.</li>
</ol>
<p>Additional features that came from both Matt's video and my post-video thinking include:</p>
<ol>
<li>The option to highlight your guesses as either correct or incorrect if you find yourself stuck or needing a hint.</li>
<li>Sharable boards so you can challenge friends to see who can complete the same board in the shortest amount of time.</li>
<li>A way to restore your previous board in case you accidentally generate a new puzzle, overriding your current work.</li>
</ol>
<p>Since we outlined the features our app needs, we can try to map our what components it will need. It's a small app which means there are but a handful of pieces to this <em>puzzle</em> (ha ha, get it?).</p>
<ol>
<li><code>SudokuBoard.vue</code> is our board itself.</li>
<li><code>SudokuField.vue</code> is the individual square or block composing the board.</li>
<li><code>Timer.vue</code> is the clock keeping track of how long you've been playing.</li>
<li><code>Result.vue</code> is what shows when your puzzle is completed (either by cheating with the shortcut or naturally because you're so good at Sudoku).</li>
<li><code>ReloadPrompt.vue</code> also exists, but it's only purpose is to display a message when the app is available for offline use, as part of the <code>vite-plugin-pwa</code> package.</li>
</ol>
<p>Knowing what the outcome is supposed to be and what components we'll need, we can now start building it. I used Vite's Vue 3 starter, so <a href="https://vitejs.dev/guide/">follow their docs</a> for the most up to date method of doing that.</p>
<h2 id="generating-data" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2022/03/03/making-a-sudoku-app-with-vue/#generating-data">Generating Data</a></h2>
<p>In order to create a board, we need a Sudoku generator, so let's install <code>sudoku</code> from <code>npm</code>.</p>
<pre class="language-js"><code class="language-js">npm i sudoku</code></pre>
<p>The sudoku package comes with two key functions we'll be using: <code>makepuzzle()</code> and <code>solvepuzzle()</code>. The former generates an array with <code>null</code> values for empty boxes and fills the rest with starter numbers.</p>
<p>That's great, but we need it to be in the format of columns and rows since that's how you work with the board. We also need the solution for that puzzle so we can check progress during the game. And that's not all! We want to track the player's start time <strong>and</strong> end time.</p>
<p>The best way of keeping all these pieces of information together is by creating our own object to store data in.</p>
<pre class="language-js"><code class="language-js"><span class="token comment">// lib/sudoku.js</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span>makepuzzle<span class="token punctuation">,</span> solvepuzzle<span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"sudoku"</span><span class="token punctuation">;</span>
<span class="token comment">/*
Generates a sudoku with the structure
{rows: [{index: 0, cols: [{row: 0, col: 0, value: 1, readonly: true}, ...]}, ...]}
*/</span>
<span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token function">generateSudoku</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> fromUrl <span class="token operator">=</span> <span class="token function">extractUrlData</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Used if you share the game with a friend</span>
<span class="token keyword">const</span> raw <span class="token operator">=</span> fromUrl <span class="token operator">?</span> fromUrl<span class="token punctuation">.</span>raw <span class="token operator">:</span> <span class="token function">makepuzzle</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> rawSolution <span class="token operator">=</span> <span class="token function">solvepuzzle</span><span class="token punctuation">(</span>raw<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> formatted <span class="token operator">=</span> raw<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">e</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">(</span>e <span class="token operator">===</span> <span class="token keyword">null</span> <span class="token operator">?</span> <span class="token keyword">null</span> <span class="token operator">:</span> e <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Adjust the values slightly since we're working with a 0 indexed situation</span>
<span class="token keyword">const</span> formattedSolution <span class="token operator">=</span> rawSolution<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">e</span><span class="token punctuation">)</span> <span class="token operator">=></span> e <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Same thing goes for the solution</span>
<span class="token keyword">const</span> result <span class="token operator">=</span> <span class="token punctuation">{</span>
raw<span class="token punctuation">,</span>
<span class="token literal-property property">rows</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token literal-property property">solution</span><span class="token operator">:</span> formattedSolution<span class="token punctuation">,</span>
<span class="token literal-property property">startTime</span><span class="token operator">:</span> <span class="token keyword">new</span> <span class="token class-name">Date</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token literal-property property">solvedTime</span><span class="token operator">:</span> <span class="token keyword">null</span><span class="token punctuation">,</span>
<span class="token literal-property property">challengerStartTime</span><span class="token operator">:</span> fromUrl <span class="token operator">&&</span> <span class="token keyword">new</span> <span class="token class-name">Date</span><span class="token punctuation">(</span>fromUrl<span class="token punctuation">.</span>startTime<span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token literal-property property">challengerSolvedTime</span><span class="token operator">:</span> fromUrl <span class="token operator">&&</span> <span class="token keyword">new</span> <span class="token class-name">Date</span><span class="token punctuation">(</span>fromUrl<span class="token punctuation">.</span>solvedTime<span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token comment">// Loop over the formatted data to generate row and column data</span>
<span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">let</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator"><</span> <span class="token number">9</span><span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> row <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token literal-property property">cols</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token literal-property property">index</span><span class="token operator">:</span> i<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">let</span> j <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> j <span class="token operator"><</span> <span class="token number">9</span><span class="token punctuation">;</span> j<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> value <span class="token operator">=</span> formatted<span class="token punctuation">[</span>i <span class="token operator">*</span> <span class="token number">9</span> <span class="token operator">+</span> j<span class="token punctuation">]</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> col <span class="token operator">=</span> <span class="token punctuation">{</span>
<span class="token literal-property property">row</span><span class="token operator">:</span> i<span class="token punctuation">,</span>
<span class="token literal-property property">col</span><span class="token operator">:</span> j<span class="token punctuation">,</span>
<span class="token literal-property property">value</span><span class="token operator">:</span> value<span class="token punctuation">,</span>
<span class="token literal-property property">readonly</span><span class="token operator">:</span> value <span class="token operator">!==</span> <span class="token keyword">null</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
row<span class="token punctuation">.</span>cols<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>col<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
result<span class="token punctuation">.</span>rows<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>row<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">return</span> result<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>At this point, we have a file <code>sudoku.js</code> in our <code>lib</code> directory. To use our data, we need to import <code>generateSudoku()</code>to our <code>App.vue</code> and store the Sudoku in app state.</p>
<pre class="language-js"><code class="language-js"><span class="token comment">// App.vue</span>
<span class="token operator"><</span>script setup<span class="token operator">></span>
<span class="token keyword">import</span> <span class="token punctuation">{</span>reactive<span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'vue'</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span>generateSudoku<span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'./lib/sudoku'</span>
<span class="token keyword">const</span> store <span class="token operator">=</span> <span class="token punctuation">{</span>
<span class="token literal-property property">state</span><span class="token operator">:</span> <span class="token function">reactive</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
<span class="token literal-property property">sudoku</span><span class="token operator">:</span> <span class="token function">generateSudoku</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token literal-property property">showProgress</span><span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span> <span class="token comment">// Used in highlighting correct/incorrect cells</span>
<span class="token literal-property property">previousSudoku</span><span class="token operator">:</span> <span class="token keyword">null</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token comment">/*
* snip
*/</span>
<span class="token operator"><</span><span class="token operator">/</span>script<span class="token operator">></span></code></pre>
<p>Our app now has access to a <code>store</code> object, which holds our app's <code>state</code> object. The app state is defined as reactive so we can change its value as needed, and our Sudoku is generated on the fly (along with a game options setting and previous Sudoku fields). It's <code>store.state.sudoku</code> that gets passed as a prop to our board component.</p>
<pre class="language-js"><code class="language-js"><span class="token comment">// App.vue</span>
<span class="token operator"><</span>script setup<span class="token operator">></span>
<span class="token keyword">import</span> <span class="token punctuation">{</span>reactive<span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'vue'</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span>generateSudoku<span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'./lib/sudoku'</span>
<span class="token keyword">import</span> SudokuBoard <span class="token keyword">from</span> <span class="token string">'./components/SudokuBoard.vue'</span>
<span class="token keyword">const</span> store <span class="token operator">=</span> <span class="token punctuation">{</span>
<span class="token literal-property property">state</span><span class="token operator">:</span> <span class="token function">reactive</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
<span class="token literal-property property">sudoku</span><span class="token operator">:</span> <span class="token function">generateSudoku</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token literal-property property">showProgress</span><span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span> <span class="token comment">// Used in highlighting correct/incorrect cells</span>
<span class="token literal-property property">previousSudoku</span><span class="token operator">:</span> <span class="token keyword">null</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token comment">/*
* snip
*/</span>
<span class="token operator"><</span><span class="token operator">/</span>script<span class="token operator">></span>
<span class="token operator"><</span>template<span class="token operator">></span>
<span class="token operator"><</span>SudokuBoard <span class="token operator">:</span>sudoku<span class="token operator">=</span><span class="token string">"store.state.sudoku"</span> <span class="token operator">:</span>progress<span class="token operator">=</span><span class="token string">"store.state.showProgress"</span> <span class="token operator">:</span>previous<span class="token operator">=</span><span class="token string">"store.state.previousSudoku"</span><span class="token operator">/</span><span class="token operator">></span>
<span class="token operator"><</span><span class="token operator">/</span>template<span class="token operator">></span></code></pre>
<h2 id="the-board" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2022/03/03/making-a-sudoku-app-with-vue/#the-board">The Board</a></h2>
<p>We have data, but we have no home for that data. Our app needs a <code>SudokuField</code> component because it's those fields where we will provide our guesses, and our data is what plops those fields into the board.</p>
<p>Our field component needs to accept block information (a value and whether it should be read only), and it needs to pass change events back into the app. The entire component is relatively small since it has only a few specific needs.</p>
<pre class="language-js"><code class="language-js"><span class="token comment">// components/SudokuField.vue</span>
<span class="token operator"><</span>script setup<span class="token operator">></span>
<span class="token keyword">const</span> props <span class="token operator">=</span> <span class="token function">defineProps</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
<span class="token literal-property property">field</span><span class="token operator">:</span> Object<span class="token punctuation">,</span>
<span class="token literal-property property">onChange</span><span class="token operator">:</span> Function
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token comment">/**
* Pass changed field up to the Sudoku board to evaluate the field's value
* @param e - Event
*/</span>
<span class="token keyword">function</span> <span class="token function">handleChange</span><span class="token punctuation">(</span><span class="token parameter">e</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> el <span class="token operator">=</span> e<span class="token punctuation">.</span>target
<span class="token keyword">const</span> value <span class="token operator">=</span> e<span class="token punctuation">.</span>target<span class="token punctuation">.</span>value <span class="token operator">===</span> <span class="token string">''</span> <span class="token operator">?</span> <span class="token keyword">null</span> <span class="token operator">:</span> <span class="token function">parseInt</span><span class="token punctuation">(</span>e<span class="token punctuation">.</span>target<span class="token punctuation">.</span>value<span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">)</span>
props<span class="token punctuation">.</span><span class="token function">onChange</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token operator">...</span>props<span class="token punctuation">.</span>field<span class="token punctuation">,</span> <span class="token literal-property property">value</span><span class="token operator">:</span> value<span class="token punctuation">,</span> <span class="token literal-property property">el</span><span class="token operator">:</span> el<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token operator"><</span><span class="token operator">/</span>script<span class="token operator">></span>
<span class="token operator"><</span>template<span class="token operator">></span>
<span class="token operator"><</span>input <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"field"</span> inputmode<span class="token operator">=</span><span class="token string">"numeric"</span> maxlength<span class="token operator">=</span><span class="token string">"1"</span> pattern<span class="token operator">=</span><span class="token string">"[0-9]*"</span> <span class="token operator">:</span>value<span class="token operator">=</span><span class="token string">"props.field.value || ''"</span> <span class="token operator">:</span>readonly<span class="token operator">=</span><span class="token string">"props.field.readonly"</span> @change<span class="token operator">=</span><span class="token string">"handleChange"</span><span class="token operator">></span>
<span class="token operator"><</span><span class="token operator">/</span>template<span class="token operator">></span>
<span class="token operator"><</span>style lang<span class="token operator">=</span><span class="token string">"scss"</span> scoped<span class="token operator">></span>
<span class="token punctuation">.</span>field <span class="token punctuation">{</span>
<span class="token operator">--</span>lightness<span class="token operator">:</span> <span class="token number">8</span><span class="token operator">%</span><span class="token punctuation">;</span>
aspect<span class="token operator">-</span>ratio<span class="token operator">:</span> <span class="token number">1</span> <span class="token operator">/</span> <span class="token number">1</span><span class="token punctuation">;</span>
<span class="token literal-property property">border</span><span class="token operator">:</span> 1px solid <span class="token keyword">var</span><span class="token punctuation">(</span><span class="token operator">--</span>ink<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token literal-property property">color</span><span class="token operator">:</span> <span class="token function">hsl</span><span class="token punctuation">(</span>0deg<span class="token punctuation">,</span> <span class="token number">0</span><span class="token operator">%</span><span class="token punctuation">,</span> <span class="token keyword">var</span><span class="token punctuation">(</span><span class="token operator">--</span>lightness<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token literal-property property">font</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token literal-property property">size</span><span class="token operator">:</span> <span class="token keyword">var</span><span class="token punctuation">(</span><span class="token operator">--</span>step<span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
text<span class="token operator">-</span>align<span class="token operator">:</span> center<span class="token punctuation">;</span>
<span class="token operator">&</span><span class="token punctuation">[</span>readonly<span class="token punctuation">]</span> <span class="token punctuation">{</span>
<span class="token operator">--</span>lightness<span class="token operator">:</span> <span class="token number">30</span><span class="token operator">%</span><span class="token punctuation">;</span>
<span class="token literal-property property">cursor</span><span class="token operator">:</span> not<span class="token operator">-</span>allowed<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token operator">&</span><span class="token operator">:</span>nth<span class="token operator">-</span><span class="token keyword">of</span><span class="token operator">-</span><span class="token function">type</span><span class="token punctuation">(</span><span class="token parameter"><span class="token number">3</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
border<span class="token operator">-</span>inline<span class="token operator">-</span>end<span class="token operator">-</span>width<span class="token operator">:</span> <span class="token keyword">var</span><span class="token punctuation">(</span><span class="token operator">--</span>thickness<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token operator">&</span><span class="token operator">:</span>nth<span class="token operator">-</span><span class="token keyword">of</span><span class="token operator">-</span><span class="token function">type</span><span class="token punctuation">(</span><span class="token parameter"><span class="token number">6</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
border<span class="token operator">-</span>inline<span class="token operator">-</span>end<span class="token operator">-</span>width<span class="token operator">:</span> <span class="token keyword">var</span><span class="token punctuation">(</span><span class="token operator">--</span>thickness<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
@<span class="token function">media</span> <span class="token punctuation">(</span><span class="token parameter">prefers<span class="token operator">-</span>color<span class="token operator">-</span>scheme<span class="token operator">:</span> dark</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token punctuation">.</span>field <span class="token punctuation">{</span>
<span class="token operator">--</span>lightness<span class="token operator">:</span> <span class="token number">95</span><span class="token operator">%</span><span class="token punctuation">;</span>
background<span class="token operator">-</span>color<span class="token operator">:</span> <span class="token keyword">var</span><span class="token punctuation">(</span><span class="token operator">--</span>canvas<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token operator">&</span><span class="token punctuation">[</span>readonly<span class="token punctuation">]</span> <span class="token punctuation">{</span>
<span class="token operator">--</span>lightness<span class="token operator">:</span> <span class="token number">70</span><span class="token operator">%</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token operator"><</span><span class="token operator">/</span>style<span class="token operator">></span></code></pre>
<p>You may have noticed our <code>handleChange()</code> function and <code>onChange</code> prop. These two pieces are what pass state back to our app. <code>onChange</code> is a function prop from <code>App.vue</code> and <code>handleChange()</code> passes our <code>onChange</code> function data for the field you're currently working with. What's getting passed? Well, we need to tell the app 1) which field out of all the fields is being changed (<code>props.field</code>), and 2) what value that cell now is.</p>
<p>Our <code>onChange</code> function takes that and checks your guesses against the stored solution to determine if your game is completed or still in progress.</p>
<pre class="language-js"><code class="language-js"><span class="token comment">// App.vue</span>
<span class="token comment">/*
* snip
*/</span>
<span class="token keyword">const</span> store <span class="token operator">=</span> <span class="token punctuation">{</span>
<span class="token literal-property property">state</span><span class="token operator">:</span> <span class="token function">reactive</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
<span class="token literal-property property">sudoku</span><span class="token operator">:</span> <span class="token function">generateSudoku</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token literal-property property">showProgress</span><span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span>
<span class="token literal-property property">previousSudoku</span><span class="token operator">:</span> <span class="token keyword">null</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token comment">/**
* Receives events from the individual fields to either highlight cells or check solutions
* @param e - Event
*/</span>
<span class="token function">handleChange</span><span class="token punctuation">(</span><span class="token parameter">e</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
store<span class="token punctuation">.</span>state<span class="token punctuation">.</span>sudoku<span class="token punctuation">.</span>rows<span class="token punctuation">[</span>e<span class="token punctuation">.</span>row<span class="token punctuation">]</span><span class="token punctuation">.</span>cols<span class="token punctuation">[</span>e<span class="token punctuation">.</span>col<span class="token punctuation">]</span><span class="token punctuation">.</span>value <span class="token operator">=</span> e<span class="token punctuation">.</span>value
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>store<span class="token punctuation">.</span>state<span class="token punctuation">.</span>sudoku<span class="token punctuation">.</span>solvedTime<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> solved <span class="token operator">=</span> <span class="token function">checkSolution</span><span class="token punctuation">(</span>store<span class="token punctuation">.</span>state<span class="token punctuation">.</span>sudoku<span class="token punctuation">)</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>solved<span class="token punctuation">)</span> <span class="token punctuation">{</span>
store<span class="token punctuation">.</span>state<span class="token punctuation">.</span>sudoku<span class="token punctuation">.</span>solvedTime <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Date</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
store<span class="token punctuation">.</span>state<span class="token punctuation">.</span>sudoku<span class="token punctuation">.</span>shareUrl <span class="token operator">=</span> <span class="token function">shareUrl</span><span class="token punctuation">(</span>store<span class="token punctuation">.</span>state<span class="token punctuation">.</span>sudoku<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token comment">/*
* snip
*/</span></code></pre>
<p>Now it's time to actually create fields for our entire Sudoku board, and that's where Vue's <code>v-for</code> directives come in. We have access to our Sudoku's rows, which gives us access to its columns and values. If we iterate over the rows, then iterate over each row's columns, we can insert fields for each value in our puzzle.</p>
<pre class="language-js"><code class="language-js"><span class="token comment">// components/SudokuBoard.vue</span>
<span class="token operator"><</span>script setup<span class="token operator">></span>
<span class="token keyword">import</span> SudokuField <span class="token keyword">from</span> <span class="token string">'./SudokuField.vue'</span>
<span class="token keyword">const</span> props <span class="token operator">=</span> <span class="token function">defineProps</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
<span class="token literal-property property">sudoku</span><span class="token operator">:</span> Object<span class="token punctuation">,</span>
<span class="token literal-property property">onChange</span><span class="token operator">:</span> Function<span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token operator"><</span><span class="token operator">/</span>script<span class="token operator">></span>
<span class="token operator"><</span>template<span class="token operator">></span>
<span class="token operator"><</span>main <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"main"</span><span class="token operator">></span>
<span class="token operator"><</span>div <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"wrapper"</span><span class="token operator">></span>
<span class="token operator"><</span>div <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"board"</span> <span class="token operator">:</span><span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"{solved: props.sudoku.solvedTime}"</span><span class="token operator">></span>
<span class="token operator"><</span>div <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"row"</span> v<span class="token operator">-</span><span class="token keyword">for</span><span class="token operator">=</span><span class="token string">"row in props.sudoku.rows"</span> <span class="token operator">:</span>key<span class="token operator">=</span><span class="token string">"row.index"</span><span class="token operator">></span>
<span class="token operator"><</span>SudokuField v<span class="token operator">-</span><span class="token keyword">for</span><span class="token operator">=</span><span class="token string">"field in row.cols"</span> <span class="token operator">:</span>key<span class="token operator">=</span><span class="token string">"field.col"</span> <span class="token operator">:</span>field<span class="token operator">=</span><span class="token string">"field"</span> <span class="token operator">:</span>onChange<span class="token operator">=</span><span class="token string">"props.onChange"</span><span class="token operator">/</span><span class="token operator">></span>
<span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span>
<span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span>
<span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span>
<span class="token operator"><</span><span class="token operator">/</span>main<span class="token operator">></span>
<span class="token operator"><</span><span class="token operator">/</span>template<span class="token operator">></span></code></pre>
<h2 id="timer-and-result-components" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2022/03/03/making-a-sudoku-app-with-vue/#timer-and-result-components">Timer and Result Components</a></h2>
<p>Let's get these two pieces out of the way while we're building our board. <code>Timer.vue</code> is a basic counter that doesn't depend on or interact with any other components.</p>
<pre class="language-js"><code class="language-js"><span class="token comment">// components/Timer.vue</span>
<span class="token operator"><</span>script setup<span class="token operator">></span>
<span class="token keyword">import</span> <span class="token punctuation">{</span>onBeforeUnmount<span class="token punctuation">,</span> onMounted<span class="token punctuation">,</span> reactive<span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"vue"</span>
<span class="token keyword">const</span> props <span class="token operator">=</span> <span class="token function">defineProps</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
<span class="token literal-property property">start</span><span class="token operator">:</span> Date
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token keyword">const</span> state <span class="token operator">=</span> <span class="token function">reactive</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
<span class="token literal-property property">elapsed</span><span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span>
<span class="token literal-property property">interval</span><span class="token operator">:</span> <span class="token function">setInterval</span><span class="token punctuation">(</span>getTime<span class="token punctuation">,</span> <span class="token number">1000</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token keyword">function</span> <span class="token function">getTime</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
state<span class="token punctuation">.</span>elapsed <span class="token operator">=</span> Math<span class="token punctuation">.</span><span class="token function">floor</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">Date</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getTime</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-</span> props<span class="token punctuation">.</span>start<span class="token punctuation">.</span><span class="token function">getTime</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">/</span> <span class="token number">1000</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token function">onMounted</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
state<span class="token punctuation">.</span>interval
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token function">onBeforeUnmount</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token function">clearInterval</span><span class="token punctuation">(</span>state<span class="token punctuation">.</span>interval<span class="token punctuation">)</span>
<span class="token keyword">delete</span> state<span class="token punctuation">.</span>interval
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token operator"><</span><span class="token operator">/</span>script<span class="token operator">></span>
<span class="token operator"><</span>template<span class="token operator">></span>
<span class="token operator"><</span>h2<span class="token operator">></span>Time<span class="token operator">:</span> <span class="token operator"><</span><span class="token operator">/</span>h2<span class="token operator">></span>
<span class="token operator"><</span><span class="token operator">/</span>template<span class="token operator">></span></code></pre>
<p><code>Result.vue</code> is slightly more complicated but mainly because it controls the end-game share functionality. Winning the game calls for a celebration, so confetti cannons are in order when this component is mounted to the DOM. We also want to show different content depending on if you cheated with the shortcut button or if you completed it on your own. Cheating sets a value in our <code>sudoku</code> object, so that's fine enough to trigger dynamic content with. We punish cheaters by calling them out on it and subjecting them to a YouTube video.</p>
<p>Sharing the game will either open up your device's native share sheet or copy your game URL to the clipboard.</p>
<pre class="language-js"><code class="language-js"><span class="token comment">// components/Result.vue</span>
<span class="token operator"><</span>script setup<span class="token operator">></span>
<span class="token keyword">import</span> <span class="token punctuation">{</span>onMounted<span class="token punctuation">,</span> reactive<span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'vue'</span>
<span class="token keyword">import</span> confetti <span class="token keyword">from</span> <span class="token string">'canvas-confetti'</span>
<span class="token keyword">const</span> props <span class="token operator">=</span> <span class="token function">defineProps</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
<span class="token literal-property property">sudoku</span><span class="token operator">:</span> Object
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token keyword">const</span> state <span class="token operator">=</span> <span class="token function">reactive</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
<span class="token literal-property property">elapsed</span><span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span>
<span class="token literal-property property">opponent</span><span class="token operator">:</span> <span class="token number">0</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token comment">/**
* Share your Sudoku link either as a URL or with the Share API
* @param e - Event
*/</span>
<span class="token keyword">function</span> <span class="token function">shareLink</span><span class="token punctuation">(</span><span class="token parameter">e</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">let</span> link <span class="token operator">=</span> props<span class="token punctuation">.</span>sudoku<span class="token punctuation">.</span>shareUrl
<span class="token keyword">if</span> <span class="token punctuation">(</span>navigator<span class="token punctuation">.</span>share<span class="token punctuation">)</span> <span class="token punctuation">{</span>
navigator<span class="token punctuation">.</span><span class="token function">share</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
<span class="token literal-property property">title</span><span class="token operator">:</span> <span class="token string">'Sudoku'</span><span class="token punctuation">,</span>
<span class="token literal-property property">url</span><span class="token operator">:</span> link
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
navigator<span class="token punctuation">.</span>clipboard<span class="token punctuation">.</span><span class="token function">writeText</span><span class="token punctuation">(</span>link<span class="token punctuation">)</span>
<span class="token keyword">let</span> el <span class="token operator">=</span> e<span class="token punctuation">.</span>target
<span class="token keyword">let</span> initialText <span class="token operator">=</span> el<span class="token punctuation">.</span>innerText
el<span class="token punctuation">.</span>innerText <span class="token operator">=</span> <span class="token string">"👍 Link Copied 👍"</span>
<span class="token function">setTimeout</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
el<span class="token punctuation">.</span>innerText <span class="token operator">=</span> initialText
<span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token number">3000</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token function">onMounted</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token comment">// Confetti Cannons</span>
state<span class="token punctuation">.</span>elapsed <span class="token operator">=</span> Math<span class="token punctuation">.</span><span class="token function">floor</span><span class="token punctuation">(</span><span class="token punctuation">(</span>props<span class="token punctuation">.</span>sudoku<span class="token punctuation">.</span>solvedTime<span class="token punctuation">.</span><span class="token function">getTime</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-</span> props<span class="token punctuation">.</span>sudoku<span class="token punctuation">.</span>startTime<span class="token punctuation">.</span><span class="token function">getTime</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">/</span> <span class="token number">1000</span><span class="token punctuation">)</span>
state<span class="token punctuation">.</span>opponent <span class="token operator">=</span> props<span class="token punctuation">.</span>sudoku<span class="token punctuation">.</span>challengerSolvedTime <span class="token operator">?</span> Math<span class="token punctuation">.</span><span class="token function">floor</span><span class="token punctuation">(</span><span class="token punctuation">(</span>props<span class="token punctuation">.</span>sudoku<span class="token punctuation">.</span>challengerSolvedTime<span class="token punctuation">.</span><span class="token function">getTime</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-</span> props<span class="token punctuation">.</span>sudoku<span class="token punctuation">.</span>challengerStartTime<span class="token punctuation">.</span><span class="token function">getTime</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">/</span> <span class="token number">1000</span><span class="token punctuation">)</span> <span class="token operator">:</span> <span class="token keyword">null</span>
<span class="token keyword">let</span> duration <span class="token operator">=</span> <span class="token number">15</span> <span class="token operator">*</span> <span class="token number">1000</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> animationEnd <span class="token operator">=</span> Date<span class="token punctuation">.</span><span class="token function">now</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> duration<span class="token punctuation">;</span>
<span class="token keyword">const</span> defaults <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token literal-property property">startVelocity</span><span class="token operator">:</span> <span class="token number">15</span><span class="token punctuation">,</span> <span class="token literal-property property">spread</span><span class="token operator">:</span> <span class="token number">360</span><span class="token punctuation">,</span> <span class="token literal-property property">ticks</span><span class="token operator">:</span> <span class="token number">60</span><span class="token punctuation">,</span> <span class="token literal-property property">zIndex</span><span class="token operator">:</span> <span class="token number">0</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">function</span> <span class="token function">randomInRange</span><span class="token punctuation">(</span><span class="token parameter">min<span class="token punctuation">,</span> max</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> Math<span class="token punctuation">.</span><span class="token function">random</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">*</span> <span class="token punctuation">(</span>max <span class="token operator">-</span> min<span class="token punctuation">)</span> <span class="token operator">+</span> min<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">const</span> interval <span class="token operator">=</span> <span class="token function">setInterval</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> timeLeft <span class="token operator">=</span> animationEnd <span class="token operator">-</span> Date<span class="token punctuation">.</span><span class="token function">now</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>timeLeft <span class="token operator"><=</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token function">clearInterval</span><span class="token punctuation">(</span>interval<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">let</span> particleCount <span class="token operator">=</span> <span class="token number">50</span> <span class="token operator">*</span> <span class="token punctuation">(</span>timeLeft <span class="token operator">/</span> duration<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// since particles fall down, start a bit higher than random</span>
<span class="token function">confetti</span><span class="token punctuation">(</span>Object<span class="token punctuation">.</span><span class="token function">assign</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span> defaults<span class="token punctuation">,</span> <span class="token punctuation">{</span> particleCount<span class="token punctuation">,</span> <span class="token literal-property property">origin</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token literal-property property">x</span><span class="token operator">:</span> <span class="token function">randomInRange</span><span class="token punctuation">(</span><span class="token number">0.1</span><span class="token punctuation">,</span> <span class="token number">0.3</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token literal-property property">y</span><span class="token operator">:</span> Math<span class="token punctuation">.</span><span class="token function">random</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token number">0.2</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">confetti</span><span class="token punctuation">(</span>Object<span class="token punctuation">.</span><span class="token function">assign</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span> defaults<span class="token punctuation">,</span> <span class="token punctuation">{</span> particleCount<span class="token punctuation">,</span> <span class="token literal-property property">origin</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token literal-property property">x</span><span class="token operator">:</span> <span class="token function">randomInRange</span><span class="token punctuation">(</span><span class="token number">0.7</span><span class="token punctuation">,</span> <span class="token number">0.9</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token literal-property property">y</span><span class="token operator">:</span> Math<span class="token punctuation">.</span><span class="token function">random</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token number">0.2</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token number">250</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token operator"><</span><span class="token operator">/</span>script<span class="token operator">></span>
<span class="token operator"><</span>template<span class="token operator">></span>
<span class="token operator"><</span>div <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"container"</span><span class="token operator">></span>
<span class="token operator"><</span>h2 v<span class="token operator">-</span><span class="token keyword">if</span><span class="token operator">=</span><span class="token string">"!props.sudoku.cheated"</span><span class="token operator">></span>You solved it <span class="token keyword">in</span> seconds<span class="token operator"><</span><span class="token operator">/</span>h2<span class="token operator">></span>
<span class="token operator"><</span>h2 v<span class="token operator">-</span><span class="token keyword">else</span><span class="token operator">></span>You cheated<span class="token punctuation">,</span> but it took you seconds to <span class="token keyword">do</span> so<span class="token punctuation">.</span><span class="token operator"><</span><span class="token operator">/</span>h2<span class="token operator">></span>
<span class="token operator"><</span>p v<span class="token operator">-</span><span class="token keyword">if</span><span class="token operator">=</span><span class="token string">"state.opponent"</span><span class="token operator">></span>Your opponent solved it <span class="token keyword">in</span> seconds<span class="token punctuation">.</span><span class="token operator"><</span><span class="token operator">/</span>p<span class="token operator">></span>
<span class="token operator"><</span>div <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"rickroll"</span> v<span class="token operator">-</span><span class="token keyword">if</span><span class="token operator">=</span><span class="token string">"props.sudoku.cheated"</span><span class="token operator">></span>
<span class="token operator"><</span>p<span class="token operator">></span>⬇️ This is your punishment <span class="token keyword">for</span> cheating<span class="token punctuation">.</span> ⬇️<span class="token operator"><</span><span class="token operator">/</span>p<span class="token operator">></span>
<span class="token operator"><</span>iframe src<span class="token operator">=</span><span class="token string">"https://www.youtube-nocookie.com/embed/dQw4w9WgXcQ?controls=0&autoplay=1"</span>
title<span class="token operator">=</span><span class="token string">"YouTube video player"</span>
allow<span class="token operator">=</span><span class="token string">"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"</span>
allowfullscreen<span class="token operator">></span><span class="token operator"><</span><span class="token operator">/</span>iframe<span class="token operator">></span>
<span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span>
<span class="token operator"><</span>p<span class="token operator">></span>Challenge a friend<span class="token operator">:</span>
<span class="token operator"><</span>button id<span class="token operator">=</span><span class="token string">"share"</span> @click<span class="token operator">=</span><span class="token string">"shareLink"</span><span class="token operator">></span>Share Puzzle Link<span class="token operator"><</span><span class="token operator">/</span>button<span class="token operator">></span>
<span class="token operator"><</span><span class="token operator">/</span>p<span class="token operator">></span>
<span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span>
<span class="token operator"><</span><span class="token operator">/</span>template<span class="token operator">></span></code></pre>
<p>Both of these components live in <code>SudokuBoard.vue</code>.</p>
<pre class="language-js"><code class="language-js"><span class="token comment">// components/SudokuBoard.vue</span>
<span class="token operator"><</span>script setup<span class="token operator">></span>
<span class="token keyword">import</span> SudokuField <span class="token keyword">from</span> <span class="token string">'./SudokuField.vue'</span>
<span class="token keyword">import</span> Timer <span class="token keyword">from</span> <span class="token string">'./Timer.vue'</span>
<span class="token keyword">import</span> Result <span class="token keyword">from</span> <span class="token string">'./Result.vue'</span>
<span class="token keyword">const</span> props <span class="token operator">=</span> <span class="token function">defineProps</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
<span class="token literal-property property">sudoku</span><span class="token operator">:</span> Object<span class="token punctuation">,</span>
<span class="token literal-property property">onChange</span><span class="token operator">:</span> Function<span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token operator"><</span><span class="token operator">/</span>script<span class="token operator">></span>
<span class="token operator"><</span>template<span class="token operator">></span>
<span class="token operator"><</span>main <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"main"</span><span class="token operator">></span>
<span class="token operator"><</span>Timer v<span class="token operator">-</span><span class="token keyword">if</span><span class="token operator">=</span><span class="token string">"!props.sudoku.solvedTime"</span> <span class="token operator">:</span>start<span class="token operator">=</span><span class="token string">"props.sudoku.startTime"</span><span class="token operator">/</span><span class="token operator">></span>
<span class="token operator"><</span>Result v<span class="token operator">-</span><span class="token keyword">if</span><span class="token operator">=</span><span class="token string">"props.sudoku.solvedTime"</span> <span class="token operator">:</span>sudoku<span class="token operator">=</span><span class="token string">"props.sudoku"</span><span class="token operator">/</span><span class="token operator">></span>
<span class="token operator"><</span>div <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"wrapper"</span><span class="token operator">></span>
<span class="token operator"><</span>div <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"board"</span> <span class="token operator">:</span><span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"{solved: props.sudoku.solvedTime}"</span><span class="token operator">></span>
<span class="token operator"><</span>div <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"row"</span> v<span class="token operator">-</span><span class="token keyword">for</span><span class="token operator">=</span><span class="token string">"row in props.sudoku.rows"</span> <span class="token operator">:</span>key<span class="token operator">=</span><span class="token string">"row.index"</span><span class="token operator">></span>
<span class="token operator"><</span>SudokuField v<span class="token operator">-</span><span class="token keyword">for</span><span class="token operator">=</span><span class="token string">"field in row.cols"</span> <span class="token operator">:</span>key<span class="token operator">=</span><span class="token string">"field.col"</span> <span class="token operator">:</span>field<span class="token operator">=</span><span class="token string">"field"</span> <span class="token operator">:</span>onChange<span class="token operator">=</span><span class="token string">"props.onChange"</span><span class="token operator">/</span><span class="token operator">></span>
<span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span>
<span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span>
<span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span>
<span class="token operator"><</span><span class="token operator">/</span>main<span class="token operator">></span>
<span class="token operator"><</span><span class="token operator">/</span>template<span class="token operator">></span></code></pre>
<p>Let's recap where we are right now. We have a board filled with input fields, a timer keeping track of our play time, and a result view to display when the game is won. How do we know when the game is won though? Well, let's revisit those previous snippets <code>App.vue</code>.</p>
<h2 id="checking-solutions" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2022/03/03/making-a-sudoku-app-with-vue/#checking-solutions">Checking Solutions</a></h2>
<p>Back in <code>lib/sudoku.js</code> we need to define the <code>checkSolution</code> function and export it. This function will accept a Sudoku object - the same being played in your current game - and compare it to said Sudoku's solution. We already have the solution on-hand because it gets stored when the Sudoku is initially generated, but we need to re-flatten our playable Sudoku to properly compare it back to the flat solution array.</p>
<pre class="language-js"><code class="language-js"><span class="token comment">// lib/sudoku.js</span>
<span class="token comment">/**
* Evaluate the current solution against the solution
* @param sudoku
* @returns {boolean}
*/</span>
<span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token function">checkSolution</span><span class="token punctuation">(</span><span class="token parameter">sudoku</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> candidate <span class="token operator">=</span> sudoku<span class="token punctuation">.</span>rows
<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">row</span><span class="token punctuation">)</span> <span class="token operator">=></span> row<span class="token punctuation">.</span>cols<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">col</span><span class="token punctuation">)</span> <span class="token operator">=></span> col<span class="token punctuation">.</span>value<span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">flat</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">let</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator"><</span> candidate<span class="token punctuation">.</span>length<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>candidate<span class="token punctuation">[</span>i<span class="token punctuation">]</span> <span class="token operator">===</span> <span class="token keyword">null</span> <span class="token operator">||</span> candidate<span class="token punctuation">[</span>i<span class="token punctuation">]</span> <span class="token operator">!==</span> sudoku<span class="token punctuation">.</span>solution<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token boolean">false</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token keyword">return</span> <span class="token boolean">true</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>We use a similar method to <em>generate</em> the solution with <code>solveSudoku</code> when you cheat in the game.</p>
<pre class="language-js"><code class="language-js"><span class="token comment">// App.vue</span>
<span class="token operator"><</span>script setup<span class="token operator">></span>
<span class="token keyword">import</span> <span class="token punctuation">{</span>reactive<span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'vue'</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span>generateSudoku<span class="token punctuation">,</span> checkSolution<span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'./lib/sudoku'</span>
<span class="token keyword">import</span> SudokuBoard <span class="token keyword">from</span> <span class="token string">'./components/SudokuBoard.vue'</span>
<span class="token keyword">const</span> store <span class="token operator">=</span> <span class="token punctuation">{</span>
<span class="token literal-property property">state</span><span class="token operator">:</span> <span class="token function">reactive</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
<span class="token literal-property property">sudoku</span><span class="token operator">:</span> <span class="token function">generateSudoku</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token literal-property property">showProgress</span><span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span>
<span class="token literal-property property">previousSudoku</span><span class="token operator">:</span> <span class="token keyword">null</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token comment">/* snip */</span>
<span class="token comment">/**
* Instantly solve the sudoku
* @function
*/</span>
<span class="token function">solveSudoku</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
store<span class="token punctuation">.</span>state<span class="token punctuation">.</span>sudoku<span class="token punctuation">.</span>rows<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token parameter">row</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
row<span class="token punctuation">.</span>cols<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token parameter">col</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
col<span class="token punctuation">.</span>value <span class="token operator">=</span> store<span class="token punctuation">.</span>state<span class="token punctuation">.</span>sudoku<span class="token punctuation">.</span>solution<span class="token punctuation">[</span>col<span class="token punctuation">.</span>row <span class="token operator">*</span> <span class="token number">9</span> <span class="token operator">+</span> col<span class="token punctuation">.</span>col<span class="token punctuation">]</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
store<span class="token punctuation">.</span>state<span class="token punctuation">.</span>sudoku<span class="token punctuation">.</span>solvedTime <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Date</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
store<span class="token punctuation">.</span>state<span class="token punctuation">.</span>sudoku<span class="token punctuation">.</span>shareUrl <span class="token operator">=</span> <span class="token function">shareUrl</span><span class="token punctuation">(</span>store<span class="token punctuation">.</span>state<span class="token punctuation">.</span>sudoku<span class="token punctuation">)</span>
store<span class="token punctuation">.</span>state<span class="token punctuation">.</span>sudoku<span class="token punctuation">.</span>cheated <span class="token operator">=</span> <span class="token boolean">true</span> <span class="token comment">// This is what shows different content in the Result component</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token operator"><</span><span class="token operator">/</span>script<span class="token operator">></span></code></pre>
<p>At this point, we <em>should</em> have a playable game of Sudoku that checks for a solution with every input change event. When that solution is found, our <code>Result</code> component is displayed and all is good!</p>
<p>But let's make it better.</p>
<h2 id="game-too-hard%3F-add-hints!" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2022/03/03/making-a-sudoku-app-with-vue/#game-too-hard%3F-add-hints!">Game Too Hard? Add Hints!</a></h2>
<p>We're already checking your game against the solution with every input, so we should be able to manipulate those cells on-the-fly. After being told by one player (my mom) that they filled up the board in what looks like a winning game but weren't being told as such, I added hint system. It's off by default, but turning it on will highlight cells red or green to denote wrong or right.</p>
<p>We can add a <code>highlightCell</code> function in our lib file.</p>
<pre class="language-js"><code class="language-js"><span class="token comment">// lib/sudoku.js</span>
<span class="token comment">/**
* Take the last edited field and add the proper class to it
* @param field
* @param sudoku
*/</span>
<span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token function">highlightCell</span><span class="token punctuation">(</span><span class="token parameter">field<span class="token punctuation">,</span> sudoku</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> value <span class="token operator">=</span> field<span class="token punctuation">.</span>value<span class="token punctuation">;</span>
<span class="token keyword">const</span> solvedValue <span class="token operator">=</span> sudoku<span class="token punctuation">.</span>solution<span class="token punctuation">[</span>field<span class="token punctuation">.</span>row <span class="token operator">*</span> <span class="token number">9</span> <span class="token operator">+</span> field<span class="token punctuation">.</span>col<span class="token punctuation">]</span><span class="token punctuation">;</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>value <span class="token operator">===</span> solvedValue<span class="token punctuation">)</span> <span class="token punctuation">{</span>
field<span class="token punctuation">.</span>el<span class="token punctuation">.</span>classList<span class="token punctuation">.</span><span class="token function">contains</span><span class="token punctuation">(</span><span class="token string">"wrong"</span><span class="token punctuation">)</span>
<span class="token operator">?</span> field<span class="token punctuation">.</span>el<span class="token punctuation">.</span>classList<span class="token punctuation">.</span><span class="token function">remove</span><span class="token punctuation">(</span><span class="token string">"wrong"</span><span class="token punctuation">)</span>
<span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">;</span>
field<span class="token punctuation">.</span>el<span class="token punctuation">.</span>classList<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">"correct"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
field<span class="token punctuation">.</span>el<span class="token punctuation">.</span>classList<span class="token punctuation">.</span><span class="token function">contains</span><span class="token punctuation">(</span><span class="token string">"correct"</span><span class="token punctuation">)</span>
<span class="token operator">?</span> field<span class="token punctuation">.</span>el<span class="token punctuation">.</span>classList<span class="token punctuation">.</span><span class="token function">remove</span><span class="token punctuation">(</span><span class="token string">"correct"</span><span class="token punctuation">)</span>
<span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">;</span>
field<span class="token punctuation">.</span>el<span class="token punctuation">.</span>classList<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">"wrong"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>It takes the field you changed and the overall Sudoku. It will check your field against the overall solution and either add a class of "wrong" or "correct" to the cell. We first check if one of those classes exist and remove it before adding it - this is to account for a wrong cell later on becoming a correct cell so we don't end up with double classes.</p>
<p>These are the same parameters we're using to check for a solved game, so we can add this highlight step to the same function.</p>
<pre class="language-js"><code class="language-js"><span class="token comment">// App.vue</span>
<span class="token operator"><</span>script setup<span class="token operator">></span>
<span class="token keyword">import</span> <span class="token punctuation">{</span>reactive<span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'vue'</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span>generateSudoku<span class="token punctuation">,</span> checkSolution<span class="token punctuation">,</span> highlightCell<span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'./lib/sudoku'</span>
<span class="token keyword">import</span> SudokuBoard <span class="token keyword">from</span> <span class="token string">'./components/SudokuBoard.vue'</span>
<span class="token comment">/* snip */</span>
<span class="token comment">/**
* Receives events from the individual fields to either highlight cells or check solutions
* @param e - Event
*/</span>
<span class="token function">handleChange</span><span class="token punctuation">(</span><span class="token parameter">e</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
store<span class="token punctuation">.</span>state<span class="token punctuation">.</span>sudoku<span class="token punctuation">.</span>rows<span class="token punctuation">[</span>e<span class="token punctuation">.</span>row<span class="token punctuation">]</span><span class="token punctuation">.</span>cols<span class="token punctuation">[</span>e<span class="token punctuation">.</span>col<span class="token punctuation">]</span><span class="token punctuation">.</span>value <span class="token operator">=</span> e<span class="token punctuation">.</span>value
<span class="token function">highlightCell</span><span class="token punctuation">(</span>e<span class="token punctuation">,</span> store<span class="token punctuation">.</span>state<span class="token punctuation">.</span>sudoku<span class="token punctuation">)</span> <span class="token comment">// Add the highlight step</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>store<span class="token punctuation">.</span>state<span class="token punctuation">.</span>sudoku<span class="token punctuation">.</span>solvedTime<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> solved <span class="token operator">=</span> <span class="token function">checkSolution</span><span class="token punctuation">(</span>store<span class="token punctuation">.</span>state<span class="token punctuation">.</span>sudoku<span class="token punctuation">)</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>solved<span class="token punctuation">)</span> <span class="token punctuation">{</span>
store<span class="token punctuation">.</span>state<span class="token punctuation">.</span>sudoku<span class="token punctuation">.</span>solvedTime <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Date</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
store<span class="token punctuation">.</span>state<span class="token punctuation">.</span>sudoku<span class="token punctuation">.</span>shareUrl <span class="token operator">=</span> <span class="token function">shareUrl</span><span class="token punctuation">(</span>store<span class="token punctuation">.</span>state<span class="token punctuation">.</span>sudoku<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token operator"><</span><span class="token operator">/</span>script<span class="token operator">></span></code></pre>
<p>Now we need to be able to show the highlighting when the player turns on the setting. We will do this by adding a <code>showProgress</code> boolean to the app state, and we'll alter that via checkbox in the board. We already defined <code>showProgress</code> earlier in this article, so let's add the checkbox.</p>
<p>In our board component, we'll add a <code>fieldset</code> to house our game option. When checked, we'll call <code>handleToggle</code>, which will call either the <code>enable</code> or <code>disable</code> function from the <code>progressOpts</code> prop.</p>
<pre class="language-js"><code class="language-js"><span class="token comment">// components/SudokuBoard.vue</span>
<span class="token operator"><</span>script setup<span class="token operator">></span>
<span class="token keyword">import</span> SudokuField <span class="token keyword">from</span> <span class="token string">'./SudokuField.vue'</span>
<span class="token keyword">import</span> Timer <span class="token keyword">from</span> <span class="token string">'./Timer.vue'</span>
<span class="token keyword">import</span> Result <span class="token keyword">from</span> <span class="token string">'./Result.vue'</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span>reactive<span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"vue"</span>
<span class="token keyword">const</span> props <span class="token operator">=</span> <span class="token function">defineProps</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
<span class="token literal-property property">sudoku</span><span class="token operator">:</span> Object<span class="token punctuation">,</span>
<span class="token literal-property property">onChange</span><span class="token operator">:</span> Function<span class="token punctuation">,</span>
<span class="token literal-property property">solver</span><span class="token operator">:</span> Function<span class="token punctuation">,</span>
<span class="token literal-property property">reset</span><span class="token operator">:</span> Function<span class="token punctuation">,</span>
<span class="token literal-property property">progress</span><span class="token operator">:</span> Boolean<span class="token punctuation">,</span>
<span class="token literal-property property">progressOpts</span><span class="token operator">:</span> Object<span class="token punctuation">,</span>
<span class="token literal-property property">restore</span><span class="token operator">:</span> Function<span class="token punctuation">,</span>
<span class="token literal-property property">previous</span><span class="token operator">:</span> Object<span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token keyword">const</span> toggle <span class="token operator">=</span> <span class="token function">reactive</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token literal-property property">checked</span><span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token keyword">function</span> <span class="token function">handleToggle</span><span class="token punctuation">(</span><span class="token parameter">e</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>e<span class="token punctuation">.</span>target<span class="token punctuation">.</span>value<span class="token punctuation">)</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>toggle<span class="token punctuation">.</span>checked<span class="token punctuation">)</span> <span class="token punctuation">{</span>
props<span class="token punctuation">.</span>progressOpts<span class="token punctuation">.</span><span class="token function">enable</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
props<span class="token punctuation">.</span>progressOpts<span class="token punctuation">.</span><span class="token function">disable</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token operator"><</span><span class="token operator">/</span>script<span class="token operator">></span>
<span class="token operator"><</span>template<span class="token operator">></span>
<span class="token operator"><</span>main <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"main"</span><span class="token operator">></span>
<span class="token operator"><</span>Timer v<span class="token operator">-</span><span class="token keyword">if</span><span class="token operator">=</span><span class="token string">"!props.sudoku.solvedTime"</span> <span class="token operator">:</span>start<span class="token operator">=</span><span class="token string">"props.sudoku.startTime"</span><span class="token operator">/</span><span class="token operator">></span>
<span class="token operator"><</span>Result v<span class="token operator">-</span><span class="token keyword">if</span><span class="token operator">=</span><span class="token string">"props.sudoku.solvedTime"</span> <span class="token operator">:</span>sudoku<span class="token operator">=</span><span class="token string">"props.sudoku"</span><span class="token operator">/</span><span class="token operator">></span>
<span class="token operator"><</span>div <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"wrapper"</span><span class="token operator">></span>
<span class="token operator"><</span>div <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"board"</span> <span class="token operator">:</span><span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"{solved: props.sudoku.solvedTime}"</span><span class="token operator">></span>
<span class="token operator"><</span>div <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"row"</span> v<span class="token operator">-</span><span class="token keyword">for</span><span class="token operator">=</span><span class="token string">"row in props.sudoku.rows"</span> <span class="token operator">:</span>key<span class="token operator">=</span><span class="token string">"row.index"</span><span class="token operator">></span>
<span class="token operator"><</span>SudokuField v<span class="token operator">-</span><span class="token keyword">for</span><span class="token operator">=</span><span class="token string">"field in row.cols"</span> <span class="token operator">:</span>key<span class="token operator">=</span><span class="token string">"field.col"</span> <span class="token operator">:</span>field<span class="token operator">=</span><span class="token string">"field"</span> <span class="token operator">:</span>onChange<span class="token operator">=</span><span class="token string">"props.onChange"</span><span class="token operator">/</span><span class="token operator">></span>
<span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span>
<span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span>
<span class="token operator"><</span>div <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"actions"</span><span class="token operator">></span>
<span class="token operator"><</span>fieldset <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"options"</span><span class="token operator">></span>
<span class="token operator"><</span>legend<span class="token operator">></span>Game Options<span class="token operator"><</span><span class="token operator">/</span>legend<span class="token operator">></span>
<span class="token operator"><</span>label <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"switch"</span> <span class="token keyword">for</span><span class="token operator">=</span><span class="token string">"progress-toggle"</span><span class="token operator">></span>
<span class="token operator"><</span>span <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"switch__label"</span><span class="token operator">></span>Color Clues 🔍<span class="token operator"><</span><span class="token operator">/</span>span<span class="token operator">></span>
<span class="token operator"><</span>input type<span class="token operator">=</span><span class="token string">"checkbox"</span> name<span class="token operator">=</span><span class="token string">"Toggle Cell Highlighting"</span> id<span class="token operator">=</span><span class="token string">"progress-toggle"</span> v<span class="token operator">-</span>model<span class="token operator">=</span><span class="token string">"toggle.checked"</span>
@change<span class="token operator">=</span><span class="token string">"handleToggle"</span><span class="token operator">></span>
<span class="token operator"><</span>span <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"slider"</span><span class="token operator">></span><span class="token operator"><</span><span class="token operator">/</span>span<span class="token operator">></span>
<span class="token operator"><</span><span class="token operator">/</span>label<span class="token operator">></span>
<span class="token operator"><</span><span class="token operator">/</span>fieldset<span class="token operator">></span>
<span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span>
<span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span>
<span class="token operator"><</span><span class="token operator">/</span>main<span class="token operator">></span>
<span class="token operator"><</span><span class="token operator">/</span>template<span class="token operator">></span></code></pre>
<p><code>progressOpts</code> is a function in our app state that we pass as a prop to the board.</p>
<pre class="language-js"><code class="language-js"><span class="token comment">// App.vue</span>
<span class="token comment">/**
* Determines whether to show the highlighted cells
*/</span>
<span class="token literal-property property">progressOptions</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token function-variable function">enable</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
store<span class="token punctuation">.</span>state<span class="token punctuation">.</span>showProgress <span class="token operator">=</span> <span class="token boolean">true</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token function-variable function">disable</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
store<span class="token punctuation">.</span>state<span class="token punctuation">.</span>showProgress <span class="token operator">=</span> <span class="token boolean">false</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span></code></pre>
<p>We're not done yet! We need to only apply the highlighting CSS when <code>showProgress</code> is true. We do that with <code>v-if</code>. In <code>App.vue</code>, we add a conditional component at the top of the template to handle this.</p>
<pre class="language-js"><code class="language-js"><span class="token comment">// App.vue</span>
<span class="token operator"><</span>template<span class="token operator">></span>
<span class="token operator"><</span>component <span class="token operator">:</span>is<span class="token operator">=</span><span class="token string">"'style'"</span> v<span class="token operator">-</span><span class="token keyword">if</span><span class="token operator">=</span><span class="token string">"store.state.showProgress"</span><span class="token operator">></span>
<span class="token punctuation">.</span>field<span class="token punctuation">.</span>wrong<span class="token operator">:</span><span class="token function">not</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">[</span>readonly<span class="token punctuation">]</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
background<span class="token operator">-</span>color<span class="token operator">:</span> <span class="token function">rgb</span><span class="token punctuation">(</span><span class="token number">255</span> <span class="token number">0</span> <span class="token number">0</span> <span class="token operator">/</span> <span class="token number">0.3</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">.</span>field<span class="token punctuation">.</span>correct<span class="token operator">:</span><span class="token function">not</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">[</span>readonly<span class="token punctuation">]</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
background<span class="token operator">-</span>color<span class="token operator">:</span> <span class="token function">rgba</span><span class="token punctuation">(</span><span class="token number">0</span> <span class="token number">255</span> <span class="token number">0</span> <span class="token operator">/</span> <span class="token number">0.3</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token operator"><</span><span class="token operator">/</span>component<span class="token operator">></span>
<span class="token operator"><</span>header <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"header"</span><span class="token operator">></span>
<span class="token operator"><</span>h1<span class="token operator">></span>Sudoku<span class="token operator"><</span><span class="token operator">/</span>h1<span class="token operator">></span>
<span class="token operator"><</span><span class="token operator">/</span>header<span class="token operator">></span>
<span class="token operator"><</span>SudokuBoard <span class="token operator">:</span>sudoku<span class="token operator">=</span><span class="token string">"store.state.sudoku"</span> <span class="token operator">:</span>onChange<span class="token operator">=</span><span class="token string">"store.handleChange"</span> <span class="token operator">:</span>solver<span class="token operator">=</span><span class="token string">"store.solveSudoku"</span>
<span class="token operator">:</span>reset<span class="token operator">=</span><span class="token string">"store.resetSudoku"</span> <span class="token operator">:</span>progressOpts<span class="token operator">=</span><span class="token string">"store.progressOptions"</span> <span class="token operator">:</span>progress<span class="token operator">=</span><span class="token string">"store.state.showProgress"</span>
<span class="token operator">:</span>restore<span class="token operator">=</span><span class="token string">"store.restoreSudoku"</span> <span class="token operator">:</span>previous<span class="token operator">=</span><span class="token string">"store.state.previousSudoku"</span><span class="token operator">/</span><span class="token operator">></span>
<span class="token operator"><</span>ReloadPrompt<span class="token operator">/</span><span class="token operator">></span>
<span class="token operator"><</span><span class="token operator">/</span>template<span class="token operator">></span></code></pre>
<p>There you have it! Hints.</p>
<h2 id="challenge-your-friends" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2022/03/03/making-a-sudoku-app-with-vue/#challenge-your-friends">Challenge Your Friends</a></h2>
<p>This game feature came from Matt's video and I'll be honest that I don't <em>fully</em> understand the APIs being used. That said, let's add it in.</p>
<p>We start with two new functions in our lib:</p>
<pre class="language-js"><code class="language-js"><span class="token comment">// lib/sudoku.js</span>
<span class="token comment">/**
* Create a URL for your sudoku to share with someone else
* @param sudoku
* @returns {string}
*/</span>
<span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token function">shareUrl</span><span class="token punctuation">(</span><span class="token parameter">sudoku</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> data <span class="token operator">=</span> <span class="token punctuation">{</span>
<span class="token literal-property property">raw</span><span class="token operator">:</span> sudoku<span class="token punctuation">.</span>raw<span class="token punctuation">,</span>
<span class="token literal-property property">startTime</span><span class="token operator">:</span> sudoku<span class="token punctuation">.</span>startTime<span class="token punctuation">,</span>
<span class="token literal-property property">solvedTime</span><span class="token operator">:</span> sudoku<span class="token punctuation">.</span>solvedTime<span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> query <span class="token operator">=</span> <span class="token function">btoa</span><span class="token punctuation">(</span><span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">return</span> document<span class="token punctuation">.</span>location<span class="token punctuation">.</span>href<span class="token punctuation">.</span><span class="token function">replace</span><span class="token punctuation">(</span><span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">\?.+$</span><span class="token regex-delimiter">/</span></span><span class="token punctuation">,</span> <span class="token string">""</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">?sudoku=</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>query<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">function</span> <span class="token function">extractUrlData</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> match <span class="token operator">=</span> document<span class="token punctuation">.</span>location<span class="token punctuation">.</span>search<span class="token punctuation">.</span><span class="token function">match</span><span class="token punctuation">(</span><span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">\?sudoku=([^&]+)</span><span class="token regex-delimiter">/</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>match<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">parse</span><span class="token punctuation">(</span><span class="token function">atob</span><span class="token punctuation">(</span>match<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">return</span> <span class="token keyword">null</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>It uses the Web APIs <a href="https://developer.mozilla.org/en-US/docs/Web/API/btoa"><code>btoa</code></a> and <a href="https://developer.mozilla.org/en-US/docs/Web/API/atob"><code>atob</code></a>. The former creates a Base64-encoded ASCII string from a binary string while the latter decodes it. We first create an object called <code>data</code> out of our Sudoku, then use <code>JSON.stringify</code> on that data, which becomes the binary string we encode with <code>btoa</code>. If we revisit our generator function, you can see <code>extractUrlData</code> in use as a way to generate your game if it was shared with you.</p>
<pre class="language-js"><code class="language-js"><span class="token comment">// lib/sudoku.js</span>
<span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token function">generateSudoku</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> fromUrl <span class="token operator">=</span> <span class="token function">extractUrlData</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token keyword">const</span> raw <span class="token operator">=</span> fromUrl <span class="token operator">?</span> fromUrl<span class="token punctuation">.</span>raw <span class="token operator">:</span> <span class="token function">makepuzzle</span><span class="token punctuation">(</span><span class="token punctuation">)</span></code></pre>
<p>If <code>extractUrlData</code> returns anything, it is stored as <code>raw</code>; otherwise, make a fresh puzzle.</p>
<h2 id="you're-stuck-and-want-a-new-game" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2022/03/03/making-a-sudoku-app-with-vue/#you're-stuck-and-want-a-new-game">You're Stuck and Want a New Game</a></h2>
<p>I can't control how difficult the Sudoku actually is - and I've been told the game is <strong>hard</strong> - but I <em>can</em> add an option to make a new puzzle if you want. We can keep this function in the app state with the rest of our core functions.</p>
<pre class="language-js"><code class="language-js"><span class="token comment">// App.vue</span>
<span class="token comment">/**
* Start over with a fresh board
*/</span>
<span class="token function">resetSudoku</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>store<span class="token punctuation">.</span>state<span class="token punctuation">.</span>sudoku<span class="token punctuation">.</span>solvedTime<span class="token punctuation">)</span> <span class="token punctuation">{</span>
store<span class="token punctuation">.</span>state<span class="token punctuation">.</span>previousSudoku <span class="token operator">=</span> store<span class="token punctuation">.</span>state<span class="token punctuation">.</span>sudoku
<span class="token punctuation">}</span>
<span class="token keyword">const</span> allCorrectFields <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelectorAll</span><span class="token punctuation">(</span><span class="token string">'.field.correct'</span><span class="token punctuation">)</span>
allCorrectFields<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token parameter">field</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>store<span class="token punctuation">.</span>state<span class="token punctuation">.</span>sudoku<span class="token punctuation">.</span>solvedTime<span class="token punctuation">)</span> field<span class="token punctuation">.</span>classList<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">"previousCorrect"</span><span class="token punctuation">)</span>
field<span class="token punctuation">.</span>classList<span class="token punctuation">.</span><span class="token function">remove</span><span class="token punctuation">(</span><span class="token string">"correct"</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token keyword">const</span> allWrongFields <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelectorAll</span><span class="token punctuation">(</span><span class="token string">'.field.wrong'</span><span class="token punctuation">)</span>
allWrongFields<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token parameter">field</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>store<span class="token punctuation">.</span>state<span class="token punctuation">.</span>sudoku<span class="token punctuation">.</span>solvedTime<span class="token punctuation">)</span> field<span class="token punctuation">.</span>classList<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">"previousWrong"</span><span class="token punctuation">)</span>
field<span class="token punctuation">.</span>classList<span class="token punctuation">.</span><span class="token function">remove</span><span class="token punctuation">(</span><span class="token string">"wrong"</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
store<span class="token punctuation">.</span>state<span class="token punctuation">.</span>sudoku <span class="token operator">=</span> <span class="token function">generateSudoku</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span></code></pre>
<p>I'll run through this step by step because it's a lot.</p>
<ol>
<li>Check to see if there's a solved time - meaning your game is completed. If your game in incomplete, we store your entire Sudoku in state as <code>previousSudoku</code>. We'll use this in the next section.</li>
<li>Traverse the DOM looking for any input fields with the class "correct" and swap classes from "correct" to "previousCorrect." Do the same thing for fields with the class "wrong." Again, we'll use this later.</li>
<li>Replace the current Sudoku in state with a new one by calling <code>generateSudoku</code>. This has the side effects of also resetting your game clock, so we're all set to go!</li>
</ol>
<p>We'll pass this function as a prop to our board and add a button to activate it.</p>
<pre class="language-js"><code class="language-js"><span class="token comment">// components/SudokuBoard.vue</span>
<span class="token operator"><</span>template<span class="token operator">></span>
<span class="token operator"><</span>main <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"main"</span><span class="token operator">></span>
<span class="token operator"><</span><span class="token operator">!</span><span class="token operator">--</span> snip <span class="token operator">--</span><span class="token operator">></span>
<span class="token operator"><</span>div <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"buttons"</span><span class="token operator">></span>
<span class="token operator"><</span>button <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"solve"</span> @click<span class="token operator">=</span><span class="token string">"props.solver"</span><span class="token operator">></span>Solve it Magically<span class="token operator">!</span><span class="token operator"><</span><span class="token operator">/</span>button<span class="token operator">></span>
<span class="token operator"><</span>button <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"reset"</span> @click<span class="token operator">=</span><span class="token string">"props.reset"</span><span class="token operator">></span>New Puzzle<span class="token operator"><</span><span class="token operator">/</span>button<span class="token operator">></span>
<span class="token operator"><</span>button <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"restore"</span> @click<span class="token operator">=</span><span class="token string">"props.restore"</span> v<span class="token operator">-</span><span class="token keyword">if</span><span class="token operator">=</span><span class="token string">"props.previous"</span><span class="token operator">></span>Restore Your Last Board<span class="token operator"><</span><span class="token operator">/</span>button<span class="token operator">></span>
<span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span>
<span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span>
<span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span>
<span class="token operator"><</span><span class="token operator">/</span>main<span class="token operator">></span>
<span class="token operator"><</span><span class="token operator">/</span>template<span class="token operator">></span></code></pre>
<h2 id="oops%2C-you-accidentally-reset-your-game" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2022/03/03/making-a-sudoku-app-with-vue/#oops%2C-you-accidentally-reset-your-game">Oops, you Accidentally Reset Your Game</a></h2>
<p>You're out of luck then, that's your fault.</p>
<p>I'll help you out though. Remember how we stored your Sudoku as <code>previousSudoku</code> and swapped the field class names with "previousCorrect" and "previousWrong?" Let's use those in a new function called <code>restoreSudoku</code>.</p>
<pre class="language-js"><code class="language-js"><span class="token comment">// App.vue</span>
<span class="token comment">/**
* Restore your last board if you created a new one by mistake
*/</span>
<span class="token function">restoreSudoku</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
store<span class="token punctuation">.</span>state<span class="token punctuation">.</span>sudoku <span class="token operator">=</span> store<span class="token punctuation">.</span>state<span class="token punctuation">.</span>previousSudoku
store<span class="token punctuation">.</span>state<span class="token punctuation">.</span>previousSudoku <span class="token operator">=</span> <span class="token keyword">null</span>
<span class="token keyword">const</span> allCorrectFields <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelectorAll</span><span class="token punctuation">(</span><span class="token string">'.field.correct'</span><span class="token punctuation">)</span>
allCorrectFields<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token parameter">field</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
field<span class="token punctuation">.</span>classList<span class="token punctuation">.</span><span class="token function">remove</span><span class="token punctuation">(</span><span class="token string">"correct"</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token keyword">const</span> allWrongFields <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelectorAll</span><span class="token punctuation">(</span><span class="token string">'.field.wrong'</span><span class="token punctuation">)</span>
allWrongFields<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token parameter">field</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
field<span class="token punctuation">.</span>classList<span class="token punctuation">.</span><span class="token function">remove</span><span class="token punctuation">(</span><span class="token string">"wrong"</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token keyword">const</span> previousCorrectFields <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelectorAll</span><span class="token punctuation">(</span><span class="token string">'.previousCorrect'</span><span class="token punctuation">)</span>
previousCorrectFields<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token parameter">field</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
field<span class="token punctuation">.</span>classList<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">"correct"</span><span class="token punctuation">)</span>
field<span class="token punctuation">.</span>classList<span class="token punctuation">.</span><span class="token function">remove</span><span class="token punctuation">(</span><span class="token string">"previousCorrect"</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token keyword">const</span> previousWrongFields <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelectorAll</span><span class="token punctuation">(</span><span class="token string">'.previousWrong'</span><span class="token punctuation">)</span>
previousWrongFields<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token parameter">field</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
field<span class="token punctuation">.</span>classList<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">"wrong"</span><span class="token punctuation">)</span>
field<span class="token punctuation">.</span>classList<span class="token punctuation">.</span><span class="token function">remove</span><span class="token punctuation">(</span><span class="token string">"previousWrong"</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span></code></pre>
<p>The steps are as follows:</p>
<ol>
<li>Take your previous Sudoku and put it back in as your current Sudoku, then <code>null</code> your <code>previousSudoku</code>.</li>
<li>Look for all fields with classes of "correct" and "wrong" and remove them.</li>
<li>Find all your "previousCorrect" and "previousWrong" fields and replace them with "correct" and "wrong."</li>
</ol>
<p>You now have your game restored! This button can live beside your reset button as I showed in a previous snippet.</p>
<h2 id="a-complete-sudoku-game" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2022/03/03/making-a-sudoku-app-with-vue/#a-complete-sudoku-game">A Complete Sudoku Game</a></h2>
<p>If you followed along, you should have a game nearly identical to the game I made, aside from any styling you want applied. Use <code>npm run dev</code> to spin up your dev server and see it in all its glory. You can <a href="https://github.com/troyvassalotti/sudoku">check the repo</a> if you want to see all the code as well. I hope this helps someone else out there looking to make a Sudoku app in Vue.</p>
Plvylist is on npm2021-12-05T00:00:00Zhttps://www.troyv.dev/2021/12/05/plvylist-is-on-npm/
<!-- @format -->
<p><code>npm i plvylist</code></p>
<p>That's all you need to do to use <a href="https://www.troyv.dev/2021/04/13/plvylist-is-now-a-web-component/">Plvylist</a> in your own projects now. This is my first time publishing a package to npm so, uh, hopefully it works! I promise I did a test of adding it to a Vite + Vue project before writing this post and it worked out. Here's the code I used for said test:</p>
<pre class="language-js"><code class="language-js"><span class="token comment">// src/App.vue</span>
<span class="token operator"><</span>script setup<span class="token operator">></span>
<span class="token keyword">import</span> <span class="token string">'plvylist/dist/plvylist-component'</span>
<span class="token operator"><</span><span class="token operator">/</span>script<span class="token operator">></span>
<span class="token operator"><</span>template<span class="token operator">></span>
<span class="token operator"><</span>plvylist<span class="token operator">-</span>player tracks<span class="token operator">=</span><span class="token string">"tracks.json"</span><span class="token operator">></span><span class="token operator"><</span><span class="token operator">/</span>plvylist<span class="token operator">-</span>player<span class="token operator">></span>
<span class="token operator"><</span><span class="token operator">/</span>template<span class="token operator">></span></code></pre>
<p>Running <code>npm run dev</code> produced the expected result of Plvylist all alone in my app. Finger's crossed it's that straightforward for other use cases, but please submit any bugs to the <a href="https://github.com/troyvassalotti/plvylist/issues">repo's issues</a>.</p>
Single File Components in Nunjucks2021-11-13T00:00:00Zhttps://www.troyv.dev/2021/11/13/single-file-components-in-nunjucks/
<!-- @format -->
<p>If you've worked with Vue, chances are you're familiar with its <a href="https://v3.vuejs.org/guide/single-file-component.html#introduction">Single File Components</a> (SFC for short). If you haven't worked with Vue, the idea of a SFC can be boiled down to encapsulation of the template, logic, <strong>and</strong> styling of a component to a single file.</p>
<p>The site you're looking at right now is an <a href="https://www.11ty.dev/">Eleventy</a> site using <a href="https://mozilla.github.io/nunjucks/">Nunjucks</a> templates. I love the way Eleventy handles static site generation, and I think Nunjucks is an incredible templating language for my use cases. I've been working on my website for a couple years now and experimented with a variety of tooling methods for things like SASS compilation, asset minification, critical inlining, etc. The one thing I was missing was a way to create single file components like I've been able to do in Vue.</p>
<h2 id="moving-towards-native-nunjucks" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2021/11/13/single-file-components-in-nunjucks/#moving-towards-native-nunjucks">Moving Towards Native Nunjucks</a></h2>
<p>I didn't branch out of the basic uses of Nunjucks with Eleventy until the last few months. I relied heavily on layout files defined in the front matter for template inheritance until I hit an issue where I realized Nunjucks' <code>extends</code> feature was the solution. This change involved a level of abstraction away from Eleventy's logic and into what comes bundled with Nunjucks.</p>
<p>Template inheritance with <code>extends</code> meant I could define custom blocks aside from the <code>{{ content }}</code> (which is used to house any content in a template with the <code>layout</code> front matter) in my Nunjucks layouts and templates that can pass into each other. I learned that I couldn't use front matter for template inheritance if I also wanted to define custom blocks.</p>
<pre class="language-twig"><code class="language-twig">---
layout: base.njk
---
<span class="token twig language-twig"><span class="token delimiter punctuation">{%</span> <span class="token tag-name keyword">block</span> content <span class="token delimiter punctuation">%}</span></span>
<span class="token comment"><!-- HTML and Nunjucks in here --></span>
<span class="token twig language-twig"><span class="token delimiter punctuation">{%</span> <span class="token tag-name keyword">endblock</span> <span class="token delimiter punctuation">%}</span></span>
<span class="token twig language-twig"><span class="token delimiter punctuation">{%</span> <span class="token tag-name keyword">block</span> styles <span class="token delimiter punctuation">%}</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>style</span><span class="token punctuation">></span></span><span class="token style"><span class="token language-css">
// CSS rules in here<span class="token punctuation">,</span> except this wouldn't pass into base.njk even if it had a defined <span class="token twig language-twig"><span class="token delimiter punctuation">{%</span> <span class="token tag-name keyword">block</span> styles <span class="token delimiter punctuation">%}</span></span> present
</span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>style</span><span class="token punctuation">></span></span>
<span class="token twig language-twig"><span class="token delimiter punctuation">{%</span> <span class="token tag-name keyword">endblock</span> <span class="token delimiter punctuation">%}</span></span></code></pre>
<p>The above example is showing that even if my file defines code in a <code>styles</code> block, and if <code>base.njk</code> has a <code>styles</code> block defined to house it, nothing would actually be passed in. To pass content into predefined blocks, you have to use <code>extends</code>.</p>
<h2 id="using-extends-to-create-sfcs" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2021/11/13/single-file-components-in-nunjucks/#using-extends-to-create-sfcs">Using <code>extends</code> to Create SFCs</a></h2>
<p>My old solution to passing page-specific JS or CSS was to create files in my <code>_includes</code> directory with the same name as the slug it belongs to, and define logic in my base layout to look for and include any JS or CSS files with the same filename as the slug of the page being built. It wasn't perfect, but it got the job done.</p>
<p>My main issue with this method was that I was creating assets specific to <em>a single file</em> but not housing them with the HTML of that file. Everything was separated in a way that made editing a single page with custom CSS or JS require opening three different files for one page. I knew there had to be a better way than that.</p>
<p>It turns out there is.</p>
<h3 id="what-does-extends-do%3F" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2021/11/13/single-file-components-in-nunjucks/#what-does-extends-do%3F">What Does <code>extends</code> Do?</a></h3>
<p>When you create a Nunjucks template, you can use <code>extends</code> at the top of the file to specify what layout or base the file should adhere to. This is essentially what the <code>layout</code> field in front matter is doing, except it also adheres to defined blocks.</p>
<p>In my <code>base.njk</code> layout - the primary layout all pages file into - I defined three blocks: content, style, and script.</p>
<pre class="language-twig"><code class="language-twig"><span class="token twig language-twig"><span class="token comment">{# base.njk #}</span></span>
<span class="token comment"><!-- snip --></span>
<span class="token twig language-twig"><span class="token delimiter punctuation">{%-</span> <span class="token tag-name keyword">block</span> style <span class="token delimiter punctuation">%}</span></span>
<span class="token twig language-twig"><span class="token delimiter punctuation">{%-</span> <span class="token tag-name keyword">if</span> css <span class="token delimiter punctuation">%}</span></span>
<span class="token twig language-twig"><span class="token delimiter punctuation">{%-</span> <span class="token tag-name keyword">if</span> site<span class="token punctuation">.</span>environment <span class="token operator">==</span> <span class="token string"><span class="token punctuation">"</span>production<span class="token punctuation">"</span></span> <span class="token delimiter punctuation">%}</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>style</span><span class="token punctuation">></span></span><span class="token style"><span class="token language-css"><span class="token twig language-twig"><span class="token delimiter punctuation">{{</span> css <span class="token operator">|</span> cssmin <span class="token operator">|</span> safe <span class="token delimiter punctuation">}}</span></span></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>style</span><span class="token punctuation">></span></span>
<span class="token twig language-twig"><span class="token delimiter punctuation">{%</span> <span class="token tag-name keyword">elseif</span> site<span class="token punctuation">.</span>environment <span class="token operator">==</span> <span class="token string"><span class="token punctuation">"</span>development<span class="token punctuation">"</span></span> <span class="token delimiter punctuation">%}</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>style</span><span class="token punctuation">></span></span><span class="token style"><span class="token language-css"><span class="token twig language-twig"><span class="token delimiter punctuation">{{</span> css <span class="token operator">|</span> safe <span class="token delimiter punctuation">}}</span></span></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>style</span><span class="token punctuation">></span></span>
<span class="token twig language-twig"><span class="token delimiter punctuation">{%</span> <span class="token tag-name keyword">endif</span> <span class="token delimiter punctuation">-%}</span></span>
<span class="token twig language-twig"><span class="token delimiter punctuation">{%</span> <span class="token tag-name keyword">endif</span> <span class="token delimiter punctuation">-%}</span></span>
<span class="token twig language-twig"><span class="token delimiter punctuation">{%</span> <span class="token tag-name keyword">endblock</span> <span class="token delimiter punctuation">-%}</span></span>
<span class="token comment"><!-- snip --></span>
<span class="token twig language-twig"><span class="token delimiter punctuation">{%</span> <span class="token tag-name keyword">block</span> content <span class="token delimiter punctuation">%}</span></span><span class="token twig language-twig"><span class="token delimiter punctuation">{%</span> <span class="token tag-name keyword">endblock</span> <span class="token delimiter punctuation">%}</span></span>
<span class="token twig language-twig"><span class="token delimiter punctuation">{%-</span> <span class="token tag-name keyword">block</span> script <span class="token delimiter punctuation">%}</span></span>
<span class="token twig language-twig"><span class="token delimiter punctuation">{%-</span> <span class="token tag-name keyword">if</span> js <span class="token delimiter punctuation">%}</span></span>
<span class="token twig language-twig"><span class="token delimiter punctuation">{%-</span> <span class="token tag-name keyword">if</span> site<span class="token punctuation">.</span>environment <span class="token operator">==</span> <span class="token string"><span class="token punctuation">"</span>production<span class="token punctuation">"</span></span> <span class="token delimiter punctuation">%}</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>module<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><span class="token twig language-twig"><span class="token delimiter punctuation">{{</span> js <span class="token operator">|</span> jsmin <span class="token operator">|</span> safe <span class="token delimiter punctuation">}}</span></span></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span>
<span class="token twig language-twig"><span class="token delimiter punctuation">{%-</span> <span class="token tag-name keyword">elseif</span> site<span class="token punctuation">.</span>environment <span class="token operator">==</span> <span class="token string"><span class="token punctuation">"</span>development<span class="token punctuation">"</span></span> <span class="token delimiter punctuation">%}</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>module<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><span class="token twig language-twig"><span class="token delimiter punctuation">{{</span> js <span class="token operator">|</span> safe <span class="token delimiter punctuation">}}</span></span></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span>
<span class="token twig language-twig"><span class="token delimiter punctuation">{%</span> <span class="token tag-name keyword">endif</span> <span class="token delimiter punctuation">-%}</span></span>
<span class="token twig language-twig"><span class="token delimiter punctuation">{%</span> <span class="token tag-name keyword">endif</span> <span class="token delimiter punctuation">-%}</span></span>
<span class="token twig language-twig"><span class="token delimiter punctuation">{%</span> <span class="token tag-name keyword">endblock</span> <span class="token delimiter punctuation">-%}</span></span></code></pre>
<p>The way those blocks work is as follows:</p>
<ul>
<li>The <code>style</code> block is looking for a variable named <code>css</code>. If that variable is defined on the template, then it runs the content of that variable through as-is (development) or minified (production), passing the result into a <code><style></code> tag.</li>
<li>The <code>content</code> block will contain any HTML contained in the template's <code>content</code> block.</li>
<li>The <code>script</code> block is doing the same thing as the <code>style</code> block, except the variable it looks for is named <code>js</code>.</li>
</ul>
<p>Now take a look at an example from my homepage where I define those blocks and variables for use in the layout:</p>
<pre class="language-twig"><code class="language-twig">---
title: Welcome
description: Watch as Troy Vassalotti learns his way around a computer.
---
<span class="token twig language-twig"><span class="token delimiter punctuation">{%</span> <span class="token tag-name keyword">extends</span> <span class="token string"><span class="token punctuation">'</span>layouts/base.njk<span class="token punctuation">'</span></span> <span class="token delimiter punctuation">%}</span></span>
<span class="token twig language-twig"><span class="token delimiter punctuation">{%</span> <span class="token tag-name keyword">block</span> content <span class="token delimiter punctuation">%}</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>main</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>layout<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token comment"><!-- snip --></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>main</span><span class="token punctuation">></span></span>
<span class="token twig language-twig"><span class="token delimiter punctuation">{%</span> <span class="token tag-name keyword">endblock</span> <span class="token delimiter punctuation">%}</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>style</span><span class="token punctuation">></span></span><span class="token style"><span class="token language-css">
<span class="token selector"><span class="token twig language-twig"><span class="token delimiter punctuation">{%</span> <span class="token tag-name keyword">set</span> css <span class="token delimiter punctuation">%}</span></span>
main.layout</span> <span class="token punctuation">{</span> <span class="token property">margin-block-start</span><span class="token punctuation">:</span> 3em<span class="token punctuation">;</span> <span class="token punctuation">}</span>
// snip
<span class="token twig language-twig"><span class="token delimiter punctuation">{%</span> <span class="token tag-name keyword">endset</span> <span class="token delimiter punctuation">%}</span></span>
</span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>style</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>module<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript">
<span class="token twig language-twig"><span class="token delimiter punctuation">{%</span> <span class="token tag-name keyword">set</span> js <span class="token delimiter punctuation">%}</span></span>
<span class="token keyword">const</span> sundial <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">"#sundial"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// snip</span>
<span class="token twig language-twig"><span class="token delimiter punctuation">{%</span> <span class="token tag-name keyword">endset</span> <span class="token delimiter punctuation">%}</span></span>
</span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></code></pre>
<p>What's happening in that file now is as follows:</p>
<ul>
<li>I tell the template to extend my base layout file.</li>
<li>I define the <code>content</code> block with all my HTML and Nunjucks logic.</li>
<li>I set the <code>css</code> variable and include any critical CSS within it. The <code><style></code> tags wrapping the variable are strictly for syntax highlighting in the editor and <em>do not</em> get passed along themselves.</li>
<li>I finally set the <code>js</code> variable with the same logic as the <code>css</code> variable.</li>
</ul>
<p>The end result is a single template file with its templating HTML and critical CSS and JS contained in a single file that pass into the final layout to be processed and placed where they need to be.</p>
<h2 id="the-payoff" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2021/11/13/single-file-components-in-nunjucks/#the-payoff">The Payoff</a></h2>
<p>I realize this system may not work for everyone, but I'm extremely proud of how it turned out and believe my development workflow has only benefited from such abstractions. I'm able to deliver a performant page with inlined critical assets while encapsulating all the dependencies of a page to a single file.</p>
<p>No wonder Vue leans so heavily into the SFC system of page building.</p>
<h2 id="update-6%2F20%2F2022" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2021/11/13/single-file-components-in-nunjucks/#update-6%2F20%2F2022">Update 6/20/2022</a></h2>
<p>Yes, I know now that I could've been using Nunjucks' built-in templating features with <code>block</code>s and inheritance to do this in a better way.</p>
Using Data with Eleventy and Forestry (a charmCityJs Talk)2021-10-15T00:00:00Zhttps://www.troyv.dev/2021/10/15/using-data-with-eleventy-and-forestry-a-charmcityjs-talk/
<!-- @format -->
<p>Earlier this month, I gave a talk at <a href="https://www.meetup.com/charmcityjs/">the charmCityJs meetup</a> about how I use the powers of Eleventy and Forestry to integrate my development workflow into a CMS. I had never given a talk before, let alone try to explain how my website works to people I barely know. That said, it was a cool 15 minute experience.</p>
<p>I'd been wanting to give a talk for the meetup in the past, but I never worked up the courage to submit an idea. Sure, I work with JavaScript all the time, but that doesn't necessarily mean I know enough about it to convince someone I know what I'm talking about.</p>
<p>But that's not what the meetup is about at its core. I'm not there to try and convince people of an idea; I'm there to present something I've been working on that I think is cool and that other people might also find cool. Being able to get in front of a community of like-minded people and talk about a thing we all enjoy (I use this term lightly in regards to JS) is a fun time.</p>
<p>I won't recap my entire talk from front to back, but here's the rundown:</p>
<ol>
<li>I accidentally made my site complicated by trying out new features in the world of JS, specifically Eleventy.</li>
<li>I got tired of being distracted in my code editor when trying to make a blog post - or even forgetting what front matter fields are necessary for said post - so I needed a way to streamline the process.</li>
<li>Forestry was brought to my attention and I found it to be a nice companion to the way my site is structured.</li>
</ol>
<p>I discussed a few of the Nunjucks features used in my templates, as well as how to use front matter to its fullest. The abbreviated talk can be <a href="https://speakerdeck.com/troyvassalotti/using-data-to-create-a-cms-with-forestry-and-eleventy">found on Speaker Deck</a>. I'm looking forward to giving more talks in the future, and I encourage anyone else who's thinking the same thing to push through the imposter syndrome and give it a shot.</p>
I Don't Have A Podcast2021-09-12T00:00:00Zhttps://www.troyv.dev/2021/09/12/i-don-t-have-a-podcast/
<!-- @format -->
<blockquote>
<p>Are all your friends starting podcasts? Does that make you never want to start your own? Proclaim your dedication to never start your own podcast.</p>
</blockquote>
<p>That's the first line of text you'll find on <a href="https://idonthaveapodcast.netlify.app/">I Don't Have A Podcast</a>. I started said website after a night of talking podcasts and how overwhelming the podcast landscape is.</p>
<p>You can find a podcast for <em>literally everything</em> you could ever want. Maybe it feels like you should start your own podcast. But here's the thing: you can be a podcast lover and not host your own podcast.</p>
<p>Show your determination to never start your own podcast to the public. Pick the side of never-podcasters and don't contribute to the saturation. Fill out the form on the site and your name (or whatever name you give me) will be added to the list.</p>
<hr />
<p><em>Disclaimer</em>: I love podcasts and am not advocating for people to stop doing what they love, but I also happen to think it is funny to sign a petition announcing that you'll never start your own podcast out of spite.</p>
Plvylist Version 32021-08-21T00:00:00Zhttps://www.troyv.dev/2021/08/21/plvylist-version-3/
<!-- @format -->
<p>The last <a href="https://www.troyv.dev/2021/04/13/plvylist-is-now-a-web-component/">update on Plvylist</a> was when I turned it into a web component. That's great and all, but something wasn't sitting right with me. It felt like it involved <em>too much code</em> to do what it's doing. A lot of CSS was integrated in the component and the initialization was a hassle. Version 2 expected the user to jump through too many hoops and that's not how it's supposed to be. For example, why would I require the user to declare their tracks in the component itself??</p>
<h2 id="how-to-use-plvylist-version-3" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2021/08/21/plvylist-version-3/#how-to-use-plvylist-version-3">How to use Plvylist Version 3</a></h2>
<p>If you've got a JSON file with the following fields, then you can use Plvylist!</p>
<ol>
<li>file: a relative or absolute path to an song,</li>
<li>title: the name of the track,</li>
<li>artist: the name of the artist,</li>
<li>album: the name of the album,</li>
<li>artwork: a relative or absolute path to an image of the track's artwork.</li>
</ol>
<p>When adding <code><plvylist-player></code> to your site, give it an attribute of <code>tracks="path/to/tracks.json"</code> and it'll be fetched in the loading process. For best results, you'd also give it a placeholder image with the attribute <code>placeholder="path/to/placeholder.jpg"</code> but I'll look into providing a better default in case this isn't declared. The same options for setting a starting volume and time are in place.</p>
<p>The CSS has been trimmed significantly to remove most of the opinionated styles. It's hard to style a simple slider across browsers, but I decided it wasn't needed anymore. The recent update adding support for <code>accent-color</code> in CSS is the reason I revisited this component in the first place.</p>
<p>There were explicit widths and heights and other various styles set that were frankly annoying to try and keep up to date. The best result was to strip it all down to the essentials.</p>
<h2 id="is-it-on-npm%3F" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2021/08/21/plvylist-version-3/#is-it-on-npm%3F">Is it on npm?</a></h2>
<p>Not yet, but I'd like it to be. I was reading a few docs while writing this post and it looked like it'd take more time than I have right now.</p>
<h2 id="where-can-i-find-it-again%3F" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2021/08/21/plvylist-version-3/#where-can-i-find-it-again%3F">Where can I find it again?</a></h2>
<p>Check it out <a href="https://github.com/troyvassalotti/plvylist">on GitHub</a>! The directions are all in the readme.</p>
Over-Optimizing2021-08-15T00:00:00Zhttps://www.troyv.dev/2021/08/15/over-optimizing/
<!-- @format -->
<p>I find myself thinking too much about my projects; I want anything I make to be the best it can be. The sentiment is nice, but takes a lot of time in practice. Time, patience, mental energy, etc. - you name it. To be on a journey of perfection means to never end that journey.</p>
<p>Everything I make and put on the internet is a representation of me in one form or another, so the goal is that everything's always amazing. That said, there comes a time when I have to convince myself <em>enough is enough</em> and walk away.</p>
<p>Take either of these two projects: <a href="https://abandoned-website.netlify.app/">Abandoned Website</a> or <a href="https://pang.netlify.app/">NotSocial</a>. Both were ambitiously made using new tools for the sake of practice, but also to prove whatever worth I had at the time. I've gotten much better at this whole <em>web thing</em> since both were published and I'm constantly fighting the urge to make them better to <strong>show</strong> that I've improved.</p>
<p>Doing so would be a waste of time and I know that. My time is better spent elsewhere. It isn't only those projects though. It's also this very website or any other mini site I've created. When I learn a new optimization method, CSS enhancement, or literally anything else, then I want to use it everywhere. It'd be one thing if every site I made was a core component of my life, but they aren't.</p>
<p>I remind myself every day that old projects can remain old projects, left alone to exist. I also remind myself that it's okay for my personal website to be messy sometimes, or if not messy then not fully optimized for performance. I like keeping things simple, which means I spend a lot of time looking at my code to see where I can get rid of fluff.</p>
<p>That time spent trimming down almost every site I make is time I could've spent reading, or writing (which I am doing to make this post), or recording music. I want to admit that I've gotten better at letting things go - I've been recording my next album! But there's still room for improvement.</p>
<p>I have a handful of websites I've reserved for dedicating my time to:</p>
<ul>
<li>This one,</li>
<li><a href="https://www.frontroyalband.com/">Front Royal's</a>,</li>
<li><a href="https://www.thisisa.band/">This is a Band</a>,</li>
<li>and my solo project - troy.</li>
</ul>
<p>A couple other sites use Python programs or shell scripts that populate content into a given template, so tinkering with those is often near-impossible. Anything else - specifically anything I've coded and have access to the source of - is <em>supposed to be</em> off limits.</p>
<p>I've recently started archiving GitHub repositories as a way of showing myself they shouldn't be touched; we'll see how that goes.</p>
<p>What's the moral of the story? Maybe it's that CSS and JavaScript don't have to be perfectly minified or fine-tuned to the narrowest of details. Or maybe it's that the web is an evolving platform built on the legs of backward compatibility and any site that can stand now will continue to stand without constant care. Both of those are great points.</p>
<p>More important than those though is that the reward received from spending countless hours relentlessly trying to make something perfect is never as good as you expect. In the end, you should've picked up that book or recorded that song.</p>
Front Royal Made a Covers Album2021-07-17T00:00:00Zhttps://www.troyv.dev/2021/07/17/front-royal-made-a-covers-album/
<!-- @format -->
<p>We've been working on some new music this year. I promise we'll have an EP of new originals later this year, but we thought a collection of covers from last year's pop hits would be a fun way to hold everyone over.</p>
<p>You can find the entire set over <a href="https://frontroyalmd.bandcamp.com/">on our Bandcamp</a> or streaming <a href="https://www.frontroyalband.com/">on our website</a>. The hits include:</p>
<ol>
<li>"cardigan" - Taylor Swift</li>
<li>"Afterglow" - Ed Sheeran</li>
<li>"Always Do" - The Kid Laroi</li>
<li>"Be Kind" - Marshmello & Halsey</li>
<li>"Hold On" - Justin Bieber</li>
</ol>
<p>Thanks for listening, and be on the lookout for more later this year.</p>
Colin.swf2021-05-26T00:00:00Zhttps://www.troyv.dev/2021/05/26/colin-swf/
<!-- @format -->
<p>I found this video years ago but never saved it. Since I recently found out HBO Max has the first eight seasons of <em>Whose Line Is It Anyway?</em>, this video resurfaced in my mind. After far too many queries on search engines like "colin.mp4," "colin.avi," and "colin mochrie meme dance gif," the letters "swf" came back to me. I bookmarked the video, downloaded it to local storage, and am now writing a post in hopes of never losing it again.</p>
<p>https://www.youtube.com/watch?v=jw8i2lnlYkM</p>
Screen Names2021-05-23T00:00:00Zhttps://www.troyv.dev/2021/05/23/screen-names/
<!-- @format -->
<p>My first introduction to the internet was being given access to AOL's kid-friendly version, KOL, and it was then that I had to decide on my first screen name. I admit to not having the best recollection of how things went from there, but I feel like it was perfectly fine to have many screen names across platforms instead of being locked to <em>one</em>. When I created my first <em>real</em> email address, I followed suit with almost everyone else and chose a name closely resembling my own. That makes sense, and it felt like the right thing to do, but it was from that point forward that my use of custom screen names diminished. I had a screen name for video games and Twitter I guess, but for ease and simplicity I resorted to the <em>adult</em> choice of my personal name for any accounts asking.</p>
<p>We live in this age where we are so often <em>on and connected</em>. We all know that a simple search can tie us to nearly every online account because we use our real names as the identifier. Data breaches are complicated not only by their nature, but because those same emails, usernames, ad potentially passwords (if you aren't using a password manager...) can be traced to a plethora of other accounts elsewhere.</p>
<p>All of that is accepted now. What I'm actually interested in is a writeup on the history of screen names; something like <em>The Rise and Fall of Screen Names</em> if you will. I am not the person to write this story. While I've grown up in the middle of it all, I was not at the age to be fully aware of it. I didn't create a Facebook until the middle of high school, and it was probably after that when I got a Twitter.</p>
<blockquote>
<p>I loved the creativity and comedy of Twitter handles (I'm looking at @fart, @nice_mustard, or @rad_milk), and it's my remembering of them that contributed to this post.</p>
</blockquote>
<p>There are people out there who probably already have this stuff documented. A quick search of my own found no immediate results for this story (correct me if I'm wrong!), so I'm putting it out there to the world that I want it. I want to know the beginnings of screen names and how we transitioned to a time when the act of using one instead of your real name can be seen as a political statement; that last part I made up, but the rest of it stands. Where are screen names still acceptable, or even expected, and where have they fallen to the wayside?</p>
<p>I hope someone who knows way more about this than I do can write a lengthy article and send it my way. If this already exists, then I'm an idiot, and I hope someone corrects me.</p>
Front Royal Covers cardigan by Taylor Swift2021-05-21T00:00:00Zhttps://www.troyv.dev/2021/05/21/front-royal-covers-cardigan-by-taylor-swift/
<!-- @format -->
<p>It's safe to say I'm the biggest swiftie in Front Royal, so when we decided to write and release a covers album, it was no surprise I voted for a Taylor Swift tune. <em>Cardigan</em> is one of five songs being released in this covers series, so keep an eye out for the next ones.</p>
<iframe title="Listen to cardigan by front royal" style="border: 0; width: 100%; height: 120px;" src="https://bandcamp.com/EmbeddedPlayer/track=1330083917/size=large/bgcol=333333/linkcol=e99708/tracklist=false/artwork=small/transparent=true/" seamless=""><a href="https://frontroyalmd.bandcamp.com/track/cardigan">cardigan by Front Royal</a></iframe>
<p>Check out Front Royal at all these locations:</p>
<ul>
<li><a href="https://frontroyalmd.bandcamp.com/">Bandcamp</a></li>
<li><a href="https://facebook.com/frontroyalmd">Facebook</a></li>
<li><a href="https://open.spotify.com/artist/1NfwIBuuWEk4d8c6LZftnD?si=7CjcwpNZTjipSVaXrLypLg">Spotify</a></li>
<li><a href="https://instagram.com/frontroyal_official">Instagram</a></li>
<li><a href="https://twitter.com/frontroyalband">Twitter</a></li>
<li><a href="https://www.youtube.com/channel/UCm-KryhT3o9NZSbpG-M1qCQ/feed">YouTube</a></li>
</ul>
I Made an Express App About my Cats2021-04-25T00:00:00Zhttps://www.troyv.dev/2021/04/25/i-made-an-express-app-about-my-cats/
<!-- @format -->
<p>I recently got antsy to make an Express app. The original idea was to try and recreate <em>this very website</em> using Express and Pug templates instead of Nunjucks. It started off fine, but then I realized it'd be a little more of a waste of time to base it on my website since 1) the site is already made with Node, and 2) it won't necessarily teach me something that new.</p>
<p>So instead, I chose to create a mini site about my cats and add on the extra challenge of using Tailwind CSS for the first time. You can find the <a href="https://github.com/troyvassalotti/express-cats">code for this app</a> on my GitHub.</p>
<h2 id="the-setup" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2021/04/25/i-made-an-express-app-about-my-cats/#the-setup">The Setup</a></h2>
<p>Getting the structure started wasn't too much of a problem. I had a repo to use as a starter from a Node & Express tutorial I've followed. Pug wasn't an issue either since I use it on other 11ty sites and it came hand-in-hand with <em>other</em> Express tutorials.</p>
<blockquote>
<p>Speaking of boilerplates, this project led to me creating a starter repo for cases like this. Check out my <a href="https://github.com/troyvassalotti/node-express-pug-starter">Node, Express, Pug, Tailwind starter</a> if you'd like.</p>
</blockquote>
<p>The most difficult hurdle to jump in new projects, for me at least, is how to organize the files. I like having everything in their proper places as early as possible so I don't have to move them around later; this can be seen in both my digital and physical lives alike. Luckily, this is a small project.</p>
<ul>
<li>Pug templates go in <code>/views</code> with partials and layouts broken out in their respective directories.</li>
<li>Tailwind CSS goes in <code>/public</code>.</li>
<li>All the routing goes in <code>/routes</code>.</li>
<li>All the config files stay in the root.</li>
</ul>
<p>Look at this <code>app.js</code> - so minimal.</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> express <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">"express"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> app <span class="token operator">=</span> <span class="token function">express</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> routes <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">"./routes/site"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> compression <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">"compression"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// set pug engine</span>
app<span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span><span class="token string">"view engine"</span><span class="token punctuation">,</span> <span class="token string">"pug"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// set compression</span>
app<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span><span class="token function">compression</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// set static assets</span>
app<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span>express<span class="token punctuation">.</span><span class="token function">static</span><span class="token punctuation">(</span><span class="token string">"./public"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
app<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span><span class="token string">"/"</span><span class="token punctuation">,</span> routes<span class="token punctuation">)</span><span class="token punctuation">;</span>
app<span class="token punctuation">.</span><span class="token function">listen</span><span class="token punctuation">(</span>process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">PORT</span> <span class="token operator">||</span> <span class="token number">3000</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"Listening on port "</span> <span class="token operator">+</span> process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">PORT</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>I've made attempts at cleaning out my project roots and keeping only config files there (not the default for 11ty sites) because it was making me feel unkempt having HTML or otherwise next to my <code>package.json</code>.</p>
<p>Since I've never used Tailwind or PostCSS, I had to do a quick dive into the docs to figure out how to get it started, and how to use Tailwind in general. A couple <code>npm install</code>s and <code>.json</code> files later and things were working fine... except for the <strong>massive</strong> CSS files being generated because I wasn't purging unused styles.</p>
<p>Oops!</p>
<h2 id="routing-and-page-generation" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2021/04/25/i-made-an-express-app-about-my-cats/#routing-and-page-generation">Routing and Page Generation</a></h2>
<p>The website has three main types of pages you could look at: the homepage, the about page, and the cat page (one for each cat). Typing that out made me realize I didn't create a 404 page.</p>
<p>Oops again!</p>
<p>Everything was able to be passed into the general layout file though. Template inheritance meant I could separate data-handling partials away from the main content and everything plays well together.</p>
<pre class="language-pug"><code class="language-pug"><span class="token comment">// layout.pug</span>
<span class="token doctype">doctype html</span>
<span class="token tag">html<span class="token attr-class">.text-gray-900</span><span class="token attr-class">.text-base</span><span class="token attr-class">.leading-tight</span><span class="token attributes"><span class="token punctuation">(</span><span class="token attr-name">lang</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"en"</span></span><span class="token punctuation">)</span></span></span>
<span class="token tag">head</span>
<span class="token tag">meta<span class="token attributes"><span class="token punctuation">(</span><span class="token attr-name">charset</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"UTF-8"</span></span><span class="token punctuation">)</span></span></span>
<span class="token tag">meta<span class="token attributes"><span class="token punctuation">(</span><span class="token attr-name">http-equiv</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"X-UA-Compatible"</span></span><span class="token punctuation">,</span> <span class="token attr-name">content</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"IE=edge"</span></span><span class="token punctuation">)</span></span></span>
<span class="token tag">meta<span class="token attributes"><span class="token punctuation">(</span><span class="token attr-name">name</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"viewport"</span></span><span class="token punctuation">,</span> <span class="token attr-name">content</span><span class="token punctuation">=</span><span class="token attr-value">"width<span class="token operator">=</span>device<span class="token operator">-</span>width</span><span class="token punctuation">,</span> <span class="token attr-name">initial-scale</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token number">1.0</span>"</span><span class="token punctuation">)</span></span></span>
<span class="token tag">title</span> <span class="token plain-text">Cats | #{title}</span>
<span class="token keyword">block styles</span>
<span class="token tag">link<span class="token attributes"><span class="token punctuation">(</span><span class="token attr-name">rel</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"stylesheet"</span></span><span class="token punctuation">,</span> <span class="token attr-name">href</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"/css/main.css"</span></span><span class="token punctuation">)</span></span></span>
<span class="token tag">body<span class="token attr-class">.min-h-screen</span><span class="token attr-class">.flex</span><span class="token attr-class">.flex-col</span><span class="token attr-class">.justify-between</span></span>
<span class="token tag">main</span>
<span class="token keyword">block content</span>
<span class="token tag">p</span> <span class="token plain-text">default content goes here</span>
<span class="token keyword">block footer</span>
<span class="token tag">footer<span class="token attr-class">.text-center</span><span class="token attr-class">.p-8</span></span>
<span class="token tag">h2</span> <span class="token plain-text">Thanks for having an interest in our cats!</span>
<span class="token tag">p</span> <span class="token plain-text">If you want to see more details about this site, #[a(href="/about", class="underline text-blue-700 hover:text-blue-400") visit the about page].</span></code></pre>
<p>Probably my favorite part about the site is figuring out how to pass the cat data to each route that needed it and visualize it on the page. What do I mean by that? I mean I passed the cat object into the homepage to display links to both cats dynamically, but also passed it into the <code>cat.pug</code> template to use a single file as both cat pages <em>while still</em> keeping their routes separate.</p>
<h3 id="the-router" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2021/04/25/i-made-an-express-app-about-my-cats/#the-router">The Router</a></h3>
<p>See, check out the router here:</p>
<pre class="language-pug"><code class="language-pug"><span class="token comment">// site.js</span>
<span class="token tag">const</span> <span class="token plain-text">express = require('express');</span>
<span class="token tag">const</span> <span class="token plain-text">router = express.Router();</span>
<span class="token tag">const</span> <span class="token plain-text">cats = require('../data/cats');</span>
<span class="token tag">router<span class="token attr-class">.get</span><span class="token attributes"><span class="token punctuation">(</span>'/'<span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token attr-name">req</span><span class="token punctuation">,</span> <span class="token attr-name">res</span><span class="token punctuation">)</span></span></span> <span class="token plain-text">=> {</span>
<span class="token tag">res<span class="token attr-class">.render</span><span class="token attributes"><span class="token punctuation">(</span>'index'<span class="token punctuation">,</span> {
<span class="token attr-name">cats</span><span class="token punctuation">,</span>
title: 'All About Cats'
}<span class="token punctuation">)</span></span></span>
})
<span class="token tag">router<span class="token attr-class">.get</span><span class="token attributes"><span class="token punctuation">(</span>'/about'<span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token attr-name">req</span><span class="token punctuation">,</span> <span class="token attr-name">res</span><span class="token punctuation">)</span></span></span> <span class="token plain-text">=> {</span>
<span class="token tag">res<span class="token attr-class">.render</span><span class="token attributes"><span class="token punctuation">(</span>'about'<span class="token punctuation">,</span> {
title: 'About This Site'
}<span class="token punctuation">)</span></span></span>
})
<span class="token tag">router<span class="token attr-class">.get</span><span class="token attributes"><span class="token punctuation">(</span>'/cats/:page'<span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token attr-name">req</span><span class="token punctuation">,</span> <span class="token attr-name">res</span><span class="token punctuation">)</span></span></span> <span class="token plain-text">=> {</span>
<span class="token tag">const</span> <span class="token plain-text">{page} = req.params;</span>
<span class="token tag">const</span> <span class="token plain-text">cat = cats.filter(item=>Object.values(item).includes(page))[0];</span>
<span class="token tag">const</span> <span class="token plain-text">remi = cats.filter(item=>Object.values(item).includes("Remi"))[0];</span>
<span class="token tag">const</span> <span class="token plain-text">sophie = cats.filter(item=>Object.values(item).includes("Sophie"))[0];</span>
<span class="token tag">res<span class="token attr-class">.render</span><span class="token attributes"><span class="token punctuation">(</span>'cat'<span class="token punctuation">,</span> {
<span class="token attr-name">cat</span><span class="token punctuation">,</span>
<span class="token attr-name">remi</span><span class="token punctuation">,</span>
<span class="token attr-name">sophie</span><span class="token punctuation">,</span>
title: cat.title
}<span class="token punctuation">)</span></span></span>
})
<span class="token tag">module<span class="token attr-class">.exports</span></span> <span class="token plain-text">= router;</span></code></pre>
<p>What's going on here is I have a <code>cats.js</code> file that holds the object for cat information. That object is imported to the router as <code>const cats</code>, and then it is passed into the response call of a route. In the case of the route <code>/cats/:page</code>, I'm manipulating it a bit. <strong>Note</strong>: I am aware this is a very brute force way of dealing with the data, but since I only have two cats it was fine.</p>
<p>I store the cat being requested from the request parameters. You couldn't request any cat though - I determined the cats being requested by linking to them on the homepage. Then I filter the <code>cats</code> array by finding the object that uses the requested cat in a key:value pair. That's not all though since then I need to store the <em>opposite</em> cat, so I create two more variables for each individual cat. All three of these variables are passed to the page to dynamically generate the cat post <em>as well as</em> the link to the other cat.</p>
<p>See how I use all this on the cat template:</p>
<pre class="language-pug"><code class="language-pug"><span class="token keyword">extends ./layouts/layout.pug</span>
<span class="token keyword">block content</span>
<span class="token tag"><span class="token attr-class">.p-6</span><span class="token attr-class">.max-w-lg</span><span class="token attr-class">.mx-auto</span><span class="token attr-class">.bg-white</span><span class="token attr-class">.rounded-xl</span><span class="token attr-class">.shadow-md</span><span class="token attr-class">.flex</span><span class="token attr-class">.items-center</span><span class="token attr-class">.space-x-4</span></span>
<span class="token tag"><span class="token attr-class">.flex-shrink-0</span></span>
<span class="token tag">img<span class="token attr-class">.w-32</span><span class="token attr-class">.h-32</span><span class="token attributes"><span class="token punctuation">(</span><span class="token attr-name">src</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"/img/"</span> <span class="token operator">+</span> cat<span class="token punctuation">.</span>url <span class="token operator">+</span> <span class="token string">"-hero.jpg"</span></span><span class="token punctuation">,</span> <span class="token attr-name">alt</span><span class="token punctuation">=</span><span class="token attr-value">cat<span class="token punctuation">.</span>title</span><span class="token punctuation">)</span></span></span>
<span class="token tag">ul<span class="token attr-class">.text-gray-500</span></span>
<span class="token tag">li<span class="token attr-class">.text-xl</span><span class="token attr-class">.font-medium</span></span> <span class="token plain-text">#[b.text-black Name:] #{cat.title}</span>
<span class="token tag">li</span> <span class="token plain-text">#[b.text-black Gotcha Day:] #{cat.date}</span>
<span class="token tag">li</span> <span class="token plain-text">#[b.text-black Weight:] #{cat.weight}</span>
<span class="token tag">li</span> <span class="token plain-text">#[b.text-black Personality:] #{cat.personality}</span>
<span class="token tag"><span class="token attr-class">.container</span><span class="token attr-class">.mx-auto</span><span class="token attr-class">.p-8</span></span>
<span class="token tag">article<span class="token attributes"><span class="token punctuation">(</span><span class="token attr-name">class</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"grid grid-cols-1 gap-8 justify-items-center items-center md:grid-cols-2"</span></span><span class="token punctuation">)</span></span></span>
<span class="token flow-control"><span class="token branch keyword">if</span> cat<span class="token punctuation">.</span>url <span class="token operator">==</span> <span class="token string">"sophie"</span></span>
<span class="token keyword">include:markdown-it ./partials/sophie.md</span>
<span class="token flow-control"><span class="token branch keyword">else</span></span>
<span class="token keyword">include:markdown-it ./partials/remi.md</span>
<span class="token tag"><span class="token attr-class">.text-center</span><span class="token attr-class">.mt-8</span><span class="token attr-class">.text-2xl</span><span class="token attr-class">.grid</span><span class="token attr-class">.gap-4</span></span>
<span class="token flow-control"><span class="token branch keyword">if</span> cat<span class="token punctuation">.</span>url <span class="token operator">==</span> <span class="token string">"sophie"</span></span>
<span class="token tag">p<span class="token punctuation">:</span></span> <span class="token tag">a<span class="token attributes"><span class="token punctuation">(</span><span class="token attr-name">href</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"/cats/"</span> <span class="token operator">+</span> remi<span class="token punctuation">.</span>url</span><span class="token punctuation">,</span> <span class="token attr-name">class</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"px-4 py-1 text-sm text-purple-600 font-semibold rounded-full border border-purple-200 hover:text-white hover:bg-purple-600 hover:border-transparent focus:outline-none focus:ring-2 focus:ring-purple-600 focus:ring-offset-2"</span></span><span class="token punctuation">)</span></span></span> <span class="token tag">Read</span> <span class="token tag">About</span> <span class="token tag">Remi</span><span class="token punctuation">.</span>
<span class="token flow-control"><span class="token branch keyword">else</span></span>
<span class="token tag">p<span class="token punctuation">:</span></span> <span class="token tag">a<span class="token attributes"><span class="token punctuation">(</span><span class="token attr-name">href</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"/cats/"</span> <span class="token operator">+</span> sophie<span class="token punctuation">.</span>url</span><span class="token punctuation">,</span> <span class="token attr-name">class</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"px-4 py-1 text-sm text-purple-600 font-semibold rounded-full border border-purple-200 hover:text-white hover:bg-purple-600 hover:border-transparent focus:outline-none focus:ring-2 focus:ring-purple-600 focus:ring-offset-2"</span></span><span class="token punctuation">)</span></span></span> <span class="token tag">Read</span> <span class="token tag">About</span> <span class="token tag">Sophie</span><span class="token punctuation">.</span>
<span class="token tag">p<span class="token punctuation">:</span></span> <span class="token tag">a<span class="token attributes"><span class="token punctuation">(</span><span class="token attr-name">href</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"/"</span></span><span class="token punctuation">,</span> <span class="token attr-name">class</span><span class="token punctuation">=</span><span class="token attr-value"><span class="token string">"px-4 py-1 text-sm text-purple-600 font-semibold rounded-full border border-purple-200 hover:text-white hover:bg-purple-600 hover:border-transparent focus:outline-none focus:ring-2 focus:ring-purple-600 focus:ring-offset-2"</span></span><span class="token punctuation">)</span></span></span> <span class="token tag">Return</span> <span class="token tag">Home</span><span class="token punctuation">.</span></code></pre>
<p>If you visit Sophie's page, you'll see a button/link to Remi's page, and vice versa. Aside from all that, the only piece of the puzzle remaining is styling the thing, which leads us to Tailwind.</p>
<h2 id="thoughts-on-tailwind" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2021/04/25/i-made-an-express-app-about-my-cats/#thoughts-on-tailwind">Thoughts on Tailwind</a></h2>
<p>I should use it more. Not that it was a life-changing tool to use here. In fact, it was a little annoying feeling like I removed control of creating custom CSS and having to rely on a seemingly-endless series of predetermined classes. One downside of this is applying the same classes to adjacent HTML elements since I couldn't create an all-encompassing rule to target all <code>h2</code> elements or something.</p>
<p>That being said, I only used it this one time in a very surface-level way. People love this thing and I have no doubts I could fall in love with it too if I read more of the docs and tried it out on a larger scale. But without all that prior practice, I definitely could've made this thing quicker if I used my own CSS files.</p>
<p>There are aspects of Tailwind I grew to enjoy. For one, I appreciate the notion that headings should be detached from their initial font sizes because it isn't always the case that the <code>h1</code> should be bigger than the <code>h2</code>. Years of it being that way by default makes it feel weird for them to not follow that order, but years of seeing people use a heading element because it has the size or styling they want - as opposed to it being the right element semantically - says that isn't how everyone sees it.</p>
<p>But I didn't love having a blank slate either. Since everything is reset with no styles, I felt more pressure to make sure all lists, links, headings, body text, etc were accounted for. I didn't design the app ahead of time though, which is why it is very basic looking. I would not let this go as a production-ready application, but it is totally fine as a simple, fun, nonsense thing.</p>
<h2 id="hosting" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2021/04/25/i-made-an-express-app-about-my-cats/#hosting">Hosting</a></h2>
<p>You can find the app <a href="https://cats.onrender.com/">at this link</a>. I chose Heroku to host it because I had recently used it for - you guessed it - a tutorial. I had way more of an issue getting the app to work than I should have. For some reason, the first bunch of pushes were giving me good deploy logs with a broken URL. I still don't know how I fixed the issue, but I uninstalled and reinstalled dependencies, and changed some language even if the language <em>worked locally</em>, until the app finally deployed correctly. Again, nothing in the logs were telling me something was wrong so...</p>
<p>I have no intentions of editing this thing. It is alive and in the world, and I am banished from making any improvements because it doesn't matter - it's a joke site. The only thing that might change is where I host it, but Heroku is fine for now.</p>
<blockquote>
<p>Now hosted on Render!</p>
</blockquote>
Cool Template Features With Eleventy2021-04-14T00:00:00Zhttps://www.troyv.dev/2021/04/14/cool-template-features-with-eleventy/
<!-- @format -->
<p>You know what's great about having a personal website? The fact that I can configure it to be as simple or complicated as I please. Sure, it isn't <em>ideal</em> to make things complicated, but would I really consider myself a developer if I wasn't spending hours upon hours trying to automate something that's otherwise a slight inconvenience?</p>
<p><strong>No.</strong></p>
<p>This site has been built using <a href="https://www.11ty.dev/">Eleventy</a> ever since I decided I wanted to learn what the heck was so great about static site generators. Eleventy sounded like the best option for someone who didn't want to learn something entirely new (like a JavaScript framework) while learning something else entirely new at the same time (static site generators).</p>
<p>I know I made the right choice here. After all the struggles I had getting a <a href="https://nuxtjs.org/">Nuxt</a> site to work, even as someone who knew how to use Vue at the time, would have discouraged me to some extent.</p>
<blockquote>
<p>Not that it was Nuxt's fault at all. I was trying to accomplish something too quickly without enough planning or reading the docs because I thought "how hard could this be?"</p>
</blockquote>
<h2 id="an-evolution-in-design" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2021/04/14/cool-template-features-with-eleventy/#an-evolution-in-design">An Evolution in Design</a></h2>
<p>A lot of changes have been made around here since this site's existence. I was still getting the hang of <em>personal branding</em> (rather, I didn't give enough thought into <em>personality</em>). The site was simple and got the job done, but there wasn't anything special about it, and it didn't reflect <em>who I am</em>.</p>
<p>I made a giant redesign a few months ago - the site you're looking at right now but which you might not be looking at in the future if you read this at a later point where I've redesigned the redesign - and jumped head first into making it a templating machine, harnessing the power of Eleventy and <a href="https://mozilla.github.io/nunjucks/">Nunjucks</a> to create a monster.</p>
<blockquote>
<p>Talk about a kick-ass way to template my pages; Nunjucks <a href="https://youtu.be/UwIF95svGp0">saves the day</a> in so many aspects.</p>
</blockquote>
<h2 id="new-and-improved" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2021/04/14/cool-template-features-with-eleventy/#new-and-improved">New and Improved</a></h2>
<p>There's a lot to cover here, so I'm going to break out each cool thing into its own section here.</p>
<h3 id="plugins" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2021/04/14/cool-template-features-with-eleventy/#plugins">Plugins</a></h3>
<p>I've alluded before to my hesitancy with dependency-hell and wanting to keep my sites in a place that I can jump back into without feeling overwhelmed. So, this site previously had very little dependencies being used. I still feel that sentiment, but I am more comfortable in my implementations and decided a few plugins will be greatly beneficial here.</p>
<h4 id="npm-install-%4011ty%2Feleventy-image" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2021/04/14/cool-template-features-with-eleventy/#npm-install-%4011ty%2Feleventy-image">npm install @11ty/eleventy-image</a></h4>
<p>I used to head over to <a href="https://squoosh,app/">Squoosh</a> for manually compressing my images in their various formats; I wanted to stop doing this. It wasn't a time-consuming process necessarily, but I wanted it automated. Luckily, there's an official plugin for this sort of thing. Enter, <a href="https://www.11ty.dev/docs/plugins/image/"><code>eleventy-image</code></a>. This plugin takes a file and produces it back in the desired file formats and sizes that you ask for - all at build time.</p>
<p>For my purposes, I needed two shortcodes created to be able to fully use the plugin. One for synchronous transforms (like iterating over items in a paired shortcode) and one for asynchronous transforms (like creating images standalone that don't require knowledge of the containing shortcode).</p>
<pre class="language-js"><code class="language-js"><span class="token comment">// ImageShortcode.js is asynchronous</span>
<span class="token keyword">const</span> Image <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">"@11ty/eleventy-img"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
module<span class="token punctuation">.</span><span class="token function-variable function">exports</span> <span class="token operator">=</span> <span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token punctuation">(</span>
src<span class="token punctuation">,</span>
alt<span class="token punctuation">,</span>
widthArray<span class="token punctuation">,</span>
formatArray<span class="token punctuation">,</span>
sizes<span class="token punctuation">,</span>
className <span class="token operator">=</span> <span class="token string">""</span><span class="token punctuation">,</span>
id <span class="token operator">=</span> <span class="token string">""</span><span class="token punctuation">,</span>
<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">let</span> metadata <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">Image</span><span class="token punctuation">(</span>src<span class="token punctuation">,</span> <span class="token punctuation">{</span>
<span class="token literal-property property">widths</span><span class="token operator">:</span> widthArray<span class="token punctuation">,</span>
<span class="token literal-property property">formats</span><span class="token operator">:</span> formatArray<span class="token punctuation">,</span>
<span class="token literal-property property">urlPath</span><span class="token operator">:</span> <span class="token string">"/img/"</span><span class="token punctuation">,</span>
<span class="token literal-property property">outputDir</span><span class="token operator">:</span> <span class="token string">"./_site/img/"</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> imageAttributes<span class="token punctuation">;</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>id <span class="token operator">===</span> <span class="token string">""</span> <span class="token operator">&&</span> className <span class="token operator">===</span> <span class="token string">""</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
imageAttributes <span class="token operator">=</span> <span class="token punctuation">{</span>
alt<span class="token punctuation">,</span>
sizes<span class="token punctuation">,</span>
<span class="token literal-property property">loading</span><span class="token operator">:</span> <span class="token string">"lazy"</span><span class="token punctuation">,</span>
<span class="token literal-property property">decoding</span><span class="token operator">:</span> <span class="token string">"async"</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>otherlogic<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token comment">/* a load of omitted code */</span>
<span class="token punctuation">}</span>
<span class="token keyword">return</span> Image<span class="token punctuation">.</span><span class="token function">generateHTML</span><span class="token punctuation">(</span>metadata<span class="token punctuation">,</span> imageAttributes<span class="token punctuation">,</span> <span class="token punctuation">{</span>
<span class="token literal-property property">whitespaceMode</span><span class="token operator">:</span> <span class="token string">"inline"</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<p>I encountered a few hiccups when figuring out how to tell it where my image was. I changed my project structure so that all the source files live in <code>src</code> and all the config files (<code>package.json</code>, <code>.eleventy.js</code>, etc. ) live in the root, so that was a tiny wrench in my system. This was the solution I came to:</p>
<pre class="language-twig"><code class="language-twig">
<span class="token twig language-twig"><span class="token delimiter punctuation">{%</span> <span class="token tag-name keyword">image</span> <span class="token string"><span class="token punctuation">'</span>./public/img/me-on-set.jpg<span class="token punctuation">'</span></span><span class="token punctuation">,</span> <span class="token string"><span class="token punctuation">'</span>This is me. I look like this.<span class="token punctuation">'</span></span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token number">300</span><span class="token punctuation">,</span> <span class="token number">600</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token string"><span class="token punctuation">'</span>webp<span class="token punctuation">'</span></span><span class="token punctuation">,</span> <span class="token string"><span class="token punctuation">'</span>avif<span class="token punctuation">'</span></span><span class="token punctuation">,</span> <span class="token string"><span class="token punctuation">'</span>jpg<span class="token punctuation">'</span></span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token string"><span class="token punctuation">'</span>(max-width: 700px) 100vw, 50vw<span class="token punctuation">'</span></span><span class="token punctuation">,</span> <span class="token string"><span class="token punctuation">'</span>full<span class="token punctuation">'</span></span><span class="token punctuation">,</span> <span class="token string"><span class="token punctuation">'</span>me<span class="token punctuation">'</span></span> <span class="token delimiter punctuation">%}</span></span>
</code></pre>
<p>The outcome of this all is a single image resized, compressed, and in .webp, .avif, and .jpg formats. You can do some other cool things with variables using Nunjucks, which I do for some other pages, but this is the simplest usage I have.</p>
<h4 id="npm-install-%4011ty%2Feleventy-plugin-syntaxhighlight" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2021/04/14/cool-template-features-with-eleventy/#npm-install-%4011ty%2Feleventy-plugin-syntaxhighlight">npm install @11ty/eleventy-plugin-syntaxhighlight</a></h4>
<p>Who doesn't love some syntax highlighting? The beauty of this plugin is that all I need to do is provide my own <a href="https://prismjs.com/">Prism</a> styles. The code being highlighted gets all the HTML applied at build time without the need to use client-side JS.</p>
<h4 id="npm-install-clean-css" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2021/04/14/cool-template-features-with-eleventy/#npm-install-clean-css">npm install clean-css</a></h4>
<p>CSS <em>is</em> awesome, but it's better if it's minified for production and critical styles are inlined. I'm pretty sure that was a quote somewhere or something, <em>don't @ me though</em>. By installing clean-css, I am able to create a filter through which to pass my stylesheets or CSS snippets that makes them incredibly mini.</p>
<p>To do all that though, I needed to alter my Nunjucks template. The below code snippet looks for <code>addCSS</code> in the front matter of any template pages and finds the page's name in the directory I keep all my CSS includes. If there's syntax highlighting, I have a front matter of <code>codeblock</code> and add Prism styles. I still need to update Plvylist to the web component version on this site, but it currently looks for that too. It takes all that, runs it through the filter, and places it in a <code><style></code> block.</p>
<pre class="language-twig"><code class="language-twig">
<span class="token twig language-twig"><span class="token delimiter punctuation">{%</span> <span class="token tag-name keyword">if</span> addCSS <span class="token operator">or</span> codeblock <span class="token operator">or</span> plvylist <span class="token delimiter punctuation">%}</span></span>
<span class="token twig language-twig"><span class="token delimiter punctuation">{%-</span> <span class="token tag-name keyword">set</span> css <span class="token delimiter punctuation">-%}</span></span>
<span class="token twig language-twig"><span class="token delimiter punctuation">{%-</span> <span class="token tag-name keyword">if</span> addCSS <span class="token delimiter punctuation">-%}</span></span>
<span class="token twig language-twig"><span class="token delimiter punctuation">{%-</span> <span class="token tag-name keyword">include</span> <span class="token string"><span class="token punctuation">"</span>css/<span class="token punctuation">"</span></span> <span class="token operator">+</span> page<span class="token punctuation">.</span>fileSlug <span class="token operator">+</span> <span class="token string"><span class="token punctuation">"</span>.css<span class="token punctuation">"</span></span> <span class="token delimiter punctuation">-%}</span></span>
<span class="token twig language-twig"><span class="token delimiter punctuation">{%-</span> <span class="token tag-name keyword">endif</span> <span class="token delimiter punctuation">-%}</span></span>
<span class="token twig language-twig"><span class="token delimiter punctuation">{%-</span> <span class="token tag-name keyword">if</span> codeblock <span class="token delimiter punctuation">-%}</span></span>
<span class="token twig language-twig"><span class="token delimiter punctuation">{%-</span> <span class="token tag-name keyword">include</span> <span class="token string"><span class="token punctuation">"</span>css/prism.css<span class="token punctuation">"</span></span> <span class="token delimiter punctuation">-%}</span></span>
<span class="token twig language-twig"><span class="token delimiter punctuation">{%-</span> <span class="token tag-name keyword">endif</span> <span class="token delimiter punctuation">-%}</span></span>
<span class="token twig language-twig"><span class="token delimiter punctuation">{%-</span> <span class="token tag-name keyword">if</span> plvylist <span class="token delimiter punctuation">-%}</span></span>
<span class="token twig language-twig"><span class="token delimiter punctuation">{%-</span> <span class="token tag-name keyword">include</span> <span class="token string"><span class="token punctuation">"</span>css/plvylist.css<span class="token punctuation">"</span></span> <span class="token delimiter punctuation">-%}</span></span>
<span class="token twig language-twig"><span class="token delimiter punctuation">{%-</span> <span class="token tag-name keyword">endif</span> <span class="token delimiter punctuation">-%}</span></span>
<span class="token twig language-twig"><span class="token delimiter punctuation">{%-</span> <span class="token tag-name keyword">endset</span> <span class="token delimiter punctuation">-%}</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>style</span><span class="token punctuation">></span></span><span class="token style"><span class="token language-css">
<span class="token twig language-twig"><span class="token delimiter punctuation">{{</span> css <span class="token operator">|</span> cssmin <span class="token operator">|</span> safe <span class="token delimiter punctuation">}}</span></span>
</span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>style</span><span class="token punctuation">></span></span>
<span class="token twig language-twig"><span class="token delimiter punctuation">{%</span> <span class="token tag-name keyword">endif</span> <span class="token delimiter punctuation">%}</span></span>
</code></pre>
<p>Pretty cool, yeah? Well, let's get even cooler.</p>
<h4 id="npm-install-terser" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2021/04/14/cool-template-features-with-eleventy/#npm-install-terser">npm install terser</a></h4>
<p>I wanted to inline and minify my JavaScript the same way I did my CSS. The process is the same except that I installed terser to do so. When it all comes together, the Eleventy config file looks something like this:</p>
<pre class="language-js"><code class="language-js"><span class="token comment">// .eleventy.js</span>
<span class="token keyword">const</span> syntaxHighlight <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">"@11ty/eleventy-plugin-syntaxhighlight"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> CleanCSS <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">"clean-css"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> <span class="token punctuation">{</span>minify<span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">"terser"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
module<span class="token punctuation">.</span><span class="token function-variable function">exports</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">eleventyConfig</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token comment">// add the syntax highlighting plugin from earlier</span>
eleventyConfig<span class="token punctuation">.</span><span class="token function">addPlugin</span><span class="token punctuation">(</span>syntaxHighlight<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// add a css minifier filter from clean-css</span>
eleventyConfig<span class="token punctuation">.</span><span class="token function">addFilter</span><span class="token punctuation">(</span><span class="token string">"cssmin"</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">code</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">CleanCSS</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">minify</span><span class="token punctuation">(</span>code<span class="token punctuation">)</span><span class="token punctuation">.</span>styles<span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// add javascript minifier</span>
eleventyConfig<span class="token punctuation">.</span><span class="token function">addNunjucksAsyncFilter</span><span class="token punctuation">(</span>
<span class="token string">"jsmin"</span><span class="token punctuation">,</span>
<span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">code<span class="token punctuation">,</span> callback</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">try</span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> minified <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">minify</span><span class="token punctuation">(</span>code<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">callback</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">,</span> minified<span class="token punctuation">.</span>code<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token punctuation">{</span>
console<span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span><span class="token string">"Terser error: "</span><span class="token punctuation">,</span> err<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// Fail gracefully.</span>
<span class="token function">callback</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">,</span> code<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<p><strong>Neat.</strong></p>
<h3 id="nunjucks-features" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2021/04/14/cool-template-features-with-eleventy/#nunjucks-features">Nunjucks Features</a></h3>
<p>I've covered most of the cool Nunjucks stuff in the previous sections, but there's still more. For example, I'm really taking advantage of the built in <code>set</code> and <code>include</code> methods, breaking out code into more modular pieces. My primary layout file is a good example:</p>
<pre class="language-twig"><code class="language-twig">
<span class="token doctype"><span class="token punctuation"><!</span><span class="token doctype-tag">DOCTYPE</span> <span class="token name">html</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>html</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>en<span class="token punctuation">"</span></span> <span class="token attr-name">dir</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>ltr<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>head</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">charset</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>utf-8<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>viewport<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>width=device-width, initial-scale=1<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token twig language-twig"><span class="token delimiter punctuation">{%-</span> <span class="token tag-name keyword">if</span> noindex <span class="token delimiter punctuation">-%}</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>robots<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>noindex<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token twig language-twig"><span class="token delimiter punctuation">{%-</span> <span class="token tag-name keyword">endif</span> <span class="token delimiter punctuation">-%}</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>preconnect<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://d33wubrfki0l68.cloudfront.net<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>dns-prefetch<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://d33wubrfki0l68.cloudfront.net<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>canonical<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://www.troyv.dev<span class="token twig language-twig"><span class="token delimiter punctuation">{{</span> page<span class="token punctuation">.</span>url <span class="token delimiter punctuation">}}</span></span><span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>stylesheet<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://fonts.typotheque.com/WF-036345-011398.css<span class="token punctuation">"</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>text/css<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>stylesheet<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/css/main.css<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token twig language-twig"><span class="token delimiter punctuation">{%</span> <span class="token tag-name keyword">if</span> page<span class="token punctuation">.</span>fileSlug <span class="token operator">==</span><span class="token operator">=</span> <span class="token string"><span class="token punctuation">"</span><span class="token punctuation">"</span></span> <span class="token delimiter punctuation">-%}</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>stylesheet<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/css/homepage.css<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token twig language-twig"><span class="token delimiter punctuation">{%-</span> <span class="token tag-name keyword">endif</span> <span class="token delimiter punctuation">%}</span></span>
<span class="token twig language-twig"><span class="token delimiter punctuation">{%</span> <span class="token tag-name keyword">include</span> <span class="token string"><span class="token punctuation">"</span>process-css.njk<span class="token punctuation">"</span></span> <span class="token delimiter punctuation">%}</span></span>
<span class="token twig language-twig"><span class="token delimiter punctuation">{%</span> <span class="token tag-name keyword">include</span> <span class="token string"><span class="token punctuation">"</span>seo.njk<span class="token punctuation">"</span></span> <span class="token delimiter punctuation">%}</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>head</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>body</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token twig language-twig"><span class="token delimiter punctuation">{{</span> pageName <span class="token delimiter punctuation">}}</span></span><span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token twig language-twig"><span class="token delimiter punctuation">{%</span> <span class="token tag-name keyword">include</span> <span class="token string"><span class="token punctuation">"</span>header.html<span class="token punctuation">"</span></span> <span class="token delimiter punctuation">%}</span></span>
<span class="token twig language-twig"><span class="token delimiter punctuation">{{</span> content <span class="token operator">|</span> safe <span class="token delimiter punctuation">}}</span></span>
<span class="token twig language-twig"><span class="token delimiter punctuation">{%</span> <span class="token tag-name keyword">include</span> <span class="token string"><span class="token punctuation">"</span>footer.html<span class="token punctuation">"</span></span> <span class="token delimiter punctuation">%}</span></span>
<span class="token twig language-twig"><span class="token delimiter punctuation">{%</span> <span class="token tag-name keyword">include</span> <span class="token string"><span class="token punctuation">"</span>process-js.njk<span class="token punctuation">"</span></span> <span class="token delimiter punctuation">%}</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>body</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>html</span><span class="token punctuation">></span></span>
</code></pre>
<p>I actually started doing this because I <em>needed to</em> for the paired shortcodes on my projects page. <strong>Problem</strong>: I wanted to generate individual sections for each project, with their own screenshots and descriptions. I needed to be able to transform the images via a shortcode in this project image component (itself a shortcode since I repeat it). <strong>Solution</strong>: Set each project's description to a variable, storing the HTML in the <code>_includes</code> folder.</p>
<pre class="language-twig"><code class="language-twig">
<span class="token twig language-twig"><span class="token comment">{# projects/index.njk #}</span></span>
<span class="token twig language-twig"><span class="token delimiter punctuation">{%-</span> <span class="token tag-name keyword">set</span> frontroyal <span class="token delimiter punctuation">-%}</span></span>
<span class="token twig language-twig"><span class="token delimiter punctuation">{%-</span> <span class="token tag-name keyword">include</span> <span class="token string"><span class="token punctuation">"</span>front-royal-project-description.html<span class="token punctuation">"</span></span> <span class="token delimiter punctuation">-%}</span></span>
<span class="token twig language-twig"><span class="token delimiter punctuation">{%-</span> <span class="token tag-name keyword">endset</span> <span class="token delimiter punctuation">-%}</span></span>
<span class="token twig language-twig"><span class="token delimiter punctuation">{%-</span> <span class="token tag-name keyword">set</span> notsocial <span class="token delimiter punctuation">-%}</span></span>
<span class="token twig language-twig"><span class="token delimiter punctuation">{%-</span> <span class="token tag-name keyword">include</span> <span class="token string"><span class="token punctuation">"</span>notsocial-project-description.html<span class="token punctuation">"</span></span> <span class="token delimiter punctuation">-%}</span></span>
<span class="token twig language-twig"><span class="token delimiter punctuation">{%-</span> <span class="token tag-name keyword">endset</span> <span class="token delimiter punctuation">-%}</span></span>
<span class="token twig language-twig"><span class="token delimiter punctuation">{%-</span> <span class="token tag-name keyword">projectFeature</span> <span class="token string"><span class="token punctuation">'</span>Front Royal (The Band, Not The Town)<span class="token punctuation">'</span></span><span class="token punctuation">,</span> frontroyal<span class="token punctuation">,</span> <span class="token string"><span class="token punctuation">'</span>https://www.frontroyalband.com<span class="token punctuation">'</span></span><span class="token punctuation">,</span> <span class="token string"><span class="token punctuation">'</span>fr<span class="token punctuation">'</span></span> <span class="token delimiter punctuation">-%}</span></span>
<span class="token twig language-twig"><span class="token delimiter punctuation">{%-</span> <span class="token tag-name keyword">imageSync</span> <span class="token string"><span class="token punctuation">'</span>./public/img/front-royal1920x1080.jpeg<span class="token punctuation">'</span></span><span class="token punctuation">,</span> <span class="token string"><span class="token punctuation">'</span>Screenshot of the Front Royal website<span class="token punctuation">'</span></span><span class="token punctuation">,</span> projWidths<span class="token punctuation">,</span> imgFormats<span class="token punctuation">,</span> projSizes<span class="token punctuation">,</span> <span class="token string"><span class="token punctuation">'</span>full<span class="token punctuation">'</span></span> <span class="token delimiter punctuation">-%}</span></span>
<span class="token twig language-twig"><span class="token delimiter punctuation">{%-</span> <span class="token tag-name keyword">endprojectFeature</span> <span class="token delimiter punctuation">-%}</span></span>
<span class="token twig language-twig"><span class="token delimiter punctuation">{%-</span> <span class="token tag-name keyword">projectFeature</span> <span class="token string"><span class="token punctuation">'</span>NotSocial: A New Type of Social Media<span class="token punctuation">'</span></span><span class="token punctuation">,</span> notsocial<span class="token punctuation">,</span> <span class="token string"><span class="token punctuation">'</span>https://notsocial.app/<span class="token punctuation">'</span></span><span class="token punctuation">,</span> <span class="token string"><span class="token punctuation">'</span>ns<span class="token punctuation">'</span></span> <span class="token delimiter punctuation">-%}</span></span>
<span class="token twig language-twig"><span class="token delimiter punctuation">{%-</span> <span class="token tag-name keyword">imageSync</span> <span class="token string"><span class="token punctuation">'</span>./public/img/notsocial1920x1080.jpeg<span class="token punctuation">'</span></span><span class="token punctuation">,</span> <span class="token string"><span class="token punctuation">'</span>Screenshot of the NotSocial website<span class="token punctuation">'</span></span><span class="token punctuation">,</span> projWidths<span class="token punctuation">,</span> imgFormats<span class="token punctuation">,</span> projSizes<span class="token punctuation">,</span> <span class="token string"><span class="token punctuation">'</span>full<span class="token punctuation">'</span></span> <span class="token delimiter punctuation">-%}</span></span>
<span class="token twig language-twig"><span class="token delimiter punctuation">{%-</span> <span class="token tag-name keyword">endprojectFeature</span> <span class="token delimiter punctuation">-%}</span></span>
</code></pre>
<p>That <code>projectFeature</code> shortcode works like this:</p>
<pre class="language-js"><code class="language-js"><span class="token comment">// _includes/components/ProjectFeature.js</span>
<span class="token keyword">const</span> <span class="token punctuation">{</span>html<span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">"common-tags"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
module<span class="token punctuation">.</span><span class="token function-variable function">exports</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">content<span class="token punctuation">,</span> title<span class="token punctuation">,</span> description<span class="token punctuation">,</span> href<span class="token punctuation">,</span> id</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> html<span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string"> <article
class="project"
id="project_</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>id<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">">
<h2></span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>title<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"></h2>
<div class="skewed-background col full">
<div
class="wrapper"
data-constrain="some">
<div class="content">
<section class="project-image">
<a
href="</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>href<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">"
target="_blank"
rel="noopener">
</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>content<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">
</a>
</section>
<section class="project-description"></span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>description<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"></section>
</div>
</div>
</div>
</article></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<p><strong>Sweet.</strong></p>
<h2 id="i-could-keep-going" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2021/04/14/cool-template-features-with-eleventy/#i-could-keep-going">I Could Keep Going</a></h2>
<p>I'm super thrilled with my site right now. It's taking a lot of restraint to <em>not</em> implement some of these killer features in my other sites, but I know better than to subject myself to such a task on a project I made as a proof of concept.</p>
<p>While there are a ton of little things on this site I can talk about (like CSS, dynamic content via front matter, etc.), I need to cut this blog post off here.</p>
Plvylist is Now a Web Component2021-04-13T00:00:00Zhttps://www.troyv.dev/2021/04/13/plvylist-is-now-a-web-component/
<!-- @format -->
<p>It was only a few months ago that I <a href="https://www.troyv.dev/2020/11/20/plvylist-using-media-element-api/">created Plvylist</a> as a plugin-of-sorts. The functionality was all there, but it required too many pieces for such a simple idea: singular script that loads an audio player wherever you wish to put it. The bloat of this came from needing to manage a CSS file (or SASS if you're compiling it) <em>and</em> a JavaScript file <em>and</em> make sure you have an empty <code>div</code> with the right class on your page.</p>
<p>It wasn't super flexible unless you, as the user of it, went in and did a lot of customization, and even then you had to know where to look. Since publishing it, I learned more about <a href="https://www.webcomponents.org/">web components</a>, realizing that <em>this</em> was the right path forward for <a href="https://github.com/troyvassalotti/plvylist">Plvylist</a>.</p>
<h2 id="why-a-web-component-made-more-sense" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2021/04/13/plvylist-is-now-a-web-component/#why-a-web-component-made-more-sense">Why a Web Component Made More Sense</a></h2>
<p>As a web component, I get all the functionality I was looking for with the added flexibility of customization, and a clearer vision of what pieces affect other pieces.</p>
<ul>
<li>Everything is contained in a single file, <code>plvylist-player.js</code> - including the CSS, global variables, scripts/functions, and HTML (through the use of template literals).</li>
<li>It's a single <code><plvylist-player></plvylist-player></code> on your HTML with some values passed in as attributes, so the component knows where to look for audio files, images, and artist or album names.
<ul>
<li>For example, <code><plvylist-player audio-location="path/to/tracks/"></code> tells Plvylist that your songs are located in <code>path/to/tracks/</code> and will use said path when populating the audio source.</li>
</ul>
</li>
</ul>
<blockquote>
<p>You can view the <a href="https://codepen.io/troyvassalotti/full/ExyOgGV">new and improved demo</a> if you want.</p>
</blockquote>
<p>While it's in a much better position than before, there is still room to grow. There are a handful of assumptions required to make this work:</p>
<ul>
<li>You are hosting the files locally in the project <em>or</em> you are able to manually input the <strong>names</strong> of the tracks and their files.</li>
<li>You are hosting the cover art locally <em>or</em> can directly link to their locations.</li>
<li>You already know the name of the album or artist, <em>or</em> you can manually input them for each track you provide.</li>
<li>You are at least <em>a little</em> familiar with Web Components.</li>
</ul>
<h2 id="future-improvements" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2021/04/13/plvylist-is-now-a-web-component/#future-improvements">Future Improvements</a></h2>
<p>I want to be able to use it to fetch files from a remote location and dynamically gather artist of album names. If it was a Node application then I think I have an idea of how I'd achieve such a thing, but that's not what this is right now.</p>
<p>It would also be cool to be able to use ES modules as an import method for the tracks instead of needing to declare the array of objects in the component script directly; I tried a few things but realized it needs refactoring for that.</p>
<p>Another cool thing for the benefit of being more readable as a developer would be to move all the function declarations to their own levels like the <code>connectedCallback()</code> function is.</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">class</span> <span class="token class-name">Plvylist</span> <span class="token keyword">extends</span> <span class="token class-name">HTMLElement</span> <span class="token punctuation">{</span>
<span class="token function">constructor</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">super</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">attachShadow</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token literal-property property">mode</span><span class="token operator">:</span> <span class="token string">"open"</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">this</span><span class="token punctuation">.</span>tracks <span class="token operator">=</span> <span class="token punctuation">[</span>
<span class="token punctuation">{</span>
<span class="token literal-property property">file</span><span class="token operator">:</span>
<span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">getAttribute</span><span class="token punctuation">(</span><span class="token string">"audio-location"</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">"name_of_your_file.mp3"</span> <span class="token operator">||</span> <span class="token string">""</span><span class="token punctuation">,</span>
<span class="token literal-property property">title</span><span class="token operator">:</span> <span class="token string">"Your Track Title"</span> <span class="token operator">||</span> <span class="token string">""</span><span class="token punctuation">,</span>
<span class="token literal-property property">artist</span><span class="token operator">:</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">getAttribute</span><span class="token punctuation">(</span><span class="token string">"artist-name"</span><span class="token punctuation">)</span> <span class="token operator">||</span> <span class="token string">""</span><span class="token punctuation">,</span>
<span class="token literal-property property">album</span><span class="token operator">:</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">getAttribute</span><span class="token punctuation">(</span><span class="token string">"album-name"</span><span class="token punctuation">)</span> <span class="token operator">||</span> <span class="token string">""</span><span class="token punctuation">,</span>
<span class="token literal-property property">artwork</span><span class="token operator">:</span>
<span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">getAttribute</span><span class="token punctuation">(</span><span class="token string">"cover-art"</span><span class="token punctuation">)</span> <span class="token operator">||</span>
<span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">getAttribute</span><span class="token punctuation">(</span><span class="token string">"placeholder-image"</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">]</span><span class="token punctuation">;</span>
<span class="token comment">/* a bunch of other omitted variables */</span>
<span class="token punctuation">}</span>
<span class="token function">connectedCallback</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token comment">/* omitted code */</span>
<span class="token punctuation">}</span>
<span class="token function">loadTrackList</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token comment">/* like this */</span>
<span class="token punctuation">}</span></code></pre>
<p>Visually, it'd help to know where things are. I tried doing this as well - and got pretty close with functions like the play and pause methods - but it also required <em>a lot</em> of working with <code>this</code> and again, the whole thing would need refactored to make it work.</p>
<h2 id="in-conclusion" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2021/04/13/plvylist-is-now-a-web-component/#in-conclusion">In Conclusion</a></h2>
<p>Web components are really super cool, and you should try them out. Also, give Plvylist a try and hopefully it solves someone else's problem instead of only mine (after all, <em>open source</em>).</p>
I Logged My Migraines For Another Year (2020)2021-02-23T00:00:00Zhttps://www.troyv.dev/2021/02/23/i-logged-my-migraines-for-another-year-2020/
<!-- @format -->
<p>We learned last year that my migraine situation was a problem. Well, more of a problem than usual. You can read more about <a href="https://www.troyv.dev/2020/03/12/i-logged-my-migraines-for-a-year-2019/">how I did in 2019</a>, but it wasn't great. I continued to log each migraine I had throughout 2020 because I'm a nerd.</p>
<p>I made improvements, but they came in the second half of the year since it takes that long for trial and error to work. That said, I ended the year at <strong>45 migraines</strong> (a drop from <strong>67</strong> in 2019). It's still not ideal, but plenty of people have it worse than I do, so I'm lucky in that sense.</p>
<p>The shining light of this year's log is that I now have a working calendar heatmap created with the power of D3. This is great because D3 is more in my wheelhouse than Tableu and I can keep it running for each new year I decide to keep doing this madness.</p>
<p>Here's the resulting calendar to showcase 2019 and 2020.</p>
<style>::part(heatmap) {font-family: var(--code)}</style>
<script src="https://www.troyv.dev/assets/js/calendar-heatmap.js" type="module"></script>
<p><calendar-heatmap data-src="/assets/js/migraines-2020.json"></calendar-heatmap></p>
<p>D3 is fun and exciting and equally complicated, so my chart only goes so far in displaying the data. I only keep track of the <strong>day</strong> and <strong>start time</strong> so the chart isn't too overwhelming, but it makes it more maintainable for myself as well.</p>
<h2 id="what-were-the-causes%3F" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2021/02/23/i-logged-my-migraines-for-another-year-2020/#what-were-the-causes%3F">What Were The Causes?</a></h2>
<p>Look at this graph:</p>
<p><img src="https://res.cloudinary.com/dpmchqezv/image/upload/c_scale,f_auto,q_auto:eco/v1646349103/blog/2020-by-triggers_prdhi6.webp" alt="A graph showing the associated trigger for each of the 45 migraines I had. Among those triggers are stress, alcohol, poor sleep, anxiety, rebounds, natural causes, and weather." /></p>
<p>It's <em>very hard</em> to associate what causes a migraine. For one, it isn't always due to a single trigger alone but rather a multitude or building up of triggers over time. Also, it feels too reductive at times - especially when I have no idea what happened and I can only point to <em>natural causes</em>.</p>
<p>It shouldn't be surprising then that the order is as follows:</p>
<ol>
<li>Stress or Natural Causes</li>
<li>Weather</li>
<li>Poor Sleep</li>
<li>Rebound headaches or Anxiety</li>
<li>Alcohol (thanks New Year's Eve)</li>
</ol>
<h2 id="when-did-they-most-happen%3F" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2021/02/23/i-logged-my-migraines-for-another-year-2020/#when-did-they-most-happen%3F">When Did They Most Happen?</a></h2>
<p>I mentioned earlier that I saw improvements in the later part of the year. October was the only month I went without a single attack (woohoo), but from that point on they've been sparse. This is a result of finding the right routine and medications, but it feels great to not live in a constant state of tension-headache-fear.</p>
<p>Look at this graph now:</p>
<p><img src="https://res.cloudinary.com/dpmchqezv/image/upload/c_scale,f_auto,q_auto:eco/v1646349103/blog/2020-by-quarter_iqkdev.webp" alt="A graph showing the number of migraines I had per quarter of the year." /></p>
<ul>
<li><strong>January - March</strong>: Not great. 10 migraines.</li>
<li><strong>April - June</strong>: Even worse at 16.</li>
<li><strong>July - September</strong>: Barely better with 15.</li>
<li><strong>October - December</strong>: A pleasant total of 4.</li>
</ul>
<p>Since I keep track of the time of day they happen, I also know what <em>times of day</em> they most occurred. If that sounds interesting to you, then you'll love this graph:</p>
<p><img src="https://res.cloudinary.com/dpmchqezv/image/upload/c_scale,f_auto,q_auto:eco/v1646349103/blog/2020-by-time_o98q5l.webp" alt="A histogram showing the number of migraines I had broken out by the time of day in buckets of 4 hours." /></p>
<p>This one is a little less telling since it's a pretty regular distribution, but I had the most migraines between the hours of noon - 4:00 p.m. The second place slot was tied with midnight - 4:00 a.m. and 8:00 a.m. - noon.</p>
<blockquote>
<p>It's not like I was staying up late all the time and that's why I got so many after midnight. The reason for that is I have the unfortunate habit of waking up in the middle of the night with a migraine fully in progress.</p>
</blockquote>
<h2 id="what-now%3F" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2021/02/23/i-logged-my-migraines-for-another-year-2020/#what-now%3F">What Now?</a></h2>
<p>I've stopped doing all the logging by hand because it is cumbersome trying to keep track of every little detail myself. There's this cool app called <em>Migraine Buddy</em> that I'm trying now. So far, it's great because it asks all the same questions I was asking myself <em>and then some</em>! I need to keep track of the dates and times separately for my calendar tracker, but that's a worthwhile trade.</p>
<p>Here's to another year of hopeful progress.</p>
"Sweet Talk" by Dear and the Headlights Cover2021-01-14T00:00:00Zhttps://www.troyv.dev/2021/01/14/sweet-talk-by-dear-and-the-headlights-cover/
<!-- @format -->
<p>I don't need to reiterate what 2020 is like. My friends and I, you may know us as Front Royal - the band, not the town - were sitting restless in our separate homes after the <a href="https://frontroyalmd.bandcamp.com/">release of our debut LP</a>, so we decided to cover one of our favorite songs: <em>Sweet Talk</em> by Dear and the Headlights.</p>
<p>Watch the video here:</p>
<p>https://www.youtube.com/watch?v=p18VbHbVJ78</p>
Plvylist: Using Media Element API2020-11-20T00:00:00Zhttps://www.troyv.dev/2020/11/20/plvylist-using-media-element-api/
<!-- @format -->
<p>At work, I'm surrounded by different web audio components. They're integral to the business, but they're also integral to so many businesses, and I barely understood how they get made.</p>
<p>The basics made sense. Putting an <code><audio></code> or <code><video></code> element on a page with the proper attributes gives you a native song or movie with built-in controls, got it. Beyond that? No clue. So, I got to work learning and created <a href="https://github.com/troyvassalotti/plvylist">Plvylist - my own component for web audio</a>.</p>
<h2 id="complicated%2C-but-documented" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2020/11/20/plvylist-using-media-element-api/#complicated%2C-but-documented">Complicated, But Documented</a></h2>
<p>It took weeks to figure this stuff out completely. Honestly, I almost gave up some of the functionality for Plvylist because I thought they were lost causes. The documentation was thorough for sure, but it was also fragmented in ways that didn't quite make sense to me. It was as if I had to read docs for unrelated topics to find all the answers I was looking for.</p>
<p>This project went through many iterations as well. What started as a project on Glitch turned into <a href="https://codepen.io/troyvassalotti/full/ExyOgGV">a CodePen</a> which became a GitHub repo that is now a component I use in production on <a href="https://www.troyv.dev/music/">two different</a> projects.</p>
<p>Overall, Plvylist can be broken down into three pieces:</p>
<ul>
<li>HTML</li>
<li>CSS/SASS</li>
<li>JS</li>
</ul>
<h2 id="html" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2020/11/20/plvylist-using-media-element-api/#html">HTML</a></h2>
<p>The HTML wasn't too bad. When building the demo, it was mostly HTML with JS sprinkled in, but now all the elements are generated using <code>plvylist.js</code> and all you need in your document to latch on to is a simple <code><div class="plvylist"></div></code>.</p>
<p>Since I wanted to use Plvylist to stream my music on projects, I wanted particular aspects to be visible:</p>
<ul>
<li>Album artwork,</li>
<li>Track list with selectable elements,</li>
<li>Artist, song, and album details,</li>
<li>Controls to repeat, shuffle, seek, and change volume.</li>
</ul>
<p>You know, all basic stuff.</p>
<h2 id="css%2Fsass" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2020/11/20/plvylist-using-media-element-api/#css%2Fsass">CSS/SASS</a></h2>
<p>SASS is great and I use it a lot, so I decided this would be the route to go with Plvylist. This was confirmed for me when I learned <strong>how insanely hard</strong> it is to style a <code><input type="range"></code> across browsers. Every valid CodePen example I found used SASS to accomplish this and I was not going to be messing with that.</p>
<pre class="language-css"><code class="language-css"><span class="token atrule"><span class="token rule">@mixin</span> <span class="token function">track</span><span class="token punctuation">(</span>$<span class="token property">fill</span><span class="token punctuation">:</span> 0<span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
<span class="token property">box-sizing</span><span class="token punctuation">:</span> border-box<span class="token punctuation">;</span>
<span class="token property">max-width</span><span class="token punctuation">:</span> $plvyTrack-w<span class="token punctuation">;</span>
<span class="token property">height</span><span class="token punctuation">:</span> $plvyTrack-h<span class="token punctuation">;</span>
<span class="token property">background</span><span class="token punctuation">:</span> $plvyTrack-c<span class="token punctuation">;</span>
<span class="token property">border-radius</span><span class="token punctuation">:</span> 6px<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token atrule"><span class="token rule">@mixin</span> <span class="token function">fill</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
<span class="token property">height</span><span class="token punctuation">:</span> $plvyTrack-h<span class="token punctuation">;</span>
<span class="token property">background</span><span class="token punctuation">:</span> $plvyFill-c<span class="token punctuation">;</span>
<span class="token property">border-radius</span><span class="token punctuation">:</span> 6px<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token atrule"><span class="token rule">@mixin</span> <span class="token function">thumb</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
<span class="token property">box-sizing</span><span class="token punctuation">:</span> border-box<span class="token punctuation">;</span>
<span class="token property">background</span><span class="token punctuation">:</span> $plvyThumb-c<span class="token punctuation">;</span>
<span class="token property">width</span><span class="token punctuation">:</span> $plvyThumb-d<span class="token punctuation">;</span>
<span class="token property">height</span><span class="token punctuation">:</span> $plvyThumb-d<span class="token punctuation">;</span>
<span class="token property">border-radius</span><span class="token punctuation">:</span> 50%<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.plvylist input[type="range"]</span> <span class="token punctuation">{</span>
<span class="token property">--range</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--max<span class="token punctuation">)</span> - <span class="token function">var</span><span class="token punctuation">(</span>--min<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">--ratio</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--val<span class="token punctuation">)</span> - <span class="token function">var</span><span class="token punctuation">(</span>--min<span class="token punctuation">)</span><span class="token punctuation">)</span> / <span class="token function">var</span><span class="token punctuation">(</span>--range<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token selector">--sx: calc(0.5 * #</span><span class="token punctuation">{</span>$plvyThumb-d<span class="token punctuation">}</span> <span class="token selector">+ var(--ratio) * (100% - #</span><span class="token punctuation">{</span>$plvyThumb-d<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">max-width</span><span class="token punctuation">:</span> $plvyTrack-w<span class="token punctuation">;</span>
<span class="token property">height</span><span class="token punctuation">:</span> $plvyTrack-h<span class="token punctuation">;</span>
<span class="token selector">&::-webkit-slider-runnable-track</span> <span class="token punctuation">{</span>
<span class="token atrule"><span class="token rule">@include</span> <span class="token function">track</span><span class="token punctuation">(</span>1<span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="token punctuation">}</span>
<span class="token selector">&::-moz-range-track</span> <span class="token punctuation">{</span>
<span class="token atrule"><span class="token rule">@include</span> track<span class="token punctuation">;</span></span>
<span class="token property">padding-top</span><span class="token punctuation">:</span> 1.5px<span class="token punctuation">;</span>
<span class="token property">padding-bottom</span><span class="token punctuation">:</span> 1.5px<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">&::-ms-track</span> <span class="token punctuation">{</span>
<span class="token atrule"><span class="token rule">@include</span> track<span class="token punctuation">;</span></span>
<span class="token punctuation">}</span>
<span class="token selector">&::-moz-range-progress</span> <span class="token punctuation">{</span>
<span class="token atrule"><span class="token rule">@include</span> fill<span class="token punctuation">;</span></span>
<span class="token punctuation">}</span>
<span class="token selector">&::-webkit-slider-thumb</span> <span class="token punctuation">{</span>
<span class="token property">margin-top</span><span class="token punctuation">:</span> -9px<span class="token punctuation">;</span>
<span class="token atrule"><span class="token rule">@include</span> thumb<span class="token punctuation">;</span></span>
<span class="token punctuation">}</span>
<span class="token selector">&::-moz-range-thumb</span> <span class="token punctuation">{</span>
<span class="token property">border</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span>
<span class="token atrule"><span class="token rule">@include</span> thumb<span class="token punctuation">;</span></span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>But then I evaluated how many personal projects I have, the anxiety I feel to open up an old one due to outdated dependencies and needing to adjust configs, and decided I want to be less reliant on npm and other packages in favor of the built in technologies the web has to offer us. In the end, I think we can all agree that CSS has evolved <em>a lot</em> over the years. I was really only using SASS for a few things:</p>
<ul>
<li>Nesting,</li>
<li>Very limited mixins,</li>
<li>Imports for minor uses,</li>
<li>Globals that I later replaced with CSS variables anyway.</li>
</ul>
<p>So, I changed the SASS stylesheet to a modern CSS stylesheet and it wasn't that hard because I used the power of SASS to do so!</p>
<h3 id="how-to-turn-sass-into-css-easily" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2020/11/20/plvylist-using-media-element-api/#how-to-turn-sass-into-css-easily">How To Turn SASS Into CSS Easily</a></h3>
<p>Compile it.</p>
<p>Kidding. I use Atom, and I have a package to compile SASS in Atom regardless of project; it only requires a global installation of node-sass. It's convenient, but installing it has caused weird issues at times that I want to avoid forever in the future when I need to update things, so I used the power of SASS to quickly convert my SASS stylesheets into a usable CSS stylesheet without much fuss.</p>
<p>The package has different output styles (minified, expanded, etc...) and you can tell it where to drop those files when compiled. I chose to compile expanded so it would look like a normal stylesheet but in CSS - no minification because Netlify would do that for me, and I have another package that can minify if I want to.</p>
<p>I wasn't going to go manually replacing all the <code>$globals</code> with CSS variables though, so I had a thought: what if I set each global variable to their respective CSS variable name?</p>
<blockquote>
<p><code>$plvyMainFont: var(--plvyMainFont)</code></p>
</blockquote>
<p>This way, when the SASS gets compiled, instead of being replaced with static values they get replaced with variable names. Perfect! Then, all I had to do was also copy the globals block, keep their values, and change all the dollar signs to double-dashes in a <code>:root{}</code> set, and compile. All mixins and imports could stay the same because their global values would be filled in as necessary.</p>
<p>It was so simple once I sat down and thought about it.</p>
<p>I keep the SASS version up-to-date so anyone else can use it if they want to (thanks!), but we should all probably use the CSS version.</p>
<h2 id="js" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2020/11/20/plvylist-using-media-element-api/#js">JS</a></h2>
<p>This is where all the magic happens. Without this, nothing works. Well, you need to also have some audio files somewhere, but the JavaScript is where you declare those files anyway.</p>
<p>There's a lot going in the script to be frank, but I have some favorites. For one, I learned how to create an object and iterate over that object to set attributes on an HTML element.</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> artwork <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">createElement</span><span class="token punctuation">(</span><span class="token string">"img"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> artworkAttributes <span class="token operator">=</span> <span class="token punctuation">{</span>
<span class="token literal-property property">src</span><span class="token operator">:</span> <span class="token string">""</span><span class="token punctuation">,</span>
<span class="token literal-property property">width</span><span class="token operator">:</span> <span class="token string">"300"</span><span class="token punctuation">,</span>
<span class="token literal-property property">height</span><span class="token operator">:</span> <span class="token string">"300"</span><span class="token punctuation">,</span>
<span class="token literal-property property">alt</span><span class="token operator">:</span> <span class="token string">"album artwork"</span><span class="token punctuation">,</span>
<span class="token literal-property property">id</span><span class="token operator">:</span> <span class="token string">"artwork"</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> artworkAttributesKeys <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">keys</span><span class="token punctuation">(</span>artworkAttributes<span class="token punctuation">)</span><span class="token punctuation">;</span>
artworkAttributesKeys<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">key<span class="token punctuation">,</span> index</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
artwork<span class="token punctuation">.</span><span class="token function">setAttribute</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>key<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">,</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>artworkAttributes<span class="token punctuation">[</span>key<span class="token punctuation">]</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>I also found a neat way to build the track list in HTML in a performant way - that is, all at once and without appending elements to each other multiple times until each track is iterated over. Instead of using a loop to create a new element for each song in the songs array and appending the list items to each other, a variable - called <code>list</code> - is being created as a string of HTML, and it doesn't get completed until each track has been accounted for.</p>
<p>Once the <code>forEach</code> loop is done, <code>list</code> is done being generated, and then <code>list</code> is plopped into the HTML for <code>songs</code> variable. After that, event listeners are added to each list item to allow for playing when clicking on the track name.</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">function</span> <span class="token function">loadTrackList</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">let</span> list <span class="token operator">=</span> <span class="token string">""</span><span class="token punctuation">;</span>
tracks<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">track<span class="token punctuation">,</span> index</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
list <span class="token operator">+=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string"><li data-track="</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>index<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">" data-file="</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>track<span class="token punctuation">.</span>file<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">" class="plvy--song"><span class="plvy--song__title"></span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>track<span class="token punctuation">.</span>title<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"></span></li></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
songs<span class="token punctuation">.</span>innerHTML <span class="token operator">=</span> list<span class="token punctuation">;</span>
allTracks <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelectorAll</span><span class="token punctuation">(</span><span class="token string">".plvy--song__title"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
allTracks<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">track<span class="token punctuation">,</span> index</span><span class="token punctuation">)</span> <span class="token operator">=></span>
track<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">"click"</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>settings<span class="token punctuation">.</span>currentTrack <span class="token operator">===</span> <span class="token keyword">undefined</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token function">loadTrack</span><span class="token punctuation">(</span>index<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">pressPlay</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>audio<span class="token punctuation">.</span>paused<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token function">loadTrack</span><span class="token punctuation">(</span>index<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
<span class="token function">loadTrack</span><span class="token punctuation">(</span>index<span class="token punctuation">)</span><span class="token punctuation">;</span>
audio<span class="token punctuation">.</span><span class="token function">play</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>The last challenge I had was figuring out how to properly highlight the actively-playing track in the track list. I kept running into bugs where a track wouldn't lose its highlight class even after it was deselected or ended. The key was to learn some new events to latch on to - <code>emptied</code> and <code>loadstart</code>.</p>
<pre class="language-js"><code class="language-js"><span class="token comment">// on loadstart of the audio resource, change the active song class</span>
audio<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">"loadstart"</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token keyword">let</span> getter <span class="token operator">=</span> settings<span class="token punctuation">.</span>currentTrack<span class="token punctuation">;</span>
document
<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">[data-file="</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>tracks<span class="token punctuation">[</span>getter<span class="token punctuation">]</span><span class="token punctuation">.</span>file<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">"]</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span>
<span class="token punctuation">.</span>classList<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">"plvy--song__active"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// when the track gets emptied, remove the active track class</span>
audio<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">"emptied"</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
document
<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">".plvy--song__active"</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span>classList<span class="token punctuation">.</span><span class="token function">remove</span><span class="token punctuation">(</span><span class="token string">"plvy--song__active"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<h2 id="final-thoughts" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2020/11/20/plvylist-using-media-element-api/#final-thoughts">Final Thoughts</a></h2>
<p>This could be better, yes. There are plenty of ways this can be improved that I'm both aware and not aware of. But it works for now and that's what matters. I had a problem and I solved it. Some improvements to be had in the future though:</p>
<ul>
<li>Make it accessible. I don't actually know how accessible it is right now and without a doubt it could be better (as most things could be).</li>
<li>Make it cleaner.</li>
<li>Fix the bugs on Chromium. There's a weird bug where the seeker will jump to 50% when changing tracks and I have no idea why that happens.</li>
</ul>
An Abandoned Website2020-09-12T00:00:00Zhttps://www.troyv.dev/2020/09/12/an-abandoned-website/
<!-- @format -->
<p>Websites can be bought and live on forever...or so long as the domain name continues to be renewed. As a result, some sites become abandoned, living in the aether, but because of backwards compatibility they don't really <em>look abandoned</em>.</p>
<p>Well, what if they did? What if the website deteriorated over time, things fell apart, and the darkness came to consume it? That's where my new project, <a href="https://abandoned-website.netlify.app/">Abandoned Website</a>, comes in.</p>
<p>I don't know when, why, or how I came up with the idea to create this project. If I'm remembering correctly, it was way back in my early stages of learning to code - you know, when you're full of ideas that sound <em>fun</em> and <em>exciting</em> and <em>definitely not hard to implement</em> - and I jotted it down as a note in my phone.</p>
<p>I didn't have the skills to build it yet.</p>
<ul>
<li>No design skills.</li>
<li>I could barely code an email and that was my full-time job.</li>
<li>No clear idea how to even make a website live.</li>
</ul>
<p>But I knew one day I'd get there, and here we are.</p>
<p>As with every project, the final product never quite lives up to the vision in your head. I'm getting better at design with each project - at least I hope so - but I know it would have been much better implemented if a designer-by-trade did the bulk of the work in <a href="https://www.figma.com/">Figma</a> instead of me.</p>
<p>Anyway, with everything that was 2020, it came time to pick this project up! I created this site with the intention of - as I mentioned - making a website look abandoned, but it turned out to also be a journey into the world of building a website <a href="https://gohugo.io/">using Hugo</a>.</p>
<p>Hugo's great. I was really worried that I chose the wrong static site generator when I actually read the docs (should've learned my <a href="https://www.troyv.dev/2020/08/27/notsocial-a-social-media-revolution/">lesson from fake social media app</a>), but I accepted the challenge and made it anyway.</p>
<p>It wasn't a complete mistake! Now, Hugo might not be the best suited option here:</p>
<ul>
<li>This site isn't really a blog.</li>
<li>It's only a few pages.</li>
<li>Not a lot gets reused.</li>
</ul>
<p>Only later in the build process did I realize those were shortcomings, but once I understood the way the directory structure works in Hugo it all made sense, and I was able to make it work for me.</p>
<p>Honestly, the hardest parts became the following:</p>
<ol>
<li>Getting the favicon to appear when in the root directory (I gave up and put it in the <code>/img/</code> directory instead).</li>
<li>Templating the page titles the way I wanted them to be (Learning about the <code>{{ .Page.Title }}</code> variable changed everything for me).</li>
<li>Setting up the 404 page to work within the <code>baseof.html</code> template while still having its own special treatment (inline <code><style></code> blocks and <code>noindex</code> directive).</li>
</ol>
<p>I hope to continue working on it. Side projects are hard to find the time for, but this one is more than a proof-of-concept. Hell, I bought a domain name for it. I'd like to really take things I learn over time and make this thing better with every iteration.</p>
NotSocial: A Social Media Revolution2020-08-27T00:00:00Zhttps://www.troyv.dev/2020/08/27/notsocial-a-social-media-revolution/
<!-- @format -->
<p>We can all agree that social media is a lot to handle sometimes. I know I have a habit of scrolling on Reddit for far too long before I realize how long it's been, and it isn't isolated to Reddit. Twitter, Instagram, and the like are all in this bucket.</p>
<p>Since deleting myself from Facebook, I've realized a few things about myself and others, but that's not what this post is about. This post is about <a href="https://pang.netlify.app/">NotSocial</a>.</p>
<p>NotSocial is my new passion; it's more than an app - it's an ideology. You may be thinking that this is exactly the problem that other social media platforms have: they become more than an app and grow into a cultural phenomenon, and I'm not here to tell you whether you're right or wrong.</p>
<p>But I am here to tell you how NotSocial is a new kind of social media platform where it is only you that exists in its world. It's a section of the internet for you - and only you - to be social with yourself while feeling connected with the rest of the world.</p>
<ul>
<li>Enjoy the thrill of Twitter while also being able to <strong>wipe it off the face of your device screen.</strong></li>
<li>Comment, like, and even dislike posts from your favorite news outlets, celebrities, and crazy relatives (bonus points if you reach the high score).</li>
<li>See your direct messages, potentially play games (no promises), and look at dog pictures.</li>
</ul>
<p>When you're all done, close your account without hassle, free of charge. I make it super simple to rid yourself of NotSocial because it should never be an inconvenience to delete yourself from an internet service.</p>
<h2 id="give-me-the-technical-details" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2020/08/27/notsocial-a-social-media-revolution/#give-me-the-technical-details">Give Me The Technical Details</a></h2>
<p>NotSocial started out as a simple idea on <a href="https://codepen.io/">CodePen</a> practicing Vue. It was originally called <em>Pang</em> because Merriam-Webster defines <em>pang</em> as "a sharp attack of mental anguish," which is all social media is anyway.</p>
<blockquote>
<p>You can even <a href="https://codepen.io/troyvassalotti/full/RwWLyBV">view the original NotSocial there</a> still. Be warned though, it's a little rough around the edges in terms of how Vue works and overall design.</p>
</blockquote>
<p>I thought I was done after publishing that, but I knew it could be better. Not only could it be better looking and more thought out, but I could broaden my skills and <a href="https://nuxtjs.org/">make it with Nuxt</a>, a static site generator for Vue. I knew it wouldn't be a walk in the park, but I didn't expect it to be as challenging as it was either.</p>
<p>You can take a look at my commit history if you want to; It's a little embarrassing, but funny. You can see the thought process going on as I struggled to make the site: "Wow, you gotta wrap everything in a div or whatever to make it work."</p>
<p>Long story short, it took me a few months to get this thing right. I had to read <em>a lot</em> of Nuxt documentation to really, truly, understand what the heck I was doing, and it was worth it because NotSocial 2.0 is beautiful (to me, at least right now).</p>
<p>I learned a lot using Nuxt and Vue to make this faux-app. Both tools are incredibly useful for something like this, which I why I chose them instead of something like Hugo or Eleventy, two SSGs I use for separate projects.</p>
<p>After all this time though, it is still a fun side project. If I were to open up that folder in Atom right now, I would probably be overtaken with dread by the thought of having to update npm or something <a href="https://css-tricks.com/npm-ruin-dev/">a popular topic right now</a>. So, don't ask me to make any updates please.</p>
<h2 id="lessons-learned" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2020/08/27/notsocial-a-social-media-revolution/#lessons-learned">Lessons Learned</a></h2>
<ul>
<li>Always read the documentation.</li>
<li>Patience is a virtue.</li>
<li>Maybe don't rely on npm for everything if you want to work on a thing long term.</li>
</ul>
Finding New In The Old2020-06-25T00:00:00Zhttps://www.troyv.dev/2020/06/25/finding-new-in-the-old/
<!-- @format -->
<p>I don't write as much music as I used to. There's a combination of reasons for why that is or may be, but their irrelevant to this post. Though I may not sit down and produce a song in its entirety that often, it's a given that I'll end up recording a snippet of a future song during my practice sessions.</p>
<p>Those recordings - typically a video so I can see myself doing the thing - end up in my Google photos library where they get thrown into a folder aptly named "Song Ideas."</p>
<p>In case you haven't guessed yet, there are/were problems with that process.</p>
<ol>
<li>I can't search for any of these videos by name;</li>
<li>They don't get categorized any further than being labeled a <em>song idea</em>.</li>
</ol>
<p>My old process was to periodically download them to my laptop and organize them locally in folders named by genres (or percentage complete as a song). Then, when I am in the mood to write for a longer period of time, I create a Guitar Pro 5 session and tab it all out - guitars, drums, bass, you name it.</p>
<p>This Rube Goldberg machine of songwriting got the best of me, so I finally made a better system through the use of a Sync.com account, automatic uploads to the cloud for each video/photo, and subsequent categorization with those aptly named folders that now live in the data atmosphere.</p>
<p>Things get lost though; with hundreds of ideas living in video form, I don't have the time or desire to make a full-blown song out of all of them. Hell, some are real blasts from the past to a time when I was about 30lbs heavier and didn't know a soul patch looked terrible on a high schooler.</p>
<p>But one of those lost treasures stood out to me recently; it's a song I wrote in 2013 as part of a beginner's music theory course.</p>
<p>The purpose of the project was something along the lines of needing to write a song <em>X</em> measures long with <em>Y</em> sections and <em>Z</em> number of instruments. I was incredibly confident going into it considering I already had years of playing and writing behind me.</p>
<blockquote>
<p>Remember: this is a <em>music theory</em> course, and I had neglected to care about anything music theory up until this point. I wanted to learn it and the course fit my schedule, but I shoudn't have been as confident as I was.</p>
</blockquote>
<p>It turned out to be a test of what little ego I had at the time. I wrote this fast-paced punk-style song with bright melodies and non-standard chords - my typical I've come to learn.</p>
<p>I was pretty proud of the little thing and thought for sure I'd nailed it.</p>
<p>My professor reviewed it and sent back his notes, including a re-write of the song to show me what he meant. Among those edits were:</p>
<ul>
<li>Cutting the BPM in half;</li>
<li>Solidifying it into a major key;</li>
<li>Changing emphasis of key chords;</li>
<li>And some other things that would be too much to list.</li>
</ul>
<p>I was a bit unsettled hearing the changes, but it was nothing meant to offend me. He was incredibly nice and helpful about the project. Looking back, I greatly appreciate it even if it changed the theme of the song so much.</p>
<p>I realize now that what he did was make it better. Sure, I still don't write songs that keep to the key as tightly as they could - and I like it that way - but his rendition of my song showed me there were more paths to take with a melody.</p>
<p>And it gave me a taste of what a back-and-forth songwriting process could look like in the future.</p>
<p>I'll finish writing that song some day; maybe this year, maybe next year, but eventually. And I'll always remember how it got to where it was and what lessons can be learned by handing off a creation of your own and letting someone else make it their own.</p>
<p>In this case, it made it better. Collaboration in music is truly a great thing.</p>
Using Media Queries to Make a Light Mode Website2020-04-27T00:00:00Zhttps://www.troyv.dev/2020/04/27/using-media-queries-to-make-a-light-mode-website/
<!-- @format -->
<p>I was having a normal Saturday morning of exploring the latest features in <a href="https://play.google.com/store/apps/details?id=org.mozilla.fenix.nightly">Firefox Preview Nightly</a> and installed the Dark Reader add-on to see what it does to my site.</p>
<p>As an avid dark mode user, I designed my site with dark mode in mind. I wanted it to be dark by default and leave open the idea of a light mode version down the line.</p>
<p>That idea was pushed aside as I made more pertinent enhancements, but after giving Dark Reader a whirl, I got real excited about the <code>prefers-color-scheme</code> media query. So, I read up on the media queries available to us now and made some designs.</p>
<p><strong>PROBLEM ALERT</strong>: If you're viewing this on your phone as of April 27th, 2020, you might realize your browser has taken my <code>prefers-color-scheme: light</code> styles as default, even if your system preferences are set to "dark" and the default styles of my site use darker colors <em>(looking at you Firefox & Edge)</em>.</p>
<p>I don't know how to get the lot of you back to the original color scheme I intended, but luckily I like the light mode scheme enough to consider it a not-too-important issue.</p>
<h2 id="dark-reader-vs.-troyv.dev" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2020/04/27/using-media-queries-to-make-a-light-mode-website/#dark-reader-vs.-troyv.dev">Dark Reader vs. troyv.dev</a></h2>
<p>Take a look at my homepage in its default form compared to the dark and light emulation from Dark Reader:</p>
<p><img src="https://res.cloudinary.com/dpmchqezv/image/upload/c_scale,f_auto,q_auto:eco/v1646349103/blog/mobile-all-three_fcgn17.webp" alt="What my site looks like without any sort of color scheme emulation, a dark color scheme, and a light color scheme" /></p>
<p>As you might expect, using Dark Reader turned my site into a dark-but-not-too-dark wonderland. <em>But</em>, changing the settings in the add-on to a "light" scheme causes a handful of problems. Mainly, some work I did to make sure my colors were properly contrasting was thrown out the window.</p>
<p>This is an issue. I don't want people emulating a false light mode when using this website because they will face some potentially-major problems.</p>
<h2 id="incoming%3A-the-light-mode-experience" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2020/04/27/using-media-queries-to-make-a-light-mode-website/#incoming%3A-the-light-mode-experience">Incoming: The Light Mode Experience</a></h2>
<p>I love making these minor improvements to this site because I know the use cases might be small, but the experience will be appreciated to those who are looking for it. Plus, any project that involves CSS is a project I want in on.</p>
<h3 id="step-1%3A-color-palette" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2020/04/27/using-media-queries-to-make-a-light-mode-website/#step-1%3A-color-palette">Step 1: Color Palette</a></h3>
<p>I jumped into my favorite <a href="https://coolors.co/">color palette generator, coolors.co</a>, and spun the wheel a few times until I found a basic scheme I liked, locking in colors as I found fit and generating more until I came out with this beautiful palette.</p>
<p><img src="https://res.cloudinary.com/dpmchqezv/image/upload/c_scale,f_auto,q_auto:eco/v1646349103/blog/light-palette_d5nd2i.webp" alt="What my site looks like without any sort of color scheme emulation, a dark color scheme, and a light color scheme" /></p>
<p>These would be my guiding light (pun intended) in giving light mode users a de-<em>light</em>-ful experience.</p>
<h3 id="step-2%3A-color-contrast" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2020/04/27/using-media-queries-to-make-a-light-mode-website/#step-2%3A-color-contrast">Step 2: Color Contrast</a></h3>
<p>The next logical step is to make sure my colors are contrasting enough to be usable <em>and</em> accessible. So I jumped into my favorite <a href="https://contrast-grid.eightshapes.com/">color contrast grid generator</a> and checked it out.</p>
<p>I forgot to mention that my original color palette <em>wasn't</em> what is pictured above because I had to make some adjustments to make them properly accessible for me, but you get the point.</p>
<p>After figuring out what color combinations were acceptable, I got started on the CSS changes.</p>
<h3 id="step-3%3A-stylesheet-adjustments" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2020/04/27/using-media-queries-to-make-a-light-mode-website/#step-3%3A-stylesheet-adjustments">Step 3: Stylesheet Adjustments</a></h3>
<p>I'm a big fan of <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/--*">CSS custom properties</a>, even if I don't get as in-depth with them as many others do; the simplicity and ease-of-maintenance win me over. As such, they were hugely helpful in turning on the lights around here.</p>
<p>Let's recap on the <code>prefers-color-scheme</code> media query and come back to custom properties after.</p>
<h4 id="%40media-(prefers-color-scheme%3A-light)-%7B...%7D" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2020/04/27/using-media-queries-to-make-a-light-mode-website/#%40media-(prefers-color-scheme%3A-light)-%7B...%7D">@media (prefers-color-scheme: light) {...}</a></h4>
<p>The same way a media query is set for multiple devices, you check whether a user has a color scheme preference and change styles based on that.</p>
<h4 id="custom-properties" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2020/04/27/using-media-queries-to-make-a-light-mode-website/#custom-properties">Custom Properties</a></h4>
<p>Now that we all remember how to code a color scheme change, let's look at my usage of custom properties:</p>
<pre class="language-css"><code class="language-css"><span class="token atrule"><span class="token rule">@media</span> <span class="token punctuation">(</span><span class="token property">prefers-color-scheme</span><span class="token punctuation">:</span> light<span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
<span class="token selector">:root</span> <span class="token punctuation">{</span>
<span class="token property">--light</span><span class="token punctuation">:</span> #f0e9e9<span class="token punctuation">;</span>
<span class="token property">--black</span><span class="token punctuation">:</span> #02040f<span class="token punctuation">;</span>
<span class="token property">--bright</span><span class="token punctuation">:</span> #e59500<span class="token punctuation">;</span>
<span class="token property">--red</span><span class="token punctuation">:</span> #7d002d<span class="token punctuation">;</span>
<span class="token property">--cobalt</span><span class="token punctuation">:</span> #3d348b<span class="token punctuation">;</span>
<span class="token property">--blue</span><span class="token punctuation">:</span> #006992<span class="token punctuation">;</span>
<span class="token property">--headerBorder</span><span class="token punctuation">:</span> 2px solid <span class="token function">var</span><span class="token punctuation">(</span>--black<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">--headings</span><span class="token punctuation">:</span> <span class="token string">"Lora"</span><span class="token punctuation">,</span> sans-serif<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>Boom. Simple. Oh, I also changed the <code>font-family</code> in <code>--headings</code> because I liked the idea of having a more <em>elegant</em> look on light mode. Actually, I love it, so I've added font optimization to my new list of performance updates.</p>
<p>The <code>font-weight</code> for body copy also got one measure smaller - 300 - on default/dark versions for better readability. Weight is set back to normal - 400 - with light mode viewing.</p>
<p>Changing the colors was relatively simple. All I had to do was figure out which elements had colors attached to them and swap them as needed, like so:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">a</span> <span class="token punctuation">{</span>
<span class="token property">color</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--red<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">a:hover</span> <span class="token punctuation">{</span>
<span class="token property">color</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--cobalt<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.button</span> <span class="token punctuation">{</span>
<span class="token property">background-color</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--red<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">color</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--light<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.button:hover</span> <span class="token punctuation">{</span>
<span class="token property">background-color</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--cobalt<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token comment">/* etc... */</span></code></pre>
<p>Present me was very pleased with past me for using custom properties from the start and streamlining my rules early.</p>
<h4 id="but-wait%2C-what-about-larger-screens%3F" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2020/04/27/using-media-queries-to-make-a-light-mode-website/#but-wait%2C-what-about-larger-screens%3F">But Wait, What About Larger Screens?</a></h4>
<p>I wondered that too, but the answer is super simple.</p>
<pre class="language-css"><code class="language-css"><span class="token atrule"><span class="token rule">@media</span> <span class="token punctuation">(</span><span class="token property">prefers-color-scheme</span><span class="token punctuation">:</span> light<span class="token punctuation">)</span> <span class="token keyword">and</span> <span class="token punctuation">(</span><span class="token property">min-width</span><span class="token punctuation">:</span> 720px<span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
<span class="token comment">/* rules here */</span>
<span class="token punctuation">}</span></code></pre>
<h2 id="light-mode-is-here...now...try-it-out." tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2020/04/27/using-media-queries-to-make-a-light-mode-website/#light-mode-is-here...now...try-it-out.">Light Mode Is Here...Now...Try It Out.</a></h2>
<p>Chrome allows you to emulate a color scheme within their Dev Tools settings if you'd rather not change your system settings.</p>
<p>Turning it on reveals a lovely new version of the site that looks like this:</p>
<p><img src="https://res.cloudinary.com/dpmchqezv/image/upload/c_scale,f_auto,q_auto:eco/v1646349103/blog/light-mode_qayeyh.jpg" alt="WMy light mode website" /></p>
<h3 id="one-more-media-query" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2020/04/27/using-media-queries-to-make-a-light-mode-website/#one-more-media-query">One More Media Query</a></h3>
<p>I almost forgot to mention that I am catering to our <code>prefers-reduced-motion</code> folks with a separate rule turning off the animated header.</p>
<pre class="language-css"><code class="language-css"><span class="token atrule"><span class="token rule">@media</span> <span class="token punctuation">(</span>prefers-reduced-motion<span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
<span class="token selector">.type-animation__first,
.type-animation__second</span> <span class="token punctuation">{</span>
<span class="token property">animation</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<h2 id="recap" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2020/04/27/using-media-queries-to-make-a-light-mode-website/#recap">Recap</a></h2>
<ol>
<li>I was excited to create a light mode version of my website using the <code>prefers-color-scheme</code> media query.</li>
<li>I did some research about best practices for light mode designs in comparison to dark mode.</li>
<li>I got together the necessary assets (color palette, a11y notes).</li>
<li>I got to work switching up my colors within the media query in my main stylesheet.</li>
<li>Users can now view my site in two different experiences - light or dark - or, they might see one or the other by default depending on which browser they're using (sorry).</li>
</ol>
<p>Love it? Hate it? Let me know and @ me later. And no, my normal Saturday mornings <em>do not</em> consist of me looking up browser features.</p>
Creating The Front Royal Website, Part 22020-04-20T00:00:00Zhttps://www.troyv.dev/2020/04/20/creating-the-front-royal-website-part-2/
<!-- @format -->
<p>I <em>really</em> dropped the ball on this whole "blogging in real time" thing because the site is almost done.</p>
<p>In <a href="https://www.troyv.dev/2020/03/16/creating-the-front-royal-website-part-1/">the first blog</a>, I outlined my goals for the site with a screenshot of a few design ideas, and what stack I'll be using to build it.</p>
<p>Well, over that weekend, boredom got the best of me and <a href="https://frontroyalband.com/">I made the dang thing</a>. We haven't bought a domain yet, so ignore the randomly-selected words that Netlify provided.</p>
<h2 id="the-approach-pt.-2" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2020/04/20/creating-the-front-royal-website-part-2/#the-approach-pt.-2">The Approach Pt. 2</a></h2>
<p>To recap, I set out to make it a simple, single-page site with little bells and whistles. It was supposed to be:</p>
<ul>
<li>Scrollable;</li>
<li>Navigable;</li>
<li>Simple.</li>
</ul>
<p>And also cover:</p>
<ul>
<li>Ability to stream music;</li>
<li>Real-time tour dates;</li>
<li>Links to our social media and newsletter;</li>
<li>Reusable components to make updates and new additions easier for me to code.</li>
</ul>
<h2 id="my-stack" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2020/04/20/creating-the-front-royal-website-part-2/#my-stack">My Stack</a></h2>
<p>The repository is <a href="https://github.com/troyvassalotti/front-royal">fairly bare</a>; aside from the CSS and image assets, it's an index page, a Netlify config, and a custom 404 page.</p>
<p>The Lighthouse performance score is nothing to write home about since I'm using three separate third-party widgets (Spotify for album embedding, bandsintown for tour dates, and Mailchimp for our newsletter signup form), but the site works and it only requires a single page to download.</p>
<h2 id="the-design-pt.-2" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2020/04/20/creating-the-front-royal-website-part-2/#the-design-pt.-2">The Design Pt. 2</a></h2>
<p>Being a band, I figured listening to our music - our product - was most important. The very first section is our latest release with a Spotify embed for it, and links to our other albums.</p>
<p>I had to make some adjustments to Spotify's code to get the <code><iframe></code> to do what I wanted it to do, but I'm used to that by now.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.player</span> <span class="token punctuation">{</span>
<span class="token property">margin</span><span class="token punctuation">:</span> auto<span class="token punctuation">;</span>
<span class="token property">width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.player__featured</span> <span class="token punctuation">{</span>
<span class="token property">max-width</span><span class="token punctuation">:</span> 300px<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>iframe</span>
<span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>player player__featured<span class="token punctuation">"</span></span>
<span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://open.spotify.com/embed/album/17q2Qwv2jqrhVaX8iWX5wm<span class="token punctuation">"</span></span>
<span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>300<span class="token punctuation">"</span></span>
<span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>320<span class="token punctuation">"</span></span>
<span class="token attr-name">frameborder</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0<span class="token punctuation">"</span></span>
<span class="token attr-name">allowtransparency</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span>
<span class="token attr-name">allow</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>encrypted-media<span class="token punctuation">"</span></span>
<span class="token attr-name">title</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>These Things Happen Spotify player<span class="token punctuation">"</span></span>
<span class="token attr-name">loading</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>lazy<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>iframe</span><span class="token punctuation">></span></span></code></pre>
<p>I put the bandsintown tour dates widget right after the music, which again required a small CSS adjustment.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.bit-widget .bit-no-dates-container</span> <span class="token punctuation">{</span>
<span class="token property">padding</span><span class="token punctuation">:</span> 5rem 0 <span class="token important">!important</span><span class="token punctuation">;</span></code></pre>
<p>It's sad to look at since it's so empty (we have no dates coming up), but it'll look great when things are back in motion.</p>
<p>Finally, I listed links out to our various social media and embedded a signup form for our newsletter that I also am in charge of.</p>
<h2 id="feelings-overall" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2020/04/20/creating-the-front-royal-website-part-2/#feelings-overall">Feelings Overall</a></h2>
<p>Building websites is fun. I get chances to try new things I wouldn't normally do. In this case, I tried a new method for coding the main navigation on mobile and desktop.</p>
<p>There's a background image in the design, but the text got hard to read upon expanding the nav links on mobile. To get around that, I had to add some extra JavaScript to make sure the image remained the same and didn't expand to block the text.</p>
<pre class="language-js"><code class="language-js"><span class="token comment">/* these functions remove and toggle classes for properly styling the background image */</span>
<span class="token keyword">const</span> <span class="token function-variable function">openMenu</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
container<span class="token punctuation">.</span>classList<span class="token punctuation">.</span><span class="token function">remove</span><span class="token punctuation">(</span><span class="token string">"mobile-hide"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
nav<span class="token punctuation">.</span>classList<span class="token punctuation">.</span><span class="token function">remove</span><span class="token punctuation">(</span><span class="token string">"menu__closed"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
nav<span class="token punctuation">.</span>classList<span class="token punctuation">.</span><span class="token function">toggle</span><span class="token punctuation">(</span><span class="token string">"menu__open"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> <span class="token function-variable function">closeMenu</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
container<span class="token punctuation">.</span>classList<span class="token punctuation">.</span><span class="token function">toggle</span><span class="token punctuation">(</span><span class="token string">"mobile-hide"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
nav<span class="token punctuation">.</span>classList<span class="token punctuation">.</span><span class="token function">remove</span><span class="token punctuation">(</span><span class="token string">"menu__open"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
nav<span class="token punctuation">.</span>classList<span class="token punctuation">.</span><span class="token function">toggle</span><span class="token punctuation">(</span><span class="token string">"menu__closed"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token comment">/* this does the work of opening or closing the menu */</span>
<span class="token keyword">const</span> <span class="token function-variable function">animateMenu</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">isEven</span><span class="token punctuation">(</span>clickCount<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
menu<span class="token punctuation">.</span>innerHTML <span class="token operator">=</span> x<span class="token punctuation">;</span>
<span class="token function">openMenu</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">isOdd</span><span class="token punctuation">(</span>clickCount<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
menu<span class="token punctuation">.</span>innerHTML <span class="token operator">=</span> bars<span class="token punctuation">;</span>
<span class="token function">closeMenu</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
clickCount<span class="token operator">++</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token comment">/* look for that magic click */</span>
open<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">"click"</span><span class="token punctuation">,</span> animateMenu<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Making easily reusable sections got me thinking about how I could rework this very site you're reading right now. This past weekend, I did that too.</p>
<p>So, check out the Front Royal site, tell me where I could do better, and give us a listen. Maybe tell your friends about us too.</p>
Creating The Front Royal Website, Part 12020-03-16T00:00:00Zhttps://www.troyv.dev/2020/03/16/creating-the-front-royal-website-part-1/
<!-- @format -->
<p>There was a point in time bands either had a myspace, PureVolume, or both. Nowadays, it's not so simple.</p>
<p>People take up <em>so much</em> space on the internet and bands have to spread their coverage out even further.</p>
<p>It's like, all of a sudden, you had to know how to play an instrument and be a stellar <em>content creator</em> at the same time.</p>
<p>That means you're not going to make it much of anywhere in the music industry if your band doesn't have a Facebook, Twitter, Instagram, YouTube, Tinder, <strong>and</strong> Mastodon (don't @ me because I don't even know what you do on Mastodon).</p>
<p>While that's all well and good I guess, it's not centralized and at any moment the algorithms could change and your band falls off the face of the Earth; that's where having your own website becomes the one-stop-shop for everything Your Band.</p>
<p>No, I don't think social media will disappear and bands will be left with nothing, but I <em>do</em> think that building websites is fun and am not against having a home base for Front Royal.</p>
<h2 id="the-approach" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2020/03/16/creating-the-front-royal-website-part-1/#the-approach">The Approach</a></h2>
<p>This is the first time I'm designing a website from the ground up <em>(with the exception of image assets created by <a href="https://www.youtube.com/user/Flashgitzanimation">DJ Greger</a>)</em> that isn't the site you're looking at right now.</p>
<p>Looking at other approaches to band sites, it's pretty clear a single-page design is the way to go.</p>
<ul>
<li>It's scrollable;</li>
<li>It's navigable;</li>
<li>It does what it needs to do without all the bells and whistles.</li>
</ul>
<p>All a band's content lives elsewhere, so you don't need to host it all on your site...unless everything comes crashing down, but we'll all cross that bridge together later.</p>
<p>A single page constrains you because it needs to concisely have all the content you need while also being fast.</p>
<p>The latter can be hard to achieve when the page is loaded with widgets (media players, real-time tour dates), so everything else needs to be minimal (JavaScript, HTTP requests, even CSS).</p>
<p>I decided the these were the most important features to cover:</p>
<ul>
<li>Ability to stream music;</li>
<li>Real-time tour dates;</li>
<li>Links to our social media and newsletter;</li>
<li>Reusable components to make updates and new additions easier for me to code.</li>
</ul>
<h2 id="the-design" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2020/03/16/creating-the-front-royal-website-part-1/#the-design">The Design</a></h2>
<p>After an afternoon of fiddling around in Figma, I created these four visuals that show some promise for covering <a href="https://en.wikipedia.org/wiki/All_That">all that</a>:</p>
<p><img src="https://res.cloudinary.com/dpmchqezv/image/upload/c_scale,f_auto,q_auto:eco/v1646349103/blog/froro-des-phase1_fropbr.webp" alt="The first four visuals of the Front Royal website" /></p>
<p>I realized by Version 4 that I forgot to add our newsletter signup, so that's why it's missing from the other three...</p>
<h2 id="the-specifics" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2020/03/16/creating-the-front-royal-website-part-1/#the-specifics">The Specifics</a></h2>
<p>While I haven't done any actual coding yet, I plan on this being mostly flexbox with a little bit of grid.</p>
<p>The tour dates will be a widget from bandsintown and the media players will either come from Spotify or Bandcamp, but I'm not sure which to go with yet.</p>
<p>I want to avoid using Font Awesome to reduce unnecessary HTTP requests, so I'll opt for SVG instead.</p>
<p>I'll also self-optimize the images that accompany the media players so I can control the amount of data they bring with them.</p>
<p>Oh, and I'm hoping to avoid any website builders like Squarespace or WordPress because I want the practice and think it'll be more fun.</p>
<p>I know a builder would be easier and more accessible for most everyone else who takes it over, but we can cross that bridge as well when we get there.</p>
I Logged My Migraines For A Year (2019)2020-03-12T00:00:00Zhttps://www.troyv.dev/2020/03/12/i-logged-my-migraines-for-a-year-2019/
<!-- @format -->
<p>I've been having migraines for as long as I can remember, so it's not surprising to get a few here and there. But 2019 was not an average year; it was more like 3x my average.</p>
<p>Prior to 2019, I would've told you I averaged 1-2 migraines a month. Now, in 2020, my answer would be 4-6 times a month, and I only know this because I kept track of all 67 of them.</p>
<p>Years of being told by doctors and friends alike to keep a migraine log in an effort to control them have finally made sense.</p>
<h2 id="why-did-i-have-so-many-migraines%3F" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2020/03/12/i-logged-my-migraines-for-a-year-2019/#why-did-i-have-so-many-migraines%3F">Why Did I Have So Many Migraines?</a></h2>
<p>I'm still figuring that out. For years, I've had a pretty basic understanding of what my triggers are:</p>
<ul>
<li>Sudden changes in barometric pressure: honestly, I can't fully explain this one, but here's an article that <a href="https://blog.themigrainereliefcenter.com/barometric-pressure-and-migraines-what-you-need-to-know">discusses it in depth</a>.</li>
<li>Stress: I think this one is actually self-explanatory.</li>
<li>Bad sleep, or lack thereof: <a href="https://americanmigrainefoundation.org/resource-library/sleep/">healthy sleep and regular routines</a>, like going to bed and waking up at the same time each day, are positive habits.</li>
<li>Over-processed foods: it turns out a full year of soda and frozen, ready-to-eat corndogs in takes a toll on more than your gut.</li>
</ul>
<p>So while it hasn't been a mystery, a year of spreadsheet-tracking surely helped. Look at these <em>graphs!</em></p>
<p><img src="https://res.cloudinary.com/dpmchqezv/image/upload/c_scale,f_auto,q_auto:eco/v1646349103/blog/migraines-charts_tcbh5o.webp" alt="Two charts I created with my migraine data" /></p>
<h2 id="what-was-i-looking-to-find%3F" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2020/03/12/i-logged-my-migraines-for-a-year-2019/#what-was-i-looking-to-find%3F">What Was I Looking To Find?</a></h2>
<p>I have this <s>potentially unhealthy</s> truly great relationship with data and spreadsheets. Any and all data I thought could be useful I tracked, but I was most excited for these:</p>
<ol>
<li>What times of day am I most likely to get a migraine? (<em>it's a tie of 15 between 4:00 a.m. - 8:00 a.m. & 8:00 a.m. - noon</em>).</li>
<li>Did one day of the week have a higher migraine occurrence? (<em>Mondays & Saturdays tied at 12</em>).</li>
<li>Can I pinpoint a trigger or two and take my preventative care to the next level? (<em>Spoiler Alert: not quite</em>).</li>
</ol>
<p>I thought I'd have more graphs than I do, but I didn't want this project to consume me.</p>
<h2 id="what-did-i-find%3F" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2020/03/12/i-logged-my-migraines-for-a-year-2019/#what-did-i-find%3F">What Did I Find?</a></h2>
<p>My main goal was to create calendar heat map detailing each migraine day, color coded by the main trigger, and I'd say I succeeded.</p>
<p><img src="https://res.cloudinary.com/dpmchqezv/image/upload/c_scale,f_auto,q_auto:eco/v1646349103/blog/migraines-2019_yklspy.webp" alt="My migraine calendar heat map" /></p>
<p>I created the calendar with the following underlying data:</p>
<ul>
<li>Date</li>
<li>Time</li>
<li>Main Trigger</li>
<li>Secondary Trigger</li>
</ul>
<h2 id="any-difficulties%3F" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2020/03/12/i-logged-my-migraines-for-a-year-2019/#any-difficulties%3F">Any Difficulties?</a></h2>
<p><em>Other than the actual pain?</em> Yeah. The most difficult part of this project was assigning a trigger for myself. While sometimes I can say stress directly caused it in X cases, in Y cases it is not so clear.</p>
<p>I realized over the year that it's less "what was the cause?" and more "what <em>were</em> the <em>causes</em>?"</p>
<p>Getting poor sleep wasn't always enough to cause a migraine by itself, but drinking the night before might put me in a bad situation.</p>
<p>And I can't control the weather or tough times at work, so it's up to me to limit my exposure to other triggers as best I can. The more they stack up, the higher my chances are.</p>
<h2 id="now-what%3F" tabindex="-1"><a class="header-anchor" href="https://www.troyv.dev/2020/03/12/i-logged-my-migraines-for-a-year-2019/#now-what%3F">Now What?</a></h2>
<p>Knowing what I know now, I'm taking precautions while also cutting myself some slack. For example, I'm meditating more and seeing a neurologist again. I can't get rid of the migraines entirely and I accept that.</p>
<p>It's not worth it to get bent out of shape unless they get more extreme or happen way more often.</p>
<p>As of writing this, I've had seven and that's pretty nice, all things considered. If you're keeping math at home, that's a month's worth of migraines in three months time.</p>
<p>It's only uphill from here.</p>