<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ben Holmes</title>
    <description>The latest articles on DEV Community by Ben Holmes (@bholmesdev).</description>
    <link>https://dev.to/bholmesdev</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F142865%2Feae5c7d3-b7c3-44b1-ae9f-00e87ffc2d2c.jpg</url>
      <title>DEV Community: Ben Holmes</title>
      <link>https://dev.to/bholmesdev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bholmesdev"/>
    <language>en</language>
    <item>
      <title>Understanding single page apps &amp; client-side routing</title>
      <dc:creator>Ben Holmes</dc:creator>
      <pubDate>Wed, 07 Jul 2021 22:12:02 +0000</pubDate>
      <link>https://dev.to/bholmesdev/understanding-single-page-apps-client-side-routing-52gb</link>
      <guid>https://dev.to/bholmesdev/understanding-single-page-apps-client-side-routing-52gb</guid>
      <description>&lt;p&gt;This entry comes from my web wizardry newsletter, where I explore evergreen solutions to common web dev problems (no matter your favorite framework). If you like what you see, &lt;a href="http://tinyletter.com/bholmesdev" rel="noopener noreferrer"&gt;&lt;strong&gt;go sign up for free&lt;/strong&gt;&lt;/a&gt; 🪄&lt;/p&gt;




&lt;p&gt;"SPA" has been a hot topic ever since "modern" JS frameworks like React hit the scene. They promise all sorts of benefits like &lt;em&gt;dynamic user interactions,&lt;/em&gt; &lt;em&gt;lightning-fast load times,&lt;/em&gt; &lt;em&gt;solving world hunger,&lt;/em&gt; etc. (okay that last one is a stretch...)&lt;/p&gt;

&lt;p&gt;But have you ever stopped to wonder what's &lt;em&gt;actually&lt;/em&gt; going on behind the scenes? And if you're &lt;em&gt;not&lt;/em&gt; using an SPA, are you stuck in the past? Well, let's explore:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🤔 How non-SPAs work + how they perform on the modern web (spoiler, they're a great choice as always!)&lt;/li&gt;
&lt;li&gt;⚙️ The key benefits and inner workings of an SPA&lt;/li&gt;
&lt;li&gt;🍔 A side-by-side visual comparison feat. juicy burgers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Onwards!&lt;/p&gt;

&lt;h2&gt;
  
  
  🤔 First off, how do &lt;em&gt;non-&lt;/em&gt; SPAs work?
&lt;/h2&gt;

&lt;p&gt;Web development has been soaking in acronym soup lately, so I think it's worth clarifying what &lt;em&gt;isn't&lt;/em&gt; an SPA first 🙃&lt;/p&gt;

&lt;p&gt;If your site isn't an SPA, you're likely using what's called &lt;strong&gt;"server-based routing."&lt;/strong&gt; A metaphor is in order here. Say you're at a formal, sit-down restaurant with a waiter (a server if you will 😉). If you want food, you'll have to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Ask the waiter for the food you want&lt;/li&gt;
&lt;li&gt;Wait a moment for the dish to get prepared&lt;/li&gt;
&lt;li&gt;Receive your finished plate once it's done&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is how all websites work when you first visit them. You ask for what you want (visiting a URL like &lt;code&gt;https://breakfast.club&lt;/code&gt;), wait for the server to get back to you (loading spinner), then enjoy your "meal" once it's ready (the page is done loading! 🎉)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But what if you want to order dessert?&lt;/strong&gt; Heading back to our restaurant scenario, you'll go through the same flow as last time:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Ask the waiter for the yummiest dessert&lt;/li&gt;
&lt;li&gt;Hand them your dirty plates&lt;/li&gt;
&lt;li&gt;Wait a moment for the dish to get prepared&lt;/li&gt;
&lt;li&gt;Receive a shiny new bowl with your ice cream sundae 🍨&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There's an important bit I want to reiterate there: &lt;strong&gt;you hand over your dirty plates, and get back a brand new one.&lt;/strong&gt; This is why you'll see a little loading bar re-appear every time you jump to a new page. Here's an example of 11ty's documentation using server-based routing:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbholmes.dev%2Fassets%2Fblog%2Fspas-clientside-routing%2Fspas-11ty-docs-page-reload.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbholmes.dev%2Fassets%2Fblog%2Fspas-clientside-routing%2Fspas-11ty-docs-page-reload.gif" alt="Clicking through pages of 11ty's documentation site"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Watch the loading bar engage whenever we click a link&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;At first, it looks like you're still on the same page whenever you click those navigation links, and the browser's only refreshes the bits that change (the documentation). &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🚨 But that's not really the case!&lt;/strong&gt; Whenever you click a link, your browser is "clearing your plate" (removing &lt;em&gt;everything&lt;/em&gt; from the page) and pulling in a fresh meal, navigation and all. So you're really reloading all those pieces of navigation every time you visit a new page, even if they were &lt;em&gt;already&lt;/em&gt; on your plate to begin with.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wait, is that wasteful?
&lt;/h3&gt;

&lt;p&gt;It may sound like the browser's doing a lot of extra work here! Although this &lt;em&gt;is&lt;/em&gt; still an issue that SPAs can help with, it's not as problematic as it used to be. Here's a few efficiency points to consider:&lt;/p&gt;

&lt;h4&gt;
  
  
  1. HTML is super cheap to load and render
&lt;/h4&gt;

&lt;p&gt;Even though the browser is "re-painting" the page from scratch every time, it only takes a few milliseconds to load that HTML skeleton. The truly expensive bits are the styles, scripts, and images the browser needs to fetch, which leads us to...&lt;/p&gt;

&lt;h4&gt;
  
  
  2. Shared resources don't need to get re-loaded
&lt;/h4&gt;

&lt;p&gt;For instance, say every documentation page pulls in the same CSS file: &lt;code&gt;&amp;lt;link rel="stylesheet" href="documentation.css"&amp;gt;&lt;/code&gt;. When you click another link that &lt;em&gt;also&lt;/em&gt; pulls this CSS, the browser's smart enough to say "oh, I loaded this already! I'll just use that and apply it to the page." The same goes for images and fonts as well.&lt;/p&gt;

&lt;p&gt;💡 &lt;em&gt;&lt;strong&gt;Note:&lt;/strong&gt; This is all thanks to caching. If you want to go deeper, the Remix team has an excellent walkthrough on caching documents, resources, and more &lt;a href="https://www.youtube.com/watch?v=3XkU_DXcgl0" rel="noopener noreferrer"&gt;over here&lt;/a&gt;&lt;/em&gt; 😄&lt;/p&gt;

&lt;p&gt;So the actual &lt;em&gt;loading&lt;/em&gt; of these shared resources isn't a problem. But what about &lt;em&gt;painting&lt;/em&gt; those resources to the page over and over again? This brings us to...&lt;/p&gt;

&lt;h4&gt;
  
  
  3. The next page only appears when it's ready
&lt;/h4&gt;

&lt;p&gt;Back in the day, there was a risk you'd see a flash of white nothingness between "clearing the plate" and "receiving the new one." But &lt;a href="https://developers.google.com/web/updates/2019/05/paint-holding" rel="noopener noreferrer"&gt;modern browsers have pretty much resolved this&lt;/a&gt;! In short, web browsers wait for the signal that the next page isn't just "done loading," but is also ready for you to view and interact with. Think of this like bringing out your plate when it's ready to eat, instead of bringing you a plate of ingredients you have to assemble yourself.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbholmes.dev%2Fassets%2Fblog%2Fspas-clientside-routing%2Fspa-hibachi-rendering.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbholmes.dev%2Fassets%2Fblog%2Fspas-clientside-routing%2Fspa-hibachi-rendering.gif" alt="Chef tossing knives around a hibachi grill"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Hibachi rendering:&lt;/strong&gt; showing you the page painting process before it's ready to eat (it's less cool when browsers do it)&lt;/em&gt; 😉&lt;/p&gt;

&lt;p&gt;This especially helps out pages that rely on blocking resources like JavaScript to render everything on the page. Here's a quick before-and-after shot by the Chrome team when they launched this concept 🚀&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbholmes.dev%2Fassets%2Fblog%2Fspas-clientside-routing%2Fspa-flash-of-white.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbholmes.dev%2Fassets%2Fblog%2Fspas-clientside-routing%2Fspa-flash-of-white.gif" alt="Comparison of the flash of white from loading a JS-intensive page before and after the Google Chrome update"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://developers.google.com/web/updates/2019/05/paint-holding" rel="noopener noreferrer"&gt;Source article&lt;/a&gt; by Addy Osmani&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Disclaimer:&lt;/strong&gt; The browser won't &lt;strong&gt;always&lt;/strong&gt; wait for the next page to be interactive before showing it; It'll still show the half-finished page if you're on a slow internet connection and the browser decides it's waited too long. Still, for snappier sites like that &lt;a href="https://www.11ty.dev/docs/" rel="noopener noreferrer"&gt;11ty documentation&lt;/a&gt; from earlier, it shouldn't be a problem!&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  💁 Now, let's talk single page apps
&lt;/h2&gt;

&lt;p&gt;So how do SPAs compare? Well, let's revisit that restaurant example from before. When you first visit a site using the SPA approach, everything works pretty much the same:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Ask the server for the food you want (visit &lt;code&gt;https://spa-breakfast.club&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Wait a moment for the dish to get prepared (browser loading spinner)&lt;/li&gt;
&lt;li&gt;Receive your finished plate once it's done (the page is done loading! 🎉)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now the interesting part comes when you &lt;em&gt;go back for seconds.&lt;/em&gt; When you click a link on an SPA, it replaces the typical, server-based routing with &lt;strong&gt;client-side routing&lt;/strong&gt; 😮 In other words, we process all link requests using JavaScript we wrote ourselves, instead of sending those requests to the server right away.&lt;/p&gt;

&lt;p&gt;Here's a code snippet to get your brain moving 🧠&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/desert"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Go eat desert&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="c1"&gt;// if you clicked on an A-nchor tag (link)&lt;/span&gt;
        &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tagName&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;A&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
        &lt;span class="c1"&gt;// and you're going to a page on this domain (like /desert)&lt;/span&gt;
        &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;origin&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;origin&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// don't ask the server for that resource!&lt;/span&gt;
        &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="c1"&gt;// instead, we'll go fetch the resource ourselves&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://buffet.table/desert&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;// ...convert that response to something we can work with&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;htmlString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;desert&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DOMParser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parseFromString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;htmlString&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text/html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;// ...and do something with that desert element&lt;/span&gt;
        &lt;span class="c1"&gt;// ex. append desert to our "plate" in the DOM&lt;/span&gt;
        &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.my-plate&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;desert&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pretty weird, right? This is the super-simplified crux of an SPA: &lt;strong&gt;you never truly "leave" the page you started on.&lt;/strong&gt; Instead, you intercept all future requests (link clicks) and handle the fetching yourself. You'll often do this using &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API" rel="noopener noreferrer"&gt;the &lt;code&gt;fetch&lt;/code&gt; API&lt;/a&gt; native to all modern browsers as demo-d above.&lt;/p&gt;

&lt;p&gt;This is why I'd think of SPAs and clientside routing like a &lt;strong&gt;buffet&lt;/strong&gt;. Instead of ordering your meal and waiting for it to get prepared, you can get out of your seat and grab that pre-prepared food yourself! &lt;/p&gt;

&lt;h3&gt;
  
  
  Key benefits to this approach
&lt;/h3&gt;

&lt;p&gt;There are 2 major benefits to single page apps that clientside routing unlocks 🔓&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First, load-time efficiency &lt;em&gt;can&lt;/em&gt; go up 🚀&lt;/strong&gt; I say "can" because of all the serverside routing optimization I mentioned previously (which may cancel out any performance gains). But there &lt;em&gt;is&lt;/em&gt; a noticeable difference for resource-hungry frameworks like React, Vue, and Svelte. All of these use some form of clientside routing to keep JavaScript load times to a minimum.&lt;/p&gt;

&lt;p&gt;For instance, if I went from one page of a React app to another using serverside routing, the browser would &lt;em&gt;re-parse and re-render the whole page&lt;/em&gt; using JavaScript! This can severely impact your "time to interactive," an accessibility concern you can learn more about &lt;a href="https://web.dev/interactive/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Second, you can now build dynamic, app-like interactions ⚙️&lt;/strong&gt; Animated page transitions are the easiest to point out here. Since you're fully in control of loading new content and applying it to the page, you can pull off all sorts of CSS trickery like cross-fades, pop-up modals, tab sliders, and more. Here's an example from &lt;a href="https://bholmes.dev" rel="noopener noreferrer"&gt;my personal site&lt;/a&gt; using clientside routing:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbholmes.dev%2Fassets%2Fblog%2Fspas-clientside-routing%2Fspa-bholmesdev-page-transitions.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbholmes.dev%2Fassets%2Fblog%2Fspas-clientside-routing%2Fspa-bholmesdev-page-transitions.gif" alt="Preview of page transitions clicking links on bholmes.dev"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🍔 A juicy visual comparison
&lt;/h2&gt;

&lt;p&gt;Now that we've introduced SPAs + clientside routing as a concept, lets see a side-by-side comparison with serverside routing.&lt;/p&gt;

&lt;p&gt;Say you receive your order for a juicy burger that's &lt;a href="https://medium.com/@samsonspaddockau/whats-the-difference-between-rare-medium-and-well-done-steak-fe463e0f960f" rel="noopener noreferrer"&gt;cooked medium rare&lt;/a&gt;, but would rather have a patty that's well done. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We'll imagine this moment as "clicking a link,"&lt;/strong&gt; requesting to go from &lt;code&gt;/medium-rare-burger&lt;/code&gt; to &lt;code&gt;/well-done-burger&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here's what that "click" may do with a server-based approach:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbholmes.dev%2Fassets%2Fblog%2Fspas-clientside-routing%2Fspa-ssr-demo.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbholmes.dev%2Fassets%2Fblog%2Fspas-clientside-routing%2Fspa-ssr-demo.gif" alt="Step-by-step serverside routing process: 1. Medium rare hamburger is returned, 2. The HTML document is emptied out while the new one loads, 3. The well done hamburger is applied to our page"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Animation showing the 3 step rendering process&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Then, here's how clientside routing may handle the request:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbholmes.dev%2Fassets%2Fblog%2Fspas-clientside-routing%2Fspa-csr-demo.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbholmes.dev%2Fassets%2Fblog%2Fspas-clientside-routing%2Fspa-csr-demo.gif" alt="Step-by-step clientside routing process: 1. Medium rare hamburger is returned, 2. We request a well done burger using the fetch API, 3. We massage the response, 4. We pluck out the "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Animation showing our new 4 step rendering process&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Notice that we never clear our page in the SPA approach! We just request the resource (a well-done burger), pick out the pieces we want to add to our page (the patty), and perform the DOM manipulation with JavaScript.&lt;/p&gt;

&lt;p&gt;This doesn't have much performance benefits when we're talking HTML files. But if that HTML has some JavaScript and styles attached to it that we can &lt;em&gt;also&lt;/em&gt; fetch (like, say, a React component), there's a lot of room for performance gains.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up: so which should I choose?
&lt;/h2&gt;

&lt;p&gt;Although SPAs seem like the "silver bullet" for any website, there's a case to be made for either approach.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The most obvious win for server-side routing is, &lt;strong&gt;well, it's just simpler.&lt;/strong&gt; No need to write and maintain all those click listeners; just let the browser do the serving for you. Yes, you'll often use a framework-specific library for SPAs like &lt;a href="https://reactrouter.com" rel="noopener noreferrer"&gt;React Router&lt;/a&gt;, but learning and updating JS libraries is always more overhead.&lt;/li&gt;
&lt;li&gt;The second win for server-side is &lt;strong&gt;no accessibility worries.&lt;/strong&gt; When you handle all the routing clientside, you run the risk of hurting screenreader and keyboard experiences. For instance, you'll need to alert screenreader users that new content has appeared on the page whenever they click a link. And for keyboard-ists, you need to make sure their &lt;a href="https://bholmes.dev/blog/accessible-show-hide-animations/" rel="noopener noreferrer"&gt;focusing the right element&lt;/a&gt; when something new swipes into view.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;☝️ But if you're addressing these concerns confidently (or using a robust library to do it for you), SPAs are a great way to go!&lt;/p&gt;

&lt;h2&gt;
  
  
  Learn a little something?
&lt;/h2&gt;

&lt;p&gt;Glad to hear it! If you want more universal solutions like this, you can &lt;a href="https://tinyletter.com/bholmesdev" rel="noopener noreferrer"&gt;sign up for the web wizardry newsletter&lt;/a&gt; for some bi-weekly web sorcery 🔮&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>beginners</category>
      <category>react</category>
    </item>
    <item>
      <title>Grabbing subsets of JS object properties with... GraphQL?</title>
      <dc:creator>Ben Holmes</dc:creator>
      <pubDate>Tue, 08 Jun 2021 13:38:41 +0000</pubDate>
      <link>https://dev.to/bholmesdev/grabbing-subsets-of-js-object-properties-with-graphql-2ain</link>
      <guid>https://dev.to/bholmesdev/grabbing-subsets-of-js-object-properties-with-graphql-2ain</guid>
      <description>&lt;p&gt;This entry comes from my web wizardry newsletter, where I explore evergreen solutions to common web dev problems (no matter your favorite framework). If you like what you see, &lt;a href="http://tinyletter.com/bholmesdev" rel="noopener noreferrer"&gt;&lt;strong&gt;go sign up for free&lt;/strong&gt;&lt;/a&gt; 🪄&lt;/p&gt;




&lt;p&gt;Messing with JavaScript objects is pretty easy these days. Destructuring syntax is convenient and the spread &lt;code&gt;...&lt;/code&gt; operator helps with merging objects together. But what about grabbing just... a portion of an object?&lt;/p&gt;

&lt;p&gt;This question deserves some visuals. Let's jump into the problem we're trying to solve, and a flexible solution we can add to any existing JavaScript project 💪&lt;/p&gt;

&lt;h2&gt;
  
  
  What we want
&lt;/h2&gt;

&lt;p&gt;Say I have a big document of structured data. Without manually writing a new object by hand, I just want to pull the little slices that I actually care about.&lt;/p&gt;

&lt;p&gt;Here's one such scenario:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbholmes.dev%2Fassets%2Fblog%2Fquery-json-objects-graphql%2Fproblem.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbholmes.dev%2Fassets%2Fblog%2Fquery-json-objects-graphql%2Fproblem.png" alt="A side-by-side comparison of 2 JavaScript objects. Under "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this case, we want a copy of our original fridge, but we only care about those &lt;code&gt;isEdible&lt;/code&gt; subkeys.&lt;/p&gt;

&lt;p&gt;My gut reaction is to reach for some declarative tools in my &lt;a href="http://es6-features.org/#Constants" rel="noopener noreferrer"&gt;ES6&lt;/a&gt; arsenal. &lt;strong&gt;Object destructuring&lt;/strong&gt; comes to mind at first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;whatsInMyFridge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;weekOldPasta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;weekOldPasta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;isEdible&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pastaIsEdible&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;panSearedSalmon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;isEdible&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;panSearedSalmonIsEdible&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;whatsInMyFridge&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's a few issues with this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We can't easily destructure keys of the same name. Notice I had to convert each &lt;code&gt;isEdible&lt;/code&gt; variable to the verbose &lt;code&gt;pastaIsEdible&lt;/code&gt; and &lt;code&gt;panSearedSalmonIsEdible&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Destructuring leads to some pretty gnarly code as it gets more complex. Just with a few keys, we're already hitting multi-line { curly hell }.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And most of all, &lt;strong&gt;we still have to build our new object at the end!&lt;/strong&gt; Our destructuring statement &lt;em&gt;actually&lt;/em&gt; just made some one-off variables for each key in the object. We'll still have to do this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;whatsEdible&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;weekOldPasta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;isEdible&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pastaIsEdible&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;panSearedSalmon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;isEdible&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;panSearedSalmonIsEdible&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...which is hardly better than just writing the object from scratch 😢&lt;/p&gt;

&lt;p&gt;What we really want is some magical syntax for &lt;em&gt;just the keys&lt;/em&gt; we want to retrieve. Something like this really:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;whatsInMyFridge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;giveMeTheseKeys&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;weekOldPasta&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;isEdible&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nx"&gt;panSearedSalmon&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;isEdible&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="c1"&gt;// -&amp;gt; a beautiful formatted JS object&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  📈 Enter: GraphQL
&lt;/h2&gt;

&lt;p&gt;If you've worked with GraphQL before, you probably noticed how close that example comes to a GraphQL query!&lt;/p&gt;

&lt;p&gt;A brief rundown for those unfamiliar: GraphQL is a "querying" language originally built for API calls. It was mainly born from the frustrations with REST requests, as API endpoints had to &lt;em&gt;predict&lt;/em&gt; all the data a client might want to retrieve. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.github.com/en/graphql/guides/migrating-from-rest-to-graphql" rel="noopener noreferrer"&gt;GitHub recently migrated to GraphQL&lt;/a&gt; because of this. Imagine this scenario:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User A wants to get information about their GitHub profile. They want to send off a username and get back the accounts &lt;strong&gt;name&lt;/strong&gt; and &lt;strong&gt;profile picture&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;User B also wants some GitHub profile info. However, they're looking for a different set of info: the list of &lt;strong&gt;user recovery emails&lt;/strong&gt; and their &lt;strong&gt;personal bio&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As you could imagine, User C might want a new combination of fields, as might users D-Z. So instead of returning a massive JSON payload to satisfy everyone, GitHub exposed a GraphQL API for you to describe &lt;em&gt;exactly&lt;/em&gt; which fields you want.&lt;/p&gt;

&lt;p&gt;Here's how User A might request a name and profile picture as part of their request body&lt;br&gt;
&lt;em&gt;This is from demo purposes, and won't actually work if you send to GitHub&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    userProfile(email: '10xengineer@genius.club') {
        name
        picture {
            src
            alt
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...And GitHub will "fill in the blanks" by providing values to those requested keys. As you can imagine, this syntax is flexible enough to use on &lt;em&gt;any&lt;/em&gt; blob of JSON you want to filter down 👀&lt;/p&gt;

&lt;h2&gt;
  
  
  📖 Applying GraphQL to reading JSON objects
&lt;/h2&gt;

&lt;p&gt;💡 &lt;strong&gt;TLDR:&lt;/strong&gt; If you want the final solution without all the walkthrough, jump down to the finished product!&lt;/p&gt;

&lt;p&gt;Let's figure out how to use that fancy syntax for our use case. The biggest question to resolve is "how do we interpret a GraphQL query in JS land?" Sadly, there isn't a nice "plain JS" solution, so we &lt;em&gt;will&lt;/em&gt; be reaching for a library here.&lt;/p&gt;

&lt;p&gt;Go ahead and install this &lt;a href="https://www.npmjs.com/package/graphql-query-to-json" rel="noopener noreferrer"&gt;graphql-query-to-json package&lt;/a&gt;. It &lt;em&gt;does&lt;/em&gt; have a fair amount of sub-dependencies like the core &lt;a href="https://www.npmjs.com/package/graphql" rel="noopener noreferrer"&gt;graphql package&lt;/a&gt; and the complimentary &lt;a href="https://www.npmjs.com/package/json-to-graphql-query" rel="noopener noreferrer"&gt;json-to-graphql-query&lt;/a&gt;, so if that bothers you... my apologies 😢&lt;/p&gt;

&lt;p&gt;Let's see what we get from our old "what's edible in my fridge" request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;graphQlQueryToJson&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;graphql-query-to-json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;// or if you prefer: import { graphQlQueryToJson } from 'graphql-query-to-json'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;asJson&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;graphQlQueryToJson&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`{
  weekOldPasta {
    isEdible
  }
  panSearedSalmon {
    isEdible
  }
}`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;asJson&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="cm"&gt;/* 👇
{
  query: {
    weekOldPasta: { isEdible: true },
    panSearedSalmon: { isEdible: true }
  }
}
*/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Neat! Toss in a string, get back a JS object. You'll notice that it wraps our requested object with the &lt;code&gt;query&lt;/code&gt; key. This &lt;em&gt;would&lt;/em&gt; be useful if we were sending this request to an API, but for our purposes, we'll just ignore that key in our helper function. It also stubs out any unknown key values with &lt;code&gt;true&lt;/code&gt;, which we'll use to track down unfilled values later 👀&lt;/p&gt;

&lt;h3&gt;
  
  
  Traversing our query
&lt;/h3&gt;

&lt;p&gt;With this JS object in hand, it's time to walk through all the keys and figure out which values to fill in. Let's start with a simple example that only goes 1 level of keys deep:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myFridge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;numEggs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;pintsOfIceCream&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;degreeUnits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;celsius&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;whatIWant&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`{
    numEggs
    degreeUnits
}`&lt;/span&gt;
&lt;span class="c1"&gt;// grab the ".query" straight away, since we won't need that nested key&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;whatIWantAsJson&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;graphQlQueryToJson&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;whatIWant&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;
&lt;span class="c1"&gt;// 👉 { numEggs: true, degreeUnits: true }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we have our set of keys (&lt;code&gt;numEggs&lt;/code&gt; and &lt;code&gt;degreeUnits&lt;/code&gt;) each with a value of &lt;code&gt;true&lt;/code&gt;. To assign our actual values in place of those &lt;code&gt;true&lt;/code&gt; flags, we can&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;loop through all the object keys in &lt;code&gt;whatIWantAsJson&lt;/code&gt;, and&lt;/li&gt;
&lt;li&gt;assign values from the same key in &lt;code&gt;myFridge&lt;/code&gt;.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// loop over the object keys...&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;whatIWantAsJson&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// and if that key has a value of "true"...&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;whatIWantAsJson&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// replace "true" with the value from myFridge&lt;/span&gt;
        &lt;span class="nx"&gt;whatIWantAsJson&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;myFridge&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;whatIWantAsJson&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;// 👉 { numEggs: 5, degreeUnits: 'celsius' }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Handling nested objects
&lt;/h3&gt;

&lt;p&gt;This basic loop handles 1 level of nesting. But what if we have a request like this?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// level 1&lt;/span&gt;
  &lt;span class="nx"&gt;weekOldPasta&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// level 2&lt;/span&gt;
    &lt;span class="nx"&gt;isEdible&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For this, we'll need a way to run our loop over &lt;code&gt;Object.keys&lt;/code&gt; for &lt;em&gt;every level&lt;/em&gt; of keys in our object. Get ready to put on your computer science hat, because we're using &lt;strong&gt;recursion&lt;/strong&gt; 😨&lt;/p&gt;

&lt;p&gt;Pay attention to this new &lt;code&gt;else&lt;/code&gt; statement we're adding:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// wrap our loop in a function we can call&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;assignValuesToObjectKeys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;whatIWant&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;myFridge&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;whatIWant&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;whatIWant&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;whatIWant&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;myFridge&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// if the value isn't "true", we must have found a nested object&lt;/span&gt;
            &lt;span class="c1"&gt;// so, we'll call this same function again, now starting from&lt;/span&gt;
            &lt;span class="c1"&gt;// the nested object inside whatIWant&lt;/span&gt;
            &lt;span class="nf"&gt;assignValuesToObjectKeys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;whatIWant&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;myFridge&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a classic example of a &lt;a href="https://www.freecodecamp.org/news/learn-recursion-in-javascript-by-example/" rel="noopener noreferrer"&gt;recursive function&lt;/a&gt;. We have 2 clauses here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The base case:&lt;/strong&gt; When we hit a value of &lt;code&gt;true&lt;/code&gt;, we stop looking for nested objects&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The recursive function call:&lt;/strong&gt; When we &lt;em&gt;haven't&lt;/em&gt; hit the "base" of our nested object, keep drilling down the chain of nested keys using the same function&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With this in place, we have a reusable JS function for anywhere in our codebase 🥳&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myFridge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
    &lt;span class="na"&gt;weekOldPasta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
        &lt;span class="na"&gt;noodleSogginess&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;“&lt;/span&gt;&lt;span class="nx"&gt;high&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
        &lt;span class="na"&gt;numMeatballs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
        &lt;span class="na"&gt;isEdible&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
    &lt;span class="p"&gt;},&lt;/span&gt;  
    &lt;span class="na"&gt;panSearedSalmon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
        &lt;span class="na"&gt;oilUsed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;“&lt;/span&gt;&lt;span class="nx"&gt;avocado&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
        &lt;span class="na"&gt;numSpices&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
        &lt;span class="na"&gt;isEdible&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;whatIWant&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;graphQlQueryToJson&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`{
  weekOldPasta {
    isEdible
  }
  panSearedSalmon {
    isEdible
  }
}`&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;

&lt;span class="nf"&gt;assignValuesToObjectKeys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;whatIWant&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;myFridge&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;whatIWant&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="cm"&gt;/* 👉 {
    weekOldPasta: {
        isEdible: false,
    },
    panSearedSalmon: {
        isEdible: true,
    },
}
*/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Cleaning this up a little
&lt;/h3&gt;

&lt;p&gt;You'll notice that our &lt;code&gt;assignValuesToObjectKeys&lt;/code&gt; function doesn't return anything; it just modifies the &lt;code&gt;whatIWant&lt;/code&gt; object we passed in. For added readability, we might add a wrapper function to handle the &lt;code&gt;graphQlQueryToJson&lt;/code&gt; call and actually &lt;code&gt;return&lt;/code&gt; our requested object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;grabPartialObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;originalObject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;whatIWant&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;graphQlQueryToJson&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;
    &lt;span class="nf"&gt;assignValuesToObjectKeys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;whatIWant&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;originalObject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;whatIWant&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;whatsEdible&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;grabPartialObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myFridge&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`{
  weekOldPasta {
    isEdible
  }
  panSearedSalmon {
    isEdible
  }
}`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;whatsEdible&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// gives us the same object as before!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Handling arrays
&lt;/h3&gt;

&lt;p&gt;So we've conquered nested objects. But what if we have an &lt;em&gt;array&lt;/em&gt; of objects that we want to filter?&lt;/p&gt;

&lt;p&gt;For instance, say our fridge data was structured just a bit differently:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myFridge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;food&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Week old pasta&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;noodleSogginess&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;high&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;numMeatballs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;isEdible&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Pan Seared Salmon&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;oilUsed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;avocado&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;numSpices&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;isEdible&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...and we just care about the &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;isEdible&lt;/code&gt; keys for every object in that array. Following how GraphQL requests normally work, we'd expect this sort of syntax to work:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;food&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;isEdible&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In other words, treat &lt;code&gt;food&lt;/code&gt; like it's a regular object in the request, and we'll be smart enough to handle arrays of data.&lt;/p&gt;

&lt;p&gt;This answer is a bit more involved than our previous examples. So, I'll leave you with a thoroughly commented codeblock:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;assignValuesToObjectKeys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;whatIWant&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;myFridge&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;whatIWant&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;whatIWant&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="p"&gt;...&lt;/span&gt;
      &lt;span class="c1"&gt;// 👇 If the fridge data happens to be an array...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myFridge&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// first, keep track of the object they requested&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;originalRequest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;whatIWant&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="c1"&gt;// then, create an array where that request used to be&lt;/span&gt;
      &lt;span class="c1"&gt;// for us to "push" new elements onto&lt;/span&gt;
      &lt;span class="nx"&gt;whatIWant&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
      &lt;span class="c1"&gt;// loop over the items in our array of food...&lt;/span&gt;
      &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fridgeItem&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;myFridge&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// make a variable to store the result of assignValuesToObjectKeys&lt;/span&gt;
        &lt;span class="c1"&gt;// we use { ...originalRequest } here to create a "copy"&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;requestedItem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;originalRequest&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="c1"&gt;// grab the keys we want out of that array element&lt;/span&gt;
        &lt;span class="nf"&gt;assignValuesToObjectKeys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;requestedItem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fridgeItem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;// then, push our shiny new object onto our running list&lt;/span&gt;
        &lt;span class="nx"&gt;whatIWant&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;requestedItem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's a fair amount of code! To briefly summarize, you'll need to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Check when our actual data is an array, rather than a simple object&lt;/li&gt;
&lt;li&gt;Loop over the actual data and &lt;code&gt;assignValuesToObjectKeys&lt;/code&gt; for each one&lt;/li&gt;
&lt;li&gt;Push the results onto a running array in &lt;code&gt;whatIWant&lt;/code&gt;, with necessary helper variables to keep track of your original request&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  🚀 The finished product
&lt;/h2&gt;

&lt;p&gt;Here's how our finished product looks! I've renamed &lt;code&gt;myFridge&lt;/code&gt; 👉 &lt;code&gt;actualObj&lt;/code&gt; and &lt;code&gt;whatIWant&lt;/code&gt; 👉 &lt;code&gt;requestedObj&lt;/code&gt; so our naming conventions are more universal. I also added a &lt;code&gt;hasOwnProperty&lt;/code&gt; check to assert we're requesting a key that actually exists. If not, raise an exception.&lt;/p&gt;

&lt;p&gt;Remember, you'll need to add the &lt;a href="https://www.npmjs.com/package/graphql-query-to-json" rel="noopener noreferrer"&gt;graphql-query-to-json package&lt;/a&gt; package to your project for this to work.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;graphQlQueryToJson&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;graphql-query-to-json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;assignValuesToObjectKeys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;requestedObj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;actualObj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;requestedObj&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;actualObj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hasOwnProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="s2"&gt;`You requested a key that doesn't exist: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;requestedObj&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;requestedObj&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;actualObj&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;actualObj&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// keep track of the object they requested&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;originalRequest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;requestedObj&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="c1"&gt;// then, create an array where that request used to be&lt;/span&gt;
      &lt;span class="c1"&gt;// for us to "push" new elements onto&lt;/span&gt;
      &lt;span class="nx"&gt;requestedObj&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
      &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;actualItem&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;actualObj&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// make a variable to store the result of assignValuesToObjectKeys&lt;/span&gt;
        &lt;span class="c1"&gt;// we use { ...originalRequest } here to create a "copy"&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;requestedItem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;originalRequest&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nf"&gt;assignValuesToObjectKeys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;requestedItem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;actualItem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nx"&gt;requestedObj&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;requestedItem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;requestedObj&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
      &lt;span class="c1"&gt;// if the value isn't "true", we must have found a nested object&lt;/span&gt;
      &lt;span class="c1"&gt;// so, we'll call this same function again, now starting from&lt;/span&gt;
      &lt;span class="c1"&gt;// the nested object inside requestedObj&lt;/span&gt;
      &lt;span class="nf"&gt;assignValuesToObjectKeys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;requestedObj&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;actualObj&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// 👇 Function you'll actually `export` for others to use&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;grabPartialObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;actualObj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;requestedObj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;graphQlQueryToJson&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;
  &lt;span class="nf"&gt;assignValuesToObjectKeys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;requestedObj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;actualObj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;requestedObj&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Usage example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;grabPartialObject&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./some/helper/file&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myFridge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
    &lt;span class="na"&gt;weekOldPasta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
        &lt;span class="na"&gt;noodleSogginess&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;“&lt;/span&gt;&lt;span class="nx"&gt;high&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
        &lt;span class="na"&gt;numMeatballs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
        &lt;span class="na"&gt;isEdible&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
    &lt;span class="p"&gt;},&lt;/span&gt;  
    &lt;span class="na"&gt;panSearedSalmon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
        &lt;span class="na"&gt;oilUsed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;“&lt;/span&gt;&lt;span class="nx"&gt;avocado&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
        &lt;span class="na"&gt;numSpices&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
        &lt;span class="na"&gt;isEdible&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;whatsEdible&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;grabPartialObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myFridge&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`{
  weekOldPasta {
    isEdible
  }
  panSearedSalmon {
    isEdible
  }
}`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;whatsEdible&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="cm"&gt;/* 👉 {
    weekOldPasta: {
        isEdible: false,
    },
    panSearedSalmon: {
        isEdible: true,
    },
}
*/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Learn a little something?
&lt;/h2&gt;

&lt;p&gt;Glad to hear it! If you want more universal solutions like this, you can &lt;a href="https://tinyletter.com/bholmesdev" rel="noopener noreferrer"&gt;sign up for the web wizardry newsletter&lt;/a&gt; for some bi-weekly web sorcery 🔮&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>graphql</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Sexy, accessible show-hide animations in any web framework</title>
      <dc:creator>Ben Holmes</dc:creator>
      <pubDate>Mon, 24 May 2021 23:18:22 +0000</pubDate>
      <link>https://dev.to/bholmesdev/sexy-accessible-show-hide-animations-in-any-web-framework-4hpk</link>
      <guid>https://dev.to/bholmesdev/sexy-accessible-show-hide-animations-in-any-web-framework-4hpk</guid>
      <description>&lt;p&gt;This entry comes from my web wizardry newsletter, where I explore evergreen solutions to common web dev problems (no matter your favorite framework). If you like what you see, &lt;a href="http://tinyletter.com/bholmesdev" rel="noopener noreferrer"&gt;&lt;strong&gt;go sign up for free&lt;/strong&gt;&lt;/a&gt; 🪄&lt;/p&gt;




&lt;p&gt;Using JS to show and hide something is a web dev rite of passage. Heck, it's the first demo that comes to mind when you think about "state management" (just behind building a counter 😆).&lt;/p&gt;

&lt;p&gt;But when you do the classic "hide whenever X variable is &lt;code&gt;false&lt;/code&gt;," there's some accessibility concerns you might be forgetting:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;can keyboard users still access that hidden element?&lt;/li&gt;
&lt;li&gt;do screen readers actually &lt;em&gt;know&lt;/em&gt; what your button is trying to show and hide?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Don't worry, I have the answers for you! Let's explore a universal solution you can bring to React, Svelte, Vue, etc, along with some debugging tips using a real screen reader 🚀&lt;/p&gt;

&lt;h2&gt;
  
  
  Starting with a naive solution
&lt;/h2&gt;

&lt;p&gt;Okay, wait, why isn't this post a 2 minute read? I just whipped up this fade transition in a CodePen and it works great!&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/bholmesdev/embed/XWMKqWj?height=600&amp;amp;default-tab=css,result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you don't recognize this meme, &lt;a href="https://knowyourmeme.com/memes/nileseyy-niles-disappears" rel="noopener noreferrer"&gt;go culture yourself&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I'll admit that a simple &lt;code&gt;opacity&lt;/code&gt; shift works pretty well for non-interactable elements like this &lt;code&gt;img&lt;/code&gt;. But what if you're &lt;strong&gt;showing and hiding clickable elements,&lt;/strong&gt; like a navigation bar of links?&lt;/p&gt;

&lt;p&gt;This can spell problems for those using keyboard navigation or screenreaders. It's (embarrassingly) a problem across some of my own sites that I'm working to fix, but it can be tricky to debug.&lt;/p&gt;

&lt;p&gt;Here's an example of hitting the &lt;code&gt;tab&lt;/code&gt; key through our &lt;a href="https://hack4impact.org" rel="noopener noreferrer"&gt;Hack4Impact.org&lt;/a&gt; site. I've adjusted the dropdown overlay to &lt;code&gt;opacity: 0.1&lt;/code&gt; to show which items are focused, but you can expect the same behavior for &lt;code&gt;opacity: 0&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbholmes.dev%2Fassets%2Fblog%2Fshow-hide-animation%2Fshow-hide-tab-focus-demo.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbholmes.dev%2Fassets%2Fblog%2Fshow-hide-animation%2Fshow-hide-tab-focus-demo.gif" alt="When hitting toggling the hamburger menu off on the Hack4Impact site, we see a faint focus ring over each link as we tab to these low opacity elements"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Follow either the focus ring around the elements, or the &lt;code&gt;activeElement&lt;/code&gt; query in the console view (which prints the element being focused)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Uh oh, I'm still able to focus and select these links! What's more, our &lt;code&gt;opacity&lt;/code&gt; solution poses a problem for visually impaired users, since they won't know that our visual &lt;code&gt;opacity&lt;/code&gt; shift &lt;em&gt;really&lt;/em&gt; means showing and hiding a dropdown.&lt;/p&gt;

&lt;p&gt;What we really want is the best of &lt;em&gt;all&lt;/em&gt; worlds:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A nifty fade in-and-out animation for sighted users&lt;/li&gt;
&lt;li&gt;The ability to focus interactable links and buttons &lt;em&gt;only&lt;/em&gt; when the element is revealed&lt;/li&gt;
&lt;li&gt;A callout for screenreaders whenever we show and hide something&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  ⌨️ Let's accommodate keyboard users
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note:&lt;/strong&gt; This section only applies when showing / hiding interactable elements like a list of links.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Focusing on that &lt;code&gt;tab&lt;/code&gt; key first, we need to find a way to make sure that a not-so-visible element can't be accessed at all.&lt;/p&gt;

&lt;p&gt;You might remember a nifty property for this already: &lt;code&gt;display: none&lt;/code&gt;. This makes element nonexistent in the document, right? &lt;/p&gt;

&lt;p&gt;Well it's worth a shot! Let's toggle to &lt;code&gt;display: none&lt;/code&gt; when our element is hidden, and add &lt;code&gt;display&lt;/code&gt; alongside &lt;code&gt;opacity&lt;/code&gt; in our list of transitions.&lt;/p&gt;

&lt;p&gt;We'll be using this basic navigation dropdown as fuel for the rest of our code examples. Nothing fancy on the JS + HTML sides! The important piece are those &lt;code&gt;.dropdown&lt;/code&gt; and &lt;code&gt;.dropdown.expanded&lt;/code&gt; selectors in our CSS. That's where we toggle between our &lt;code&gt;display&lt;/code&gt; states like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.dropdown&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.dropdown.expanded&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c"&gt;/* since we're using flexbox in our dropdown */&lt;/span&gt;
    &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c"&gt;/* to (hopefully) fade from expanded to collapsed in 0.2 seconds */&lt;/span&gt;
    &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;opacity&lt;/span&gt; &lt;span class="m"&gt;0.2s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.2s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Full example:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/bholmesdev/embed/BaWzGpY?height=600&amp;amp;default-tab=css,result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;But wait, where's our fade transition? Sorry to say this is the big limitation of the &lt;code&gt;display&lt;/code&gt; property: &lt;strong&gt;it cannot be used in CSS transitions&lt;/strong&gt; ( &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_animated_properties" rel="noopener noreferrer"&gt;full list of animatable CSS properties here&lt;/a&gt;) This means, when we toggle off our &lt;code&gt;expanded&lt;/code&gt; class, it immediately switches to &lt;code&gt;display: none&lt;/code&gt; (ignoring our &lt;code&gt;0.2s&lt;/code&gt; transition) before our opacity has time to sexily fade out 😥&lt;/p&gt;

&lt;p&gt;What we need is a way to toggle off &lt;code&gt;display&lt;/code&gt; &lt;em&gt;only after&lt;/em&gt; our element fades out of view. And no, we can't use &lt;code&gt;keyframes&lt;/code&gt; to set up this sequence (&lt;a href="https://gist.github.com/Holben888/45fa3c0c5ea7bf0e2ef74b882a2e2522" rel="noopener noreferrer"&gt;here's the code you were probably about the try&lt;/a&gt; 😉). But fear not! There's a neat property called &lt;code&gt;visibility&lt;/code&gt; that can do exactly what we want. Just hot swap &lt;code&gt;display: none&lt;/code&gt; for &lt;code&gt;visibility: hidden&lt;/code&gt; and update the transition:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/bholmesdev/embed/xxqOQrw?height=600&amp;amp;default-tab=css,result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;And &lt;em&gt;BAM!&lt;/em&gt; Our fade animation remains in-tact, but we've successfully hidden our element from keyboard-ists 👍&lt;/p&gt;

&lt;p&gt;You can click on that CodePen preview above and start hitting &lt;code&gt;tab&lt;/code&gt; to test our solution. Your focus should immediately jump from the hamburger button to the CodePen zoom controls, instead of trying to focus those invisible links. And once you focus the button, hit &lt;code&gt;space&lt;/code&gt;, and start &lt;code&gt;tab&lt;/code&gt;ing through, those links become selectable.&lt;/p&gt;

&lt;p&gt;💡 &lt;em&gt;&lt;strong&gt;Note:&lt;/strong&gt; There is 1 subtle difference between &lt;code&gt;display: none&lt;/code&gt; and &lt;code&gt;visibility: hidden&lt;/code&gt;. When using the former, an element gets completely removed from the document, so any elements next to it will &lt;strong&gt;shift to take up the space it left behind.&lt;/strong&gt; By contrast, &lt;code&gt;visibility: hidden&lt;/code&gt; just hides the element visually, while &lt;strong&gt;leaving the gap&lt;/strong&gt; for where it used to sit. This prevents your website layout from shifting around when you toggle elements on and off.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  👁 Let's accommodate screen readers
&lt;/h2&gt;

&lt;p&gt;Okay, so we know what our website looks like... but what does it sound like? 🤔&lt;/p&gt;

&lt;p&gt;Well, I'd recommend breaking out a screen reader for this next part! You can reach for the VoiceOver tool if you're running MacOS, but there's also a &lt;a href="https://chrome.google.com/webstore/detail/screen-reader/kgejglhpjiefppelpmljglcjbhoiplfn" rel="noopener noreferrer"&gt;screen reader for Chrome&lt;/a&gt; you can try on any computer.&lt;/p&gt;

&lt;p&gt;If you're interested, you can follow along with this video walkthrough to see the &lt;a href="https://support.apple.com/guide/voiceover-guide/welcome/web" rel="noopener noreferrer"&gt;VoiceOver tool&lt;/a&gt; in action 👇&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/21S1PFBxV6g"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Communicating toggle states with &lt;code&gt;aria-expanded&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Our navigation toggle button is the first issue to address. If you try expanding and collapsing our menu as-is, the screenreader won't read anything &lt;em&gt;new&lt;/em&gt; to the user. This is because, well, it's a plain old button at the moment. We need to tell our reader that it's &lt;em&gt;actually&lt;/em&gt; an on-off toggle.&lt;/p&gt;

&lt;p&gt;We can pull this off by adding an &lt;code&gt;aria-expanded&lt;/code&gt; attribute to our button. This boolean should call out whether our menu is in the "expanded" or "collapsed" state. Just set the initial state in our HTML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- collapsed by default --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;aria-expanded=&lt;/span&gt;&lt;span class="s"&gt;"false"&lt;/span&gt; &lt;span class="na"&gt;aria-label=&lt;/span&gt;&lt;span class="s"&gt;"Nav toggle"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"toggle-dropdown"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And keep that value up-to-date wherever we're tracking button clicks. Here's a basic version for plain-JS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;toggle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.toggle-dropdown&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;toggle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// toggle our CSS class&lt;/span&gt;
  &lt;span class="nx"&gt;dropdown&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toggle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;expanded&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// and if our "expanded" class is turned on...&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dropdown&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;expanded&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// set aria-expanded to true&lt;/span&gt;
    &lt;span class="nx"&gt;toggle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;aria-expanded&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;true&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// otherwise it's false&lt;/span&gt;
    &lt;span class="nx"&gt;toggle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;aria-expanded&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;false&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/bholmesdev/embed/RwpVqdb?height=600&amp;amp;default-tab=js,result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbholmes.dev%2Fassets%2Fblog%2Fshow-hide-animation%2Faria-expanded-screenreader-demo.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbholmes.dev%2Fassets%2Fblog%2Fshow-hide-animation%2Faria-expanded-screenreader-demo.gif" alt="VoiceOver output as we toggle our button. Notice we get a new announcement for every click saying either "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;VoiceOver output as we toggle our button. Notice we get a new announcement for every click saying either "expanded" or "collapsed"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You can also add the attribute &lt;code&gt;aria-haspopup="true"&lt;/code&gt; to your button. This will just added the word "popup" to the screenreader output to make behavior just a bit more understandable. Optional, but recommended!&lt;/p&gt;

&lt;h3&gt;
  
  
  Focusing our dropdown on expanded
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note:&lt;/strong&gt; This should only be used when showing / hiding interactable elements. If you're just revealing images or paragraphs, the &lt;code&gt;aria-expanded&lt;/code&gt; piece should be sufficient!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So now we're telling the user that they're expanding and collapsing something. But what is this &lt;em&gt;something&lt;/em&gt; their actually toggling?&lt;/p&gt;

&lt;p&gt;Sadly, there's no straightforward way to tell the user "you just expanded my fancy dropdown!" There &lt;em&gt;is&lt;/em&gt; an &lt;code&gt;aria-controls&lt;/code&gt; attribute you can try, but it's only supported by the premium &lt;a href="https://www.freedomscientific.com/products/software/jaws/" rel="noopener noreferrer"&gt;JAWS screen reader&lt;/a&gt; (which many users don't have access to).&lt;/p&gt;

&lt;p&gt;So, we're going to offer the next best thing: just focus the first element in our dropdown to read out the contents immediately. This means shifting keyboard focus (yes, visually impaired users tend to navigate navigate using a keyboard) from our menu button to our first link whenever &lt;code&gt;aria-expanded="true"&lt;/code&gt;. Of course, this only applies when revealing interactable elements like buttons or links, so it should&lt;/p&gt;

&lt;p&gt;Should be simple enough! Just add a call to &lt;code&gt;focus()&lt;/code&gt; whenever we expand the element, right?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;toggle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dropdown&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;expanded&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// use firstElementChild to grab the first link&lt;/span&gt;
    &lt;span class="nx"&gt;dropdown&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;firstElementChild&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;focus&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nx"&gt;toggle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;aria-expanded&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;true&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Well, this doesn't work super reliably when we have our fancy CSS transition. There's a chance we'll try to focus the first link while &lt;code&gt;visibility&lt;/code&gt; is still &lt;code&gt;hidden&lt;/code&gt;, which prevents our &lt;code&gt;focus()&lt;/code&gt; call from going through 😕&lt;/p&gt;

&lt;p&gt;Luckily, there's an easy way to "wait" on our CSS transition to finish before setting focus. We just need to listen for the &lt;code&gt;transitionend&lt;/code&gt; event on our dropdown like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;dropdown&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;transitionend&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dropdown&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;expanded&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// instead of calling focus() in our 'click' listener,&lt;/span&gt;
    &lt;span class="c1"&gt;// we'll call it from here!&lt;/span&gt;
    &lt;span class="nx"&gt;dropdown&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;firstElementChild&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;focus&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this in place, we should get reliable link focusing with a matching screen reader output 👍&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbholmes.dev%2Fassets%2Fblog%2Fshow-hide-animation%2Fshow-hide-focus-first-link.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbholmes.dev%2Fassets%2Fblog%2Fshow-hide-animation%2Fshow-hide-focus-first-link.gif" alt="VoiceOver output from hitting the toggle button. We see "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Finished product&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/bholmesdev/embed/vYxKVgB?height=600&amp;amp;default-tab=result,js&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  ⚙️ Applying this to your favorite framework
&lt;/h2&gt;

&lt;p&gt;We've covered a lot of ground using plain JS, but it's quick-and-easy to apply to any component framework!&lt;/p&gt;

&lt;p&gt;We just need a couple pieces:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;an &lt;code&gt;expanded&lt;/code&gt; state variable&lt;/strong&gt; to keep track of showing / hiding&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;a callback to &lt;code&gt;focus()&lt;/code&gt; our first dropdown element on &lt;code&gt;transitionend&lt;/code&gt;.&lt;/strong&gt; For most frameworks, we can just attach a callback function to an element's &lt;code&gt;onTransitionEnd&lt;/code&gt; attribute.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  A simple React solution
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;DropdownExample&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;expanded&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setExpanded&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;focusFirstDropdownLink&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// if we apply this function to our dropdown,&lt;/span&gt;
    &lt;span class="c1"&gt;// the "target" should be a reference to the dropdown itself!&lt;/span&gt;
    &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;firstElementChild&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;focus&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;nav&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;
          &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"toggle-dropdown"&lt;/span&gt;
          &lt;span class="na"&gt;aria-expanded&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;expanded&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;aria-haspopup&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;
          &lt;span class="na"&gt;aria-label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Nav toggle"&lt;/span&gt;
          &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setExpanded&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;expanded&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          ...
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;nav&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;
        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/*toggle our "expanded" CSS class*/&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;expanded&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dropdown expanded&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dropdown&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/*trigger our focus() once the dropdown's fade effect finishes*/&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;onTransitionEnd&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;focusFirstDropdownLink&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Home&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/about"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;About&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/projects"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Projects&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/contact"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Contact&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pretty straightforward rewrite! The ability to bind our &lt;code&gt;focus()&lt;/code&gt; function to the &lt;code&gt;onTransitionEnd&lt;/code&gt; listener saves us a lot of work. We don't even need a &lt;code&gt;useRef&lt;/code&gt; to grab the element, in fact.&lt;/p&gt;

&lt;p&gt;Here's a &lt;a href="https://codesandbox.io/s/accessible-show-hide-react-4rv7m?file=/src/App.js" rel="noopener noreferrer"&gt;&lt;strong&gt;working sandbox demo&lt;/strong&gt;&lt;/a&gt; for you to try 🚀&lt;/p&gt;

&lt;h3&gt;
  
  
  Svelte example
&lt;/h3&gt;

&lt;p&gt;This should be equally trivial in Svelte:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;    
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;expanded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;focusFirstDropdownLink&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// again, "target" should be a reference to our dropdown&lt;/span&gt;
        &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;firstElementChild&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;focus&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;nav&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"toggle-dropdown"&lt;/span&gt; &lt;span class="na"&gt;aria-expanded=&lt;/span&gt;&lt;span class="s"&gt;{expanded}&lt;/span&gt; &lt;span class="na"&gt;aria-haspopup=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt; &lt;span class="na"&gt;aria-label=&lt;/span&gt;&lt;span class="s"&gt;"Nav toggle"&lt;/span&gt; &lt;span class="na"&gt;on:click=&lt;/span&gt;&lt;span class="s"&gt;{()&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; expanded = !expanded}&amp;gt;
        &lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;viewBox=&lt;/span&gt;&lt;span class="s"&gt;"0 0 448 512"&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"100"&lt;/span&gt; &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;"bars"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;path&lt;/span&gt; &lt;span class="na"&gt;d=&lt;/span&gt;&lt;span class="s"&gt;"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/nav&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!--Note we can use class:expanded to bind our "expanded" CSS class to the "expanded" state variable--&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"dropdown"&lt;/span&gt; &lt;span class="na"&gt;class:expanded&lt;/span&gt; &lt;span class="na"&gt;on:transitionend=&lt;/span&gt;&lt;span class="s"&gt;{focusFirstDropdownLink}&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Home&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/about"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;About&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/projects"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Projects&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/contact"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Contact&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's another &lt;a href="https://svelte.dev/repl/daa482983cd84a199f34895cdd3a08f6?version=3.38.2" rel="noopener noreferrer"&gt;&lt;strong&gt;working sandbox demo&lt;/strong&gt;&lt;/a&gt; for you to try 🚀&lt;/p&gt;

&lt;h2&gt;
  
  
  Learn a little something?
&lt;/h2&gt;

&lt;p&gt;Glad to hear it! If you want more universal solutions like this, you can &lt;a href="https://tinyletter.com/bholmesdev" rel="noopener noreferrer"&gt;sign up for the web wizardry newsletter&lt;/a&gt; for some bi-weekly web sorcery 🔮&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>css</category>
      <category>react</category>
      <category>svelte</category>
    </item>
    <item>
      <title>Want CSS variables in media query declarations? Try this!</title>
      <dc:creator>Ben Holmes</dc:creator>
      <pubDate>Wed, 12 May 2021 13:59:51 +0000</pubDate>
      <link>https://dev.to/bholmesdev/want-css-variables-in-media-query-declarations-try-this-o5h</link>
      <guid>https://dev.to/bholmesdev/want-css-variables-in-media-query-declarations-try-this-o5h</guid>
      <description>&lt;p&gt;If you're like me, you've probably been &lt;a href="https://bholmes.dev/blog/how-using-css-variables-cut-down-on-my-javascript/"&gt;stretching CSS variables / custom properties to their limits&lt;/a&gt; while building your own design systems. But this "silver bullet" can lead to a nasty roadblock: &lt;strong&gt;you can't use them in media query declarations.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To clarify, this is the behavior you might be after:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;--mobile-breakpoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;600px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;--tablet-breakpoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;900px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;@media&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--mobile-breakpoint&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;.obligatory-hamburger-menu&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;visibility&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;visible&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...which sadly won't work 😣&lt;/p&gt;

&lt;h2&gt;
  
  
  🙅 Why var() can't work in a @media declaration
&lt;/h2&gt;

&lt;p&gt;This may seem confusing at first. If our variables are available at the &lt;code&gt;:root&lt;/code&gt; of the page, why can't a media query access them?&lt;/p&gt;

&lt;p&gt;Well, it comes down to what the &lt;code&gt;:root&lt;/code&gt; element actually means: &lt;strong&gt;the root element of the HTML document.&lt;/strong&gt; But conceptually, media queries aren't attached to HTML elements at all. These declarations are processed while your CSS is being parsed, so it won't know to look to the &lt;code&gt;:root&lt;/code&gt; and pull in the variable values.&lt;/p&gt;

&lt;p&gt;If this seems confusing, let's consider a CSS variable like this one on the &lt;code&gt;body&lt;/code&gt; element:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;--mobile-breakpoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;600px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;@media&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--mobile-breakpoint&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Clearly, our &lt;code&gt;@media&lt;/code&gt; query doesn't know about this &lt;code&gt;--mobile-breakpoint&lt;/code&gt; variable since it doesn't "belong" to the &lt;code&gt;body&lt;/code&gt; element. Heck, it doesn't belong to any element in your HTML, which explains why our &lt;code&gt;:root&lt;/code&gt; solution won't work either.&lt;/p&gt;

&lt;p&gt;Sass / SASS can make this even more confusing by allowing &lt;code&gt;@media&lt;/code&gt; blocks to be "nested" inside other rulesets. Don't be fooled! When your lovely Sass gets compiled, those media queries float right back out to the base of your CSS document.&lt;/p&gt;

&lt;h3&gt;
  
  
  Hope on the horizon for CSS "environment" variables
&lt;/h3&gt;

&lt;p&gt;As luck would have it, the W3C isn't happy about this limitation either. &lt;a href="https://drafts.csswg.org/css-env-1/"&gt;Their proposal&lt;/a&gt; for "environment" variables is at the &lt;em&gt;earliest stage&lt;/em&gt; right now (phase 1 as of May 2021), but it seems to tackle this very issue!&lt;/p&gt;

&lt;p&gt;I'll avoid showing you the syntax since it'll likely change overtime. Just know it's meant to address variables that exist &lt;em&gt;beyond&lt;/em&gt; the HTML element level for use cases like &lt;code&gt;@media&lt;/code&gt; declarations.&lt;/p&gt;

&lt;h2&gt;
  
  
  🤔 Workarounds you can try
&lt;/h2&gt;

&lt;p&gt;If you want media query variables and want them &lt;em&gt;now,&lt;/em&gt; there's a few options available to you.&lt;/p&gt;

&lt;h3&gt;
  
  
  For Sass / SCSS users
&lt;/h3&gt;

&lt;p&gt;The first is what I'd recommend to existing Sass users: &lt;strong&gt;fall back to Sass variables for media queries, and use CSS variables everywhere else.&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;This is because, unlike CSS variables, Sass variables don't "belong" to CSS rulesets under-the-hood; They're just a piece of syntactical sugar for SASS to pick up and "paste" in the correct value. Just know that you'll need to fall back to Sass for math calculations as well instead of using the &lt;code&gt;calc()&lt;/code&gt; function (because this isn't valid either!)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* ✅ good */&lt;/span&gt;
&lt;span class="k"&gt;@media&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max-width&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$mobile-breakpoint&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="m"&gt;50px&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nc"&gt;...&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="cm"&gt;/* ❌ bad */&lt;/span&gt;
&lt;span class="k"&gt;@media&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max-width&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$mobile-breakpoint&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="m"&gt;50px&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nc"&gt;...&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  For anyone else
&lt;/h3&gt;

&lt;p&gt;If you either don't use Sass or don't want 2 different syntaxes for your variables, there's a &lt;a href="https://www.npmjs.com/package/postcss-media-variables"&gt;neat PostCSS plugin&lt;/a&gt; you can try too. Just tack this onto an existing CSS build step (or introduce a build step against your will 😣) and &lt;em&gt;BAM!&lt;/em&gt; CSS variables &lt;em&gt;and&lt;/em&gt; the CSS &lt;code&gt;calc()&lt;/code&gt; function will work in your media query declarations 👍&lt;/p&gt;

&lt;p&gt;⚠️ &lt;strong&gt;Word of caution&lt;/strong&gt; on this solution though. You'll be writing &lt;em&gt;invalid&lt;/em&gt; CSS by current browser standards and making it valid, which can look pretty confusing to new codebase contributors. At least be sure to document this plugin in your project README if you decide to go this route.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learn a little something?
&lt;/h2&gt;

&lt;p&gt;Glad to hear it! If you want more universal web dev solutions like this, you can &lt;a href="https://tinyletter.com/bholmesdev"&gt;sign up for the web wizardry newsletter&lt;/a&gt; for some bi-weekly knowledge nuggets 🧠&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>css</category>
      <category>sass</category>
    </item>
    <item>
      <title>Picture perfect image optimization for any web framework</title>
      <dc:creator>Ben Holmes</dc:creator>
      <pubDate>Wed, 05 May 2021 14:03:35 +0000</pubDate>
      <link>https://dev.to/bholmesdev/picture-perfect-image-optimization-for-any-web-framework-2o77</link>
      <guid>https://dev.to/bholmesdev/picture-perfect-image-optimization-for-any-web-framework-2o77</guid>
      <description>&lt;p&gt;This entry comes from my web wizardry newsletter, where I explore evergreen solutions to common web dev problems (no matter your favorite framework). If you like what you see, &lt;a href="http://tinyletter.com/bholmesdev" rel="noopener noreferrer"&gt;&lt;strong&gt;go sign up for free&lt;/strong&gt;&lt;/a&gt; 🪄&lt;/p&gt;




&lt;p&gt;If you've been building websites for a while, "optimize your images" probably sounds like "eat your veggies." It's good for your website's health, it'll make your SEO big and strong... but compressing every image by hand doesn't sound appetizing to me 🤢&lt;/p&gt;

&lt;p&gt;So we're going to talk &lt;strong&gt;easy wins&lt;/strong&gt; for 1) optimizing image file formats and sizes with the &lt;code&gt;picture&lt;/code&gt; element, and 2) an automation process using 11ty that you can take with you to your build setup of choice 💪&lt;/p&gt;

&lt;p&gt;💁 &lt;strong&gt;Intended audience:&lt;/strong&gt; &lt;em&gt;This is meant for developers building "template-driven"  static sites (11ty, Jekyll, Hugo, plain HTML) or "component-driven" web apps (NextJS, Gatsby, etc). If you're working with site builders like Wordpress or Shopify, this probably isn't the article for you!&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🥦 So what's wrong with my images right now?
&lt;/h2&gt;

&lt;p&gt;To show what's at stake, here's the lighthouse rating from one of my recent blog posts (images compressed with &lt;a href="https://tinyjpg.com/" rel="noopener noreferrer"&gt;tinyJPG&lt;/a&gt; mind you!)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbholmes.dev%2Fassets%2Fblog%2Fimage-opt-bad-lighthouse-score.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbholmes.dev%2Fassets%2Fblog%2Fimage-opt-bad-lighthouse-score.png" alt="List of poor image loadtimes from Chromium Lighthouse performance report"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Yikes!&lt;/em&gt; 10 seconds to grab all those images? Chromium definitely does some throttling to test on "slower" internet connections, but it's clear those KB rating are quite high (especially for mobile users).&lt;/p&gt;

&lt;p&gt;This just to show that &lt;strong&gt;there's much more to image optimization than compression!&lt;/strong&gt; There's also:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Serving the right format,&lt;/strong&gt; with JPGs preferrable and &lt;code&gt;.webp&lt;/code&gt; or &lt;code&gt;.avi&lt;/code&gt; &lt;em&gt;especially&lt;/em&gt; so&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Serving the right size,&lt;/strong&gt; ideally with &lt;em&gt;multiple copies&lt;/em&gt; of the same image at different widths and heights&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Loading at the right time,&lt;/strong&gt; reaching for "lazy" loading when we can&lt;/li&gt;
&lt;li&gt;Heck, even &lt;strong&gt;including &lt;code&gt;alt&lt;/code&gt; text&lt;/strong&gt; can affect your site from both accessibility and SEO standpoints! &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I learned a bit about addressing those format and size problems using &lt;code&gt;picture&lt;/code&gt; elements, and my lighthouse definitely thanked me for it 😄&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1378869249930842116-45" src="https://platform.twitter.com/embed/Tweet.html?id=1378869249930842116"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1378869249930842116-45');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1378869249930842116&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;h2&gt;
  
  
  🌅 Fixing format + size problems with the &lt;code&gt;picture&lt;/code&gt; element
&lt;/h2&gt;

&lt;p&gt;So how can we deliver different image files for the right people? Well, let's start with a humble image element like this one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/assets/mega-chonker.jpg"&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"1000"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"600"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"A perfectly sized cat"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Refer to &lt;a href="https://i.kym-cdn.com/photos/images/original/001/417/621/d9f.jpg" rel="noopener noreferrer"&gt;this handy chart&lt;/a&gt; for understanding "chonk" levels&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now, say we've opened our image editor and saved a smaller version for mobile users sitting at, say, 600 pixels wide. You could probably set up some CSS to hot-swap your images depending on your screen width:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"desktop"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/assets/chonker-1000w.jpg"&lt;/span&gt;
    &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"1000"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"600"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"A perfectly sized cat"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mobile"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/assets/chonker-600w.jpg"&lt;/span&gt;
    &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"600"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"300"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"A perfectly sized cat"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@media&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;600px&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;.desktop&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;@media&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;601px&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;.mobile&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...But this isn't very scalable. What if we're, say, working in a markdown file where we can't append class names? Or we have different formats we want to switch between depending on browser support (JPEG vs WEBP for example)?&lt;/p&gt;

&lt;p&gt;This is where the &lt;code&gt;picture&lt;/code&gt; element comes in. Take this example here:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;picture&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- List out all the WEBP images + WEBP sizes we can choose from --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;source&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"image/webp"&lt;/span&gt;
    &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"/assets/chonker-600w.webp 600w, /assets/chonker-1000w.webp 1000w"&lt;/span&gt;
    &lt;span class="na"&gt;sizes=&lt;/span&gt;&lt;span class="s"&gt;"100vw"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- In case a browser doesn't support WEBP, fall back to this set of JPG sources --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;source&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"image/jpeg"&lt;/span&gt;
    &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"/assets/chonker-600w.jpg 600w, /assets/chonker-1000w.jpg 1000w"&lt;/span&gt;
    &lt;span class="na"&gt;sizes=&lt;/span&gt;&lt;span class="s"&gt;"100vw"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- The actual, style-able img element that "receives" these sources --&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- Also includes a default src in case no &amp;lt;source&amp;gt; can be applied --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/assets/chonker-600.png"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"A perfectly sized cat"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/picture&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Some big takeaways:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We can wrap our image tags in a &lt;code&gt;picture&lt;/code&gt; to unlock a "switch" case of sorts, with your browser picking the first &lt;code&gt;source&lt;/code&gt; element that it's able to render. But admittedly, &lt;em&gt;most&lt;/em&gt; modern browsers will reach for those shiny &lt;code&gt;.webp&lt;/code&gt; files listed under &lt;code&gt;type="image/webp"&lt;/code&gt; without needing the JPG fallbacks (&lt;a href="https://caniuse.com/?search=webp" rel="noopener noreferrer"&gt;current browser support here&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Each source has a &lt;strong&gt;&lt;code&gt;srcset&lt;/code&gt; property&lt;/strong&gt;, which takes in a list of source URLs for a given image format. These sources are comma-separated, plus a pixel-value width using that &lt;code&gt;w&lt;/code&gt; on the end. The browser will then decide which source to use based on the &lt;code&gt;sizes&lt;/code&gt; property (more on that in the next section)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Picture elements are not images themselves!&lt;/strong&gt; This is an interesting gotcha when you start trying to style those images. So, you'll want to keep putting all your image-specific CSS (ex. &lt;code&gt;object-fit&lt;/code&gt;) on that &lt;code&gt;img&lt;/code&gt; element instead of the &lt;code&gt;picture&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  The &lt;code&gt;sizes&lt;/code&gt; attribute
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Sizes&lt;/code&gt; is an interesting beast. It &lt;em&gt;almost&lt;/em&gt; looks like CSS actually, with some small syntax differences.&lt;/p&gt;

&lt;p&gt;Remember those &lt;code&gt;mobile&lt;/code&gt; and &lt;code&gt;desktop&lt;/code&gt; helper classes from earlier? Well, &lt;code&gt;sizes&lt;/code&gt; let us do something rather similar. &lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/uGiG2VWkeSs"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key takeaways from the video:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In general, the &lt;code&gt;sizes&lt;/code&gt; attribute is a way to tell the browser &lt;strong&gt;which image to use for a given screen size.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's say we have a banner image that takes up the full width of the screen for mobile users, but we have a table of contents that takes up half the width at &lt;code&gt;500px&lt;/code&gt; wide and above.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbholmes.dev%2Fassets%2Fblog%2Fimage-sizes-table-of-contents-demo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbholmes.dev%2Fassets%2Fblog%2Fimage-sizes-table-of-contents-demo.png" alt="Table of contents taking up half the width of the screen"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Putting on our CSS hat, this means our image is &lt;code&gt;100vw&lt;/code&gt; (100% screen width) below &lt;code&gt;500px&lt;/code&gt;, and &lt;code&gt;50vw&lt;/code&gt; when we hit &lt;code&gt;@media (min-width: 500px)&lt;/code&gt;. This perfectly translates to &lt;code&gt;sizes&lt;/code&gt; 👉 &lt;code&gt;sizes="(min-width: 500px) 50vw, 100vw"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;And in the context of a &lt;code&gt;picture&lt;/code&gt; element:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;picture&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!--stack up your media queries as sizes, delineated by commas ","--&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;source&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"image/webp"&lt;/span&gt;
    &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"/img/6dfd7ac6-600.webp 600w, /img/6dfd7ac6-900.webp 900w..."&lt;/span&gt;
    &lt;span class="na"&gt;sizes=&lt;/span&gt;&lt;span class="s"&gt;"(min-width: 500px) 50vw, 100vw"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Blue and purple cluster of stars"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/img/6dfd7ac6-600.jpeg"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/picture&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As a rule-of-thumb, you should probably use &lt;code&gt;100vw&lt;/code&gt; as a "base case" for smaller devices, and rack up media queries on top depending on how your layout changes. This does mean &lt;code&gt;sizes&lt;/code&gt; will be different depending on the &lt;em&gt;context&lt;/em&gt; your images are living in, so look out for that if you're using a component-based framework!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note:&lt;/strong&gt; You may be wondering why browsers can't do all this work for us. Well, this comes down to the unpredictable nature of "width" when you're throwing around CSS everywhere. If you're like me, you tend to use a lot of percentages like &lt;code&gt;width: 100%&lt;/code&gt; for image blocks, which may adjust depending on the container, padding, margins, etc that get applied. If the browser tried to decipher all this styling before loading an image, you'd be waiting a lot longer than you might want!&lt;/em&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Respecting HD displays
&lt;/h3&gt;

&lt;p&gt;Note that our screen's pixel density can also affect which image gets picked from a given &lt;code&gt;srcset&lt;/code&gt;. For high-density mobile displays, it'll actually choose an image that's &lt;strong&gt;roughly double&lt;/strong&gt; the width you specify! Say we have a simple &lt;code&gt;picture&lt;/code&gt; declaration like this for instance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;picture&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;source&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"image/webp"&lt;/span&gt;
    &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"/img/galaxy-600.webp 600w, /img/galaxy-1200.webp 1200w"&lt;/span&gt;
    &lt;span class="na"&gt;sizes=&lt;/span&gt;&lt;span class="s"&gt;"100vw"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/picture&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We're using &lt;code&gt;100vw&lt;/code&gt; here, so the browser should match the image source's width to the width of the display. Intuitively, we'd think that a &lt;code&gt;600px&lt;/code&gt; wide display would receive &lt;code&gt;/img/galaxy-600.webp&lt;/code&gt;... but for HD displays like Macbooks or modern smartphones, it'll actually reach for an image at 600 x 2 pixels wide (&lt;code&gt;/img/galaxy-1200.webp 1200w&lt;/code&gt; in this case). So when you're generating multiple image sizes, always reach for those higher values 💡&lt;/p&gt;

&lt;h2&gt;
  
  
  🔨 Applying this to your site with 11ty image
&lt;/h2&gt;

&lt;p&gt;Alright, so we see how useful the &lt;code&gt;picture&lt;/code&gt; element can be... but it's only as powerful as the pictures we can supply to it. Do we really want to create all those beautifully resized, optimized, multi-format images &lt;em&gt;by hand?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Luckily, there's a lot of tools to handle this process for us, and I'm going to hone in on the simplest I've found: &lt;a href="https://www.11ty.dev/docs/plugins/image/#output-directory" rel="noopener noreferrer"&gt;11ty's image plugin.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🚨 Now before you start scrolling to the next section, &lt;strong&gt;no, you don't need to build your site with 11ty to use this.&lt;/strong&gt; Playing around with this tool, I realized it's perfect for generating optimized images on-the-fly for &lt;em&gt;any&lt;/em&gt; use case, no command line prowess required 🔥 &lt;/p&gt;

&lt;h3&gt;
  
  
  Generating optimized images
&lt;/h3&gt;

&lt;p&gt;Let's play along at home! Seriously, drop everything and go open your code editor 🧑‍💻 Then, make a fresh directory / folder and create a basic &lt;code&gt;package.json&lt;/code&gt;. We'll be installing the &lt;code&gt;@11ty/eleventy-img&lt;/code&gt; dependency:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;woah-11ty-image-is-cool &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;woah-11ty-image-is-cool
npm init &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="c"&gt;# Make a package.json with defaults for everything&lt;/span&gt;
npm i @11ty/eleventy-img
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now make a random JavaScript file for us to play with (I'll call mine &lt;code&gt;image-generator.js&lt;/code&gt;). Inside, just paste the example code at the top of &lt;a href="https://www.11ty.dev/docs/plugins/image/#output-directory" rel="noopener noreferrer"&gt;11ty's documentation&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@11ty/eleventy-img&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://images.unsplash.com/photo-1608178398319-48f814d0750c&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;stats&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nc"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;widths&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stats&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;})();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hm, this looks pretty straightforward. Let's run it from our terminal and see what happens:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node ./image-generator.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With any luck, you should see a couple new faces appear:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A &lt;code&gt;/img&lt;/code&gt; directory&lt;/strong&gt; with 2 images inside: 1 JPG picture of a galaxy that's 300 pixels wide, and a matching &lt;code&gt;webp&lt;/code&gt; image of the same size. Notice how this matches up with our &lt;code&gt;widths&lt;/code&gt; array from the code snippet 👀&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A &lt;code&gt;/cache&lt;/code&gt; directory&lt;/strong&gt; with some strings of characters. Think of this like a note-to-self for the plugin about the image we downloaded. It's expensive to download images off the internet, so to avoid loading it &lt;em&gt;every time we run the script,&lt;/em&gt; 11ty checks the cache to see if we've already loaded the image in the past 👍&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You'll also see a &lt;a href="https://www.11ty.dev/docs/plugins/image/#usage" rel="noopener noreferrer"&gt;blob of "stats"&lt;/a&gt; logged to your console. Most of these properties are self-explanatory, and some should look familiar from our &lt;code&gt;picture&lt;/code&gt; walkthrough earlier on (namely the &lt;code&gt;sourceType&lt;/code&gt; and &lt;code&gt;srcset&lt;/code&gt; attributes). We even get the output &lt;code&gt;size&lt;/code&gt; of the image in bytes, in case you want to inspect the differences between formats and sizes.&lt;/p&gt;

&lt;p&gt;But wait, there's more! Let's try experimenting with different widths and formats:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;stats&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nc"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;widths&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1400&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;formats&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;jpg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;webp&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gif&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We should get a plethora of resolutions inside that &lt;code&gt;img&lt;/code&gt; directory. As you can imagine, this is perfect for our picture element from earlier. You can whip up all the &lt;code&gt;source&lt;/code&gt;s and &lt;code&gt;size&lt;/code&gt; attributes by hand as a learning exercise...&lt;/p&gt;

&lt;h3&gt;
  
  
  Automating our picture elements
&lt;/h3&gt;

&lt;p&gt;...Or let the plugin do this for us! Along with that handy array of &lt;code&gt;stats&lt;/code&gt;, 11ty image will splice everything into a valid &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; element. All we need is a call to the &lt;code&gt;generateHTML&lt;/code&gt; helper:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@11ty/eleventy-img&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://images.unsplash.com/photo-1608178398319-48f814d0750c&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;stats&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nc"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;widths&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1400&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generateHTML&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stats&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;A blue and purple galaxy of stars&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// alt text is required!&lt;/span&gt;
    &lt;span class="na"&gt;sizes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;100vw&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="c1"&gt;// remember our training with "sizes" from earlier...&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;})();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With any luck, we should see a beautiful &lt;code&gt;picture&lt;/code&gt; we can use anywhere on our site:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;picture&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;source&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"image/webp"&lt;/span&gt;
          &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"/img/6dfd7ac6-300.webp 300w, /img/6dfd7ac6-1000.webp 1000w,
                  /img/6dfd7ac6-1400.webp 1400w"&lt;/span&gt;
          &lt;span class="na"&gt;sizes=&lt;/span&gt;&lt;span class="s"&gt;"100vw"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;source&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"image/jpeg"&lt;/span&gt;
          &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"/img/6dfd7ac6-300.jpeg 300w, /img/6dfd7ac6-1000.jpeg 1000w,
                  /img/6dfd7ac6-1400.jpeg 1400w"&lt;/span&gt;
          &lt;span class="na"&gt;sizes=&lt;/span&gt;&lt;span class="s"&gt;"100vw"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"A blue and purple galaxy of stars"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/img/6dfd7ac6-300.jpeg"&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"1400"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"1402"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/picture&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Going further
&lt;/h3&gt;

&lt;p&gt;This plugin has a whole host of extra options to explore too, like&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.11ty.dev/docs/plugins/image/#caching-remote-images-locally-new-in-image-0.3.0" rel="noopener noreferrer"&gt;messing with cache options&lt;/a&gt; for faster build times&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.11ty.dev/docs/plugins/image/#synchronous-usage" rel="noopener noreferrer"&gt;generating image stats + picture elements synchronously&lt;/a&gt;, so you don't have wait for the images to &lt;em&gt;actually&lt;/em&gt; get generated&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.11ty.dev/docs/plugins/image/#advanced-control-of-sharp-image-processor" rel="noopener noreferrer"&gt;fine-tuning the Sharp image processor&lt;/a&gt; to tweak the output to your needs&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  📣 Using 11ty image with any framework
&lt;/h2&gt;

&lt;p&gt;If all this &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; madness excites you, go throw this 11ty image plugin at your own &lt;code&gt;/assets&lt;/code&gt; directory! I wrote this handy little script to crawl all the images in a directory (&lt;a href="https://coderrocketfuel.com/article/recursively-list-all-the-files-in-a-directory-using-node-js" rel="noopener noreferrer"&gt;not recursively mind you&lt;/a&gt;) and spit out some optimized files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@11ty/eleventy-img&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;readdir&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fs/promises&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// node helper for reading folders&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;parse&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// node helper for grabbing file names&lt;/span&gt;

&lt;span class="p"&gt;;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;imageDir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./images&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="c1"&gt;// match this to your assets directory&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;files&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;readdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;imageDir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stats&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nc"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;imageDir&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;widths&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1400&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;// edit to your heart's content&lt;/span&gt;
      &lt;span class="na"&gt;filenameFormat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// make the filename something we can recognize.&lt;/span&gt;
        &lt;span class="c1"&gt;// In this case, it's just:&lt;/span&gt;
        &lt;span class="c1"&gt;// [original file name] - [image width] . [file format]&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stats&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// remove this if you don't want the logs&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you &lt;em&gt;happen&lt;/em&gt; to use 11ty on your personal site (or at least want to try), you can automate the &lt;code&gt;picture&lt;/code&gt; element insertion as well. &lt;a href="https://www.11ty.dev/docs/plugins/image/#use-this-in-your-templates" rel="noopener noreferrer"&gt;Their guide&lt;/a&gt; covers building your own "shortcode" function to insert the right &lt;code&gt;picture&lt;/code&gt; for every unoptimized image on your site.&lt;/p&gt;

&lt;p&gt;Even without this luxury though, this script is a great addition to any JS-based build step. Here's a basic &lt;code&gt;Image&lt;/code&gt; component I could slap into any React app based on that script above:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// consider using TypeScript for checking all these props!&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;fileName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sizes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;imageProps&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;picture&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;source&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"image/webp"&lt;/span&gt;
        &lt;span class="na"&gt;srcSet&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`/img/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;fileName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-600.webp 600w, /img/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;fileName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-1000.webp 1000w, /img/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;fileName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-1400.webp 1400w`&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;sizes&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;sizes&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;source&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"image/jpeg"&lt;/span&gt;
        &lt;span class="na"&gt;srcSet&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`/img/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;fileName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-600.jpeg 600w, /img/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;fileName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-1000.jpeg 1000w, /img/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;fileName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-1400.jpeg 1400w`&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;sizes&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;sizes&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;img&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`/img/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;fileName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-600.jpeg`&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;imageProps&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;picture&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Assuming all my images get generated with this file naming convention (and I always have images at 600, 1000, and 1400 widths), this should pull all our optimized images no problem 👍&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Here's a brief rundown&lt;/strong&gt; on applying these learnings to &lt;code&gt;create-react-app&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/RAzXB-qu22s"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Go try it yourself
&lt;/h3&gt;

&lt;p&gt;You can check out a running example of &lt;code&gt;create-react-app&lt;/code&gt; + 11ty image in &lt;a href="https://codesandbox.io/s/little-tree-dmr1w?file=/App.js" rel="noopener noreferrer"&gt;&lt;strong&gt;this CodeSandbox&lt;/strong&gt;&lt;/a&gt; 🪄&lt;/p&gt;

&lt;p&gt;This version will listen for new images during development as well. Feel free to &lt;a href="https://github.com/Holben888/create-react-app-with-11ty-image" rel="noopener noreferrer"&gt;fork the source code&lt;/a&gt; to try in your own project (and find the edge cases I inevitably missed 😉).&lt;/p&gt;

&lt;h3&gt;
  
  
  Other options for Next, Nuxt, Gatsby and more
&lt;/h3&gt;

&lt;p&gt;As cool as 11ty image can be, I should definitely highlight some "native" options for popular meta-frameworks: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;For Next, &lt;a href="https://nextjs.org/docs/api-reference/next/image" rel="noopener noreferrer"&gt;their built-in Image component&lt;/a&gt; is perfect.&lt;/strong&gt; They'll also cover our sizes, formats, and image compression automatically, plus some neat props for eagerly loading images that are "above the fold" using &lt;code&gt;priority&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;For Nuxt, &lt;a href="https://image.nuxtjs.org/components/nuxt-img/" rel="noopener noreferrer"&gt;their &lt;code&gt;&amp;lt;nuxt-img&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;nuxt-picture&amp;gt;&lt;/code&gt; components&lt;/a&gt; should have you covered.&lt;/strong&gt; These offer most of the same benefits as our 11ty image plugin, letting you specify different formats, a &lt;code&gt;sizes&lt;/code&gt; attribute, and background image compression. Just be sure to use &lt;code&gt;nuxt-picture&lt;/code&gt; if you want to allow multiple image formats instead of just one!&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;For Gatsby, &lt;a href="https://www.gatsbyjs.com/plugins/gatsby-plugin-image" rel="noopener noreferrer"&gt;you've got the gold standard of image optimization&lt;/a&gt; 🏆&lt;/strong&gt; Their image plugin was actually my main reason for using the framework a few years back, and it's only gotten better. The nicest feature (beyond matching everything we've talked about) is their loading animations for images. You can fade in over vector traces of an image, use a blur effect, and a lot more. The only downside is the hefty JS bundle it loads into the browser to pull this off, which I've &lt;a href="https://bholmes.dev/blog/before-building-your-next-static-site-with-react-consider-this/" rel="noopener noreferrer"&gt;given my opinions on over here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Beyond the framework, you can &lt;a href="https://davidwalsh.name/image-optimization-cloudinary" rel="noopener noreferrer"&gt;optimize remotely using something like Cloudinary&lt;/a&gt;.&lt;/strong&gt; This is a great option if you don't own the build process for your website, or don't want to store your images inside your code repository. For example, you can point all your Wordpress images to a cloudinary bucket and pull different image widths and formats for there. The only downside is the cost, since Cloudinary is doing all this image processing and storage for you.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Learn a little something?
&lt;/h2&gt;

&lt;p&gt;Glad to hear it! If you want more universal web dev solutions like this, you can &lt;a href="https://tinyletter.com/bholmesdev" rel="noopener noreferrer"&gt;sign up for the web wizardry newsletter&lt;/a&gt; for some bi-weekly knowledge nuggets 🧠&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>react</category>
      <category>eleventy</category>
    </item>
    <item>
      <title>How ES Modules have redefined web development</title>
      <dc:creator>Ben Holmes</dc:creator>
      <pubDate>Wed, 07 Apr 2021 21:01:18 +0000</pubDate>
      <link>https://dev.to/bholmesdev/how-es-modules-have-redefined-web-development-2hc6</link>
      <guid>https://dev.to/bholmesdev/how-es-modules-have-redefined-web-development-2hc6</guid>
      <description>&lt;p&gt;You know the inspirational phrase "skate to where the puck is going?" Well, in web development... it feels like the puck is teleporting across the rink at Mach 30 sometimes.&lt;/p&gt;

&lt;p&gt;That's how I felt diving into how ES Modules work. Turns out, there's been some huge shifts right under my framework-laden nose these past few years. After discovering that this is &lt;em&gt;valid JS&lt;/em&gt; &lt;a href="https://caniuse.com/?search=dynamic%20import"&gt;across all major browsers&lt;/a&gt;...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;js&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;script.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...I had to make a post about it. So let's explore&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;🥞 My misconceptions about what bundlers do these days&lt;/li&gt;
&lt;li&gt;🧩 What ES Modules + dynamic imports can do&lt;/li&gt;
&lt;li&gt;🚀 How build tools are evolving for the post-IE era&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Onwards!&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Foreward: Personal delusions in a webpack world
&lt;/h2&gt;

&lt;p&gt;What I'm sharing here is probably common knowledge to some. Heck, import-able JavaScript modules have lurked in the &lt;a href="https://stackoverflow.com/a/33748435"&gt;ECMAScript&lt;/a&gt; standard &lt;a href="https://medium.com/@giltayar/native-es-modules-ready-for-prime-time-87c64d294d3c"&gt;since 2017&lt;/a&gt;! But if you've been using "traditional" project configs like &lt;code&gt;create-react-app&lt;/code&gt; for a long time, you might think that old-school bundling is how the world works.&lt;/p&gt;

&lt;p&gt;So let me &lt;em&gt;ahem&lt;/em&gt; unpack the traditional definition of "bundling." In short, it's the concept of taking a chain of JS files like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// toppings.js&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;blueberries&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fresh-from-the-farm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nx"&gt;syrup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;maple&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// ingredients.js&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;flour&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;white&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;eggs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;free-range&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;milk&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2%&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;butter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dairy-free&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// pancake.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;blueberries&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;syrup&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./toppings&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;flour&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;eggs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;milk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;butter&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./ingredients&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pancake&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Pancake&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nx"&gt;pancake&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mixItUp&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt; &lt;span class="nx"&gt;flour&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;eggs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;milk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;butter&lt;/span&gt; &lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="nx"&gt;pancake&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cook&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nx"&gt;pancake&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;applyToppings&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt; &lt;span class="nx"&gt;blueberries&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;syrup&lt;/span&gt; &lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And "flattening" the import / export chains into a big bundle pancake 🥞&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// bundler-output-alksdfjfsadlf.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;toppings__chunk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;blueberries&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fresh-from-the-farm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nx"&gt;syrup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;maple&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ingredients__chunk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;flour&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;white&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;eggs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;free-range&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;milk&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2%&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;butter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dairy-free&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;blueberries&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;syrup&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;toppings__chunk&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;flour&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;eggs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;milk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;butter&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ingredients__chunk&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pancake&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Pancake&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nx"&gt;pancake&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mixItUp&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt; &lt;span class="nx"&gt;flour&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;eggs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;milk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;butter&lt;/span&gt; &lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="nx"&gt;pancake&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cook&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nx"&gt;pancake&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;applyToppings&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt; &lt;span class="nx"&gt;blueberries&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;syrup&lt;/span&gt; &lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So we're compressing all the JavaScript files we're developing into a &lt;em&gt;single&lt;/em&gt; file for the browser to consume. Back in 2015-era web development, this really was the only way to pull off "importing" one JS file into another. &lt;code&gt;import&lt;/code&gt; wasn't even valid JavaScript! It was just some neat trickery that build tools like webpack could pick up and understand.&lt;/p&gt;

&lt;p&gt;But silently, in the depths of the ES spec, &lt;code&gt;import&lt;/code&gt; and &lt;code&gt;export&lt;/code&gt; syntax &lt;em&gt;did&lt;/em&gt; become valid JavaScript. Almost overnight, it became feasible to leave all your &lt;code&gt;import&lt;/code&gt; and &lt;code&gt;export&lt;/code&gt; statements in your code or even &lt;em&gt;gasp&lt;/em&gt; ditch your JS bundler entirely 😨&lt;/p&gt;

&lt;p&gt;This innovation became what we call &lt;strong&gt;modules.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  ES Modules
&lt;/h2&gt;

&lt;p&gt;There's an &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules#other_differences_between_modules_and_standard_scripts"&gt;in-depth article from MDN&lt;/a&gt; on this topic that's &lt;em&gt;well&lt;/em&gt; worth the read. But in short, "ES Modules" (sometimes denoted by &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules#aside_%E2%80%94_.mjs_versus_.js"&gt;&lt;code&gt;.mjs&lt;/code&gt; files&lt;/a&gt;) are JavaScript files with some exported values for others to import and use. As long as you load your "entry" files with the &lt;code&gt;type="module"&lt;/code&gt; attribute:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"module"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"pancake.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That file is ready to &lt;code&gt;import&lt;/code&gt; all the other scripts it wants! Well, as long as those other scripts exist in your project's build of course (we'll ignore CORS issues for now 😁).&lt;/p&gt;

&lt;p&gt;This concept of importing what's needed over "flattening all the things" has some nice benefits:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;You don't need to load and parse &lt;em&gt;everything&lt;/em&gt; up front.&lt;/strong&gt; By default, anything &lt;code&gt;import&lt;/code&gt;ed is &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules#other_differences_between_modules_and_standard_scripts"&gt;"deferred" for loading as needed&lt;/a&gt;. In other words, your computer won't turn into a fighter jet trying to load JS when you first visit your website.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The need for tooling like webpack can (one day) disappear ✨&lt;/strong&gt; Bringing browsers closer to how &lt;em&gt;humans&lt;/em&gt; write their code is a huge win for newbies and pros alike 🏆&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Dynamic imports take it a step further
&lt;/h3&gt;

&lt;p&gt;Dynamic imports are the spicier side of ES Modules that &lt;em&gt;really&lt;/em&gt; make things interesting. As &lt;a href="https://v8.dev/features/dynamic-import"&gt;this article from the V8 team&lt;/a&gt; describes (creators of Google Chrome's rendering engine), a &lt;strong&gt;dynamic import&lt;/strong&gt; is an asynchronous fetch for some JavaScript whenever you need it.&lt;/p&gt;

&lt;p&gt;It's very similar to the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API"&gt;&lt;code&gt;fetch&lt;/code&gt; API&lt;/a&gt; in a way! But instread of grabbing some JSON or plain text, we're grabbing some real, &lt;em&gt;executable&lt;/em&gt; code that we want to run.&lt;/p&gt;

&lt;p&gt;All you need is a humble one-liner:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lookAtTheTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./fashionably-late.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...and you just grabbed all the &lt;code&gt;export&lt;/code&gt;s from that file. Loading JS on-the-fly like this has a &lt;em&gt;ton&lt;/em&gt; of benefits if you're working with &lt;a href="https://www.bloomreach.com/en/blog/2018/07/what-is-a-single-page-application.html"&gt;single page apps&lt;/a&gt; like NextJS or &lt;code&gt;create-react-app&lt;/code&gt;. The V8 team offered this elegantly simple take on client-side routing, only loading the JS you need when you click on a link:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;links&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nav &amp;gt; a&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;link&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;links&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// go grab whatever JS the route may need&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/script.mjs`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="c1"&gt;// The module exports a function named `loadPageInto`,&lt;/span&gt;
      &lt;span class="c1"&gt;// Which might render some HTML into the body&lt;/span&gt;
      &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loadPageInto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`
        &amp;lt;p&amp;gt;404 page not found&amp;lt;/p&amp;gt;
      `&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;I basically just implemented a router in 10 lines of code.&lt;/strong&gt; (yes, that's a &lt;em&gt;serious&lt;/em&gt; overstatement, but it's closer than you might think).&lt;/p&gt;

&lt;p&gt;This falls into &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/Code_splitting"&gt;code splitting&lt;/a&gt;, aka loading "components" (or modules) of code whenever the user needs them. Back in the dark ages of bundle all-the-things, you'd have to load all these components up front. This could mean &lt;em&gt;thousands&lt;/em&gt; of lines of dead code!&lt;/p&gt;

&lt;h2&gt;
  
  
  So wait, it's 2021... why does all my tooling look the same?
&lt;/h2&gt;

&lt;p&gt;This was certainly my first question when I read up on this. I recently graduated from &lt;a href="https://create-react-app.dev"&gt;&lt;code&gt;create-react-app&lt;/code&gt;&lt;/a&gt; to &lt;a href="https://nextjs.org"&gt;NextJS&lt;/a&gt; as my React boilerplate go-to, but there's still that same webpack configuration + bundle process to think about 🤷‍♀️&lt;/p&gt;

&lt;p&gt;A lot of this is just the curse of abstraction. Looking under the hood, these tools have made &lt;em&gt;great&lt;/em&gt; strides since ES modules hit the scene. Namely, tools like NextJS can magically "split" your React app into bite-sized chunks that get loaded as-needed. This means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;only load the JS for a page &lt;strong&gt;when you actually visit that page&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;only load React components &lt;strong&gt;when they actually need to display&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;(bonus) pre-fetch JS &lt;strong&gt;when someone is &lt;em&gt;likely&lt;/em&gt; to need it.&lt;/strong&gt; This is a more advanced feature (&lt;a href="https://nextjs.org/docs/api-reference/next/link#if-the-route-has-dynamic-segments"&gt;documented here&lt;/a&gt;), but it lets you do all sorts of craziness; say, grabbing resources for a page when you hover over  link&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There's also the benefit of &lt;strong&gt;backwards compatibility&lt;/strong&gt; when using a bundler. For instance, Internet Explorer has no concept of "modules" or "import" statements, so any attempt to code split will blow up in your face 😬 But with a meta-framework like NextJS by your side, you can polyfill such use cases without having to think about it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Approaching the post-IE age
&lt;/h2&gt;

&lt;p&gt;If you haven't heard, a major announcement sent ripples through the web dev community recently: &lt;strong&gt;&lt;a href="https://techcommunity.microsoft.com/t5/microsoft-365-blog/microsoft-365-apps-say-farewell-to-internet-explorer-11-and/ba-p/1591666"&gt;Microsoft will officially drop IE 11 support for its products in August 2021&lt;/a&gt;&lt;/strong&gt; 😱&lt;/p&gt;

&lt;p&gt;Many are treating this as the ticking timebomb for legacy browser support. When it goes off... we might be safe to lose our polyfills for good. Yes, certain sites for governments and internal business operations will probably stick to their PHP-laced guns. But for us bleeding-edge developers, we may have a whole new frontier to explore 🚀&lt;/p&gt;

&lt;h3&gt;
  
  
  A world of bundlers that... don't bundle
&lt;/h3&gt;

&lt;p&gt;The tides have certainly shifted in the JS bundler community in the past year. With the prospect of dropping polyfills and aggressive bundling for good, people started turning to the &lt;em&gt;real&lt;/em&gt; reasons you want a bundler:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;To process all your fanciness that &lt;em&gt;isn't&lt;/em&gt; valid JS.&lt;/strong&gt; Think JSX for React components, TypeScript for type checking, Styled Components and CSS modules for CSS-in-JS, etc etc.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;To spin up your app locally.&lt;/strong&gt; You could always open HTML files in your browser directly, but you'll loose all that immediate feedback! You should see all your new JS and CSS the millisecond you hit "save."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;To optimize code for production.&lt;/strong&gt; You'll probably want some last-minute stripping for added speed, like removing &lt;code&gt;console.log&lt;/code&gt;s, minifying everything, linting, and so on.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because of this refined feature set, the new wave of JS processors are just calling themselves "build tools" to stay more generalized.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.snowpack.dev"&gt;&lt;strong&gt;Snowpack&lt;/strong&gt;&lt;/a&gt; is really what got the ball rolling from my perspective. They promise all the selling points I listed above, plus the absolute fastest live-reloading in the biz. This is mainly because of that code splitting I mentioned earlier. Since they leave all those modules and dynamic imports in-tact, &lt;strong&gt;they avoid re-processing the JavaScript that didn't change.&lt;/strong&gt; So if you just updated a single React component, it'll reprocess those 10 lines of code and blast it onto the page in a flash ⚡️&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/vitejs/vite"&gt;&lt;strong&gt;Vite&lt;/strong&gt;&lt;/a&gt; is a major contender to note as well. This one was spearheaded by &lt;a href="https://twitter.com/youyuxi?ref_src=twsrc%5Egoogle%7Ctwcamp%5Eserp%7Ctwgr%5Eauthor"&gt;Evan You&lt;/a&gt; (the overlord of VueJS) to tackle a similar feature set to Snowpack. It's far too early to say whether I'd &lt;em&gt;prefer&lt;/em&gt; this setup to Snowpack, but &lt;a href="https://blog.logrocket.com/vite-vs-snowpack-a-comparison-of-frontend-build-tools/"&gt;here's a nice comparison piece&lt;/a&gt; if you're considering either for serious apps.&lt;/p&gt;

&lt;p&gt;There's also the crazy world of using different programming languages to process your code. &lt;a href="https://github.com/evanw/esbuild"&gt;ESBuild&lt;/a&gt; is a big contender right now, using GoLang to process JavaScript in no time flat.&lt;/p&gt;

&lt;h3&gt;
  
  
  Call to action: explore these new build tools!
&lt;/h3&gt;

&lt;p&gt;It's definitely worth your time to whip up a sandbox and start compiling some code. &lt;a href="https://github.com/vitejs/vite/tree/main/packages/create-app"&gt;Vite's &lt;code&gt;create-app&lt;/code&gt; tool&lt;/a&gt; is a great one for it's beginner friendliness, with options to use any major framework out-of-the-box (React, Vue, Svelte, and even &lt;a href="https://lit-element.polymer-project.org"&gt;Lit Element&lt;/a&gt;!).&lt;/p&gt;

&lt;p&gt;I was caught off guard to find &lt;strong&gt;there's no build directory&lt;/strong&gt; when working in development. The code your write gets mapped to the browser directly, processed on-the-fly whenever you save ❤️&lt;/p&gt;

&lt;p&gt;So go forth and see what the future is like! With any luck, we'll get to have our bundle pancake and eat it too 😁&lt;/p&gt;

&lt;h2&gt;
  
  
  Learn a little something?
&lt;/h2&gt;

&lt;p&gt;Awesome. In case you missed it, I launched an &lt;a href="https://tinyletter.com/bholmesdev"&gt;my "web wizardry" newsletter&lt;/a&gt; to explore more knowledge nuggets like this!&lt;/p&gt;

&lt;p&gt;This thing tackles the &lt;a href="https://www.swyx.io/first-principles-approach/"&gt;"first principles"&lt;/a&gt; of web development. In other words, what are all the janky browser APIs, bent CSS rules, and semi-accessible HTML that make all our web projects tick? If you're looking to go &lt;em&gt;beyond the framework&lt;/em&gt;, this one's for you dear web sorcerer 🔮&lt;/p&gt;

&lt;p&gt;&lt;a href="https://tinyletter.com/bholmesdev"&gt;Subscribe away right here&lt;/a&gt;. I promise to always teach and never spam ❤️&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>I'm building a new home outside of DEV. Here's why (and where to find me!)</title>
      <dc:creator>Ben Holmes</dc:creator>
      <pubDate>Wed, 31 Mar 2021 20:50:07 +0000</pubDate>
      <link>https://dev.to/bholmesdev/announcing-my-new-whimsical-sexy-and-informative-self-hosted-blog-3kc8</link>
      <guid>https://dev.to/bholmesdev/announcing-my-new-whimsical-sexy-and-informative-self-hosted-blog-3kc8</guid>
      <description>&lt;p&gt;Greetings fellow members of the DEV community! If you're one of my followers or someone new looking for excitement, thanks for clicking 👋&lt;/p&gt;

&lt;p&gt;Before getting to the good stuff, I want to sing some praises for the DEV platform. I've been a blogger over here since early 2019, and I can't &lt;em&gt;believe&lt;/em&gt; how much this community has helped me grow.  If it wasn't for their willingness to promote and &lt;a href="https://dev.to/nickraphael/how-to-use-suggest-a-tweet-effectively-1ncf"&gt;retweet new authors&lt;/a&gt;, I probably would've drowned in the sea of Medium and Hacker News algorithms long ago. This is why I believe that &lt;strong&gt;DEV is &lt;em&gt;the&lt;/em&gt; reason I'm so passionate about blogging today.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That said, I think I've reached a point where I need &lt;strong&gt;start hosting all this content&lt;/strong&gt; on my own domain as well. I've advertised &lt;a href="https://bholmes.dev"&gt;my personal site&lt;/a&gt; a few times on here, and I'm super excited to be bringing a blog component into the mix 🥳&lt;/p&gt;

&lt;h2&gt;
  
  
  Why self-host at all?
&lt;/h2&gt;

&lt;p&gt;Fair question! I've found quite an audience here on the DEV platform, and my SEO &lt;a href="https://twitter.com/BHolmesDev/status/1377028248253706240?s=20"&gt;certainly hasn't suffered&lt;/a&gt; by hosting on here.&lt;/p&gt;

&lt;p&gt;But there's a few consequences to &lt;em&gt;not&lt;/em&gt; owning all your web content I want to address:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;SEO for your personal site suffers.&lt;/strong&gt; It's pretty simple really: the more popular webpages there are attached to a domain, the more popular that domain becomes. But as it stands... my personal site has no association with my popular posts! So no matter how many views I may get, searching "Ben Holmes" still comes up a baseball player and a saxophone player long before &lt;a href="https://bholmes.dev"&gt;https://bholmes.dev&lt;/a&gt; 😬 (&lt;em&gt;and yes, I know Ben Holmes is as basic a name as they come. So I have quite the uphill battle&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Having a "home base" helps with cross-posting.&lt;/strong&gt; By having a base domain to share from, you can cross-post to any number of extra platforms for free using &lt;a href="https://dev.to/michaelburrows/comment/125j0"&gt;canonical URLs&lt;/a&gt;. These let you claim all that SEO goodness for your own domain while people read on the platform of their choice.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;If the platform dies out, so do you.&lt;/strong&gt; Don't get me wrong here; I'm rooting for DEV's future as a platform more than anyone! But if you've seen &lt;a href="https://forum.freecodecamp.org/t/we-just-launched-developer-news-heres-how-you-can-use-it/279929"&gt;what's happened to the Medium developer community&lt;/a&gt; over the years, you know that the brightest stars can die out for &lt;em&gt;any&lt;/em&gt; reason. So it's good to have somewhere that you own so you can quickly pivot between platforms at a moment's notice.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This advice isn't unique to me by the way! I'd say these are compelling reasons for anyone to start self-hosting.&lt;/p&gt;

&lt;h2&gt;
  
  
  So wait... are you leaving DEV?
&lt;/h2&gt;

&lt;p&gt;Absolutely not! In fact, I'll probably keep posting content that's &lt;em&gt;specific&lt;/em&gt; to the DEV platform.&lt;/p&gt;

&lt;p&gt;Discussion threads using the &lt;strong&gt;#discuss&lt;/strong&gt; tag are a big one for me. I won't have a comments section on my personal site (for now at least), so quickly polling the DEV community for ideas is a huge win I won't be giving up.&lt;/p&gt;

&lt;p&gt;I also learn a lot from the comments community members leave on here. I've received multiple requests to edit and clarify certain points further which I wouldn't expect to receive on other platforms. &lt;/p&gt;

&lt;p&gt;Lastly... &lt;strong&gt;I love that unicorn button.&lt;/strong&gt; How could I abandon that? 🦄&lt;/p&gt;

&lt;h2&gt;
  
  
  So what's to come?
&lt;/h2&gt;

&lt;p&gt;Well I'll tell you one thing: I won't keep writing the same old content on this new home. Expect some new features and experiments going forward:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://tinyletter.com/bholmesdev"&gt;A newsletter&lt;/a&gt; to share out my learnings&lt;/strong&gt; across more mediums than a static post. This could be video snippets I record, podcasts / blogs / talks / random projects I find interesting, scrappy notes that aren't &lt;em&gt;quite&lt;/em&gt; blog-ready, and more. I also want a outlet to share some music recs, so get ready for some bangers 🎧&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exploration of &lt;em&gt;interactive&lt;/em&gt; posts.&lt;/strong&gt; Though I don't have immediate plans, it would be sweet to join the &lt;a href="https://mdxjs.com/"&gt;MDX bandwagon&lt;/a&gt; and start embedding code + components right in the post you're reading. I'm deeply inspired by teachers like &lt;a href="https://www.joshwcomeau.com/animation/3d-button/"&gt;Josh Comeau&lt;/a&gt; in this area, and love the flexibility my personal platform could bring.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A place for scrappier ideas and drafts.&lt;/strong&gt; I experimented with sharing my notes with you directly over on my &lt;strong&gt;&lt;a href="https://bholmes.dev/conference-talks-that-changed-my-perspective-as-a-web-dev"&gt;4 Conference Talks&lt;/a&gt;&lt;/strong&gt; post, and I'm really encouraged by the warm response! But I have &lt;em&gt;so&lt;/em&gt; many more notes to share that don't fit into the "super inspirational" bucket. I'm hoping I can take my personal notebook (using &lt;a href="https://bholmes.dev/giving-up-on-notion-to-build-a-second-brain"&gt;Foam + VS Code&lt;/a&gt; by the way) and turn it into a public garden of my learnings you can browse. It's a pretty new idea I've seen creators like &lt;a href="https://www.swyx.io/ideas/"&gt;Swyx&lt;/a&gt; pick up; could be worth a shot!&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Call to action: join the newsletter 📫
&lt;/h2&gt;

&lt;p&gt;To my &lt;strong&gt;7137&lt;/strong&gt; followers on this platform (I still can't believe I can say that 😨), don't worry! The content will keep rolling your way over here.&lt;/p&gt;

&lt;p&gt;But if you're interested in getting the full &lt;strong&gt;Ben Blog Experience (TM)&lt;/strong&gt; or just want to support me on this new journey, I'd really appreciate the &lt;a href="https://tinyletter.com/bholmesdev"&gt;newsletter subscription&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;No, I will &lt;em&gt;not&lt;/em&gt; be flooding your inbox with mindless content. I plan to keep updates short, informative, and relatively infrequent (once every 2-3 weeks is my target right now). I'll also be sure that unsubscribe button is within reach should you need it 😁&lt;/p&gt;

&lt;p&gt;I've also written a shiny new post &lt;a href="https://bholmes.dev/blog/how-es-modules-have-redefined-web-development/"&gt;over here&lt;/a&gt; that you won't want to miss: &lt;strong&gt;how ES Modules have redefined web development.&lt;/strong&gt; I'm keeping this post &lt;em&gt;exclusive&lt;/em&gt; to my personal site until next Wednesday (when I'll cross-post over here). So to grab this top-secret-early-access preview, go give it a read 👀&lt;/p&gt;

&lt;p&gt;&lt;a href="https://tinyletter.com/bholmesdev"&gt;&lt;strong&gt;✨ Sign up for the newsletter ✨&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://bholmes.dev/blog/"&gt;&lt;strong&gt;📝 Explore the shiny new blog 📝&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks as always for your readership, and have a great day!&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>webdev</category>
    </item>
    <item>
      <title>4 Git shortcuts that define my workflow</title>
      <dc:creator>Ben Holmes</dc:creator>
      <pubDate>Thu, 18 Feb 2021 23:38:56 +0000</pubDate>
      <link>https://dev.to/bholmesdev/4-git-shortcuts-that-define-my-workflow-1fem</link>
      <guid>https://dev.to/bholmesdev/4-git-shortcuts-that-define-my-workflow-1fem</guid>
      <description>&lt;p&gt;I started my first full-time job as a programmer this year, which means I've been picking up &lt;em&gt;a lot&lt;/em&gt; of new tricks in a pretty quick timeframe. Naturally, I started automating the workflows I use day-in and day-out 😁&lt;/p&gt;

&lt;p&gt;Stop me if you've seen this workflow before:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I picked up a ticket on JIRA&lt;/li&gt;
&lt;li&gt;I need to pull the latest main branch&lt;/li&gt;
&lt;li&gt;I need to checkout a new branch&lt;/li&gt;
&lt;li&gt;I need to push that branch to origin before opening my PR&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I'll probably do this 5+ times in one day if we're in a bug-squashing flow. But when I'm hurrying, it's so easy to either &lt;strong&gt;a)&lt;/strong&gt; work off an old "main" branch, or &lt;strong&gt;b)&lt;/strong&gt; do the &lt;strong&gt;copy-paste of shame&lt;/strong&gt; before your PR:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;To push the current branch and set the remote as upstream, use

    git push --set-upstream origin crap-not-again
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;You know you cringe a little every time this pops up&lt;/em&gt; 😬&lt;/p&gt;

&lt;p&gt;So let's discuss:&lt;/p&gt;

&lt;p&gt;🍾 The importance of pushing and popping your stash&lt;/p&gt;

&lt;p&gt;🙈 The joys of &lt;code&gt;upstream HEAD&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;🧶 Writing aliases for your terminal to tie it all together&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Onwards!&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The fantastic 4 aliases
&lt;/h2&gt;

&lt;p&gt;Alright, let's do the big reveal. Here's my 4 shortcuts for slamming through daily tasks 💪&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Stash what I'm working on and checkout the latest master&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;gimme&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"git stash push -u &amp;amp;&amp;amp; git checkout main &amp;amp;&amp;amp; git pull -r"&lt;/span&gt;

&lt;span class="c"&gt;# Grab the latest master and come back to the branch I was on (with stashing!)&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;yoink&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"gimme &amp;amp;&amp;amp; git checkout - &amp;amp;&amp;amp; git stash pop"&lt;/span&gt;

&lt;span class="c"&gt;# Checkout a new branch and push it to origin (so I don't forget that set-upstream)&lt;/span&gt;
woosh&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  git checkout &lt;span class="nt"&gt;-b&lt;/span&gt; &lt;span class="nv"&gt;$1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git push &lt;span class="nt"&gt;-u&lt;/span&gt; origin HEAD
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# ALL TOGETHER NOW&lt;/span&gt;
&lt;span class="c"&gt;# Stash my current WIP, checkout a new branch off the latest master, and push it to origin&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;boop&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"gimme &amp;amp;&amp;amp; woosh"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FHolben888%2Fpersonal-blog%2Fmain%2Fget-s%2A%2A%2A-done-2020%2F1-fantastic-4-snippets.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FHolben888%2Fpersonal-blog%2Fmain%2Fget-s%2A%2A%2A-done-2020%2F1-fantastic-4-snippets.gif" alt="1-fantastic-4-snippets"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These commands are written in &lt;code&gt;bash&lt;/code&gt;. So if you want to use these in your own terminal, go find your &lt;code&gt;bashrc&lt;/code&gt; and paste away! This is usually found in your user directory: &lt;code&gt;~/.bashrc&lt;/code&gt;. And for all you windows users out there: you'll probably need to &lt;a href="https://git-scm.com/downloads" rel="noopener noreferrer"&gt;install the Git Bash terminal&lt;/a&gt; (included with Git) and create the file &lt;a href="https://stackoverflow.com/questions/6883760/git-for-windows-bashrc-or-equivalent-configuration-files-for-git-bash-shell" rel="noopener noreferrer"&gt;like so&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✋ But wait!&lt;/strong&gt; Before you blindly copy these, let's unpack each of them a little.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;gimme&lt;/code&gt; ✊
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;gimme&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"git stash push -u &amp;amp;&amp;amp; git checkout main &amp;amp;&amp;amp; git pull -r"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is my "return to home base" command. It assumes you might &lt;em&gt;not&lt;/em&gt; be on the main branch just yet, so it'll stash everything you're working on using the &lt;code&gt;stash&lt;/code&gt; command. It also saves "untracked" / new files with the &lt;code&gt;-u&lt;/code&gt; flag.&lt;/p&gt;

&lt;p&gt;Next, it checks out your &lt;code&gt;main&lt;/code&gt; branch and pulls the latest contents. That &lt;code&gt;-r&lt;/code&gt; flag will be sure to "rebase" onto the latest, preventing unecessary merge conflicts.&lt;/p&gt;

&lt;p&gt;This is the simplest command of the bunch, but it's also my most-used! On my team, we run a lot of automated scripts on &lt;code&gt;pull&lt;/code&gt; for installing dependencies and pulling content from our &lt;a href="https://www.optimizely.com/optimization-glossary/content-management-system/" rel="noopener noreferrer"&gt;CMS&lt;/a&gt;. So, it's super useful to return to &lt;code&gt;main&lt;/code&gt; and grab all this new content in one go.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;yoink&lt;/code&gt; 🎣
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;yoink&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"gimme &amp;amp;&amp;amp; git checkout - &amp;amp;&amp;amp; git stash pop"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This phrase is certainly a close relative to &lt;code&gt;gimme&lt;/code&gt;. For some etymology, I'll include the &lt;a href="https://www.urbandictionary.com/define.php?term=Yoink" rel="noopener noreferrer"&gt;urban dictionary entry&lt;/a&gt; on what it means to &lt;em&gt;yoink:&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;An exclamation that, when uttered in conjunction with taking an object, immediately transfers ownership from the original owner to the person using the word regardless of previous property rights.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;...In other words, we are grabbing the main branch and bringing it back to the branch we're on 😁 We pull this off using the &lt;code&gt;git checkout -&lt;/code&gt; command after running &lt;code&gt;gimme&lt;/code&gt;. This is super convenient for grabbing the latest changes to &lt;a href="https://egghead.io/lessons/git-push-a-rebased-local-branch-by-using-force-with-lease" rel="noopener noreferrer"&gt;force rebase the branch&lt;/a&gt; we're working on.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;💡 Note:&lt;/strong&gt; is easy to achieve with a &lt;code&gt;fetch&lt;/code&gt; command as well:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git fetch origin master:master
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...but if your team runs automated scripts whenever you &lt;code&gt;pull&lt;/code&gt;, you'll probably want the original command I described.&lt;/p&gt;

&lt;h3&gt;
  
  
  Clarification on using &lt;code&gt;stash&lt;/code&gt; vs &lt;code&gt;stash push&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;You also may be new to using &lt;code&gt;push&lt;/code&gt; and &lt;code&gt;pop&lt;/code&gt; on your stash, instead of &lt;code&gt;apply&lt;/code&gt; and &lt;code&gt;clear&lt;/code&gt; (as I often used to do). Here's the distinction:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Whenever you run "vanilla" &lt;code&gt;git stash&lt;/code&gt;, &lt;strong&gt;you're &lt;em&gt;merging&lt;/em&gt; your current changes&lt;/strong&gt; with all the other changes in the stash. Think of this like throwing all your belongings in a bag, and &lt;code&gt;git stash apply&lt;/code&gt; is like dumping everything out onto the floor.&lt;/li&gt;
&lt;li&gt;Whenever you run &lt;code&gt;git stash push&lt;/code&gt;, you're &lt;strong&gt;putting your current changes in a &lt;em&gt;separate compartment&lt;/em&gt;&lt;/strong&gt; from all the other changes in the stash. Think of this like choosing a different bag pocket every time you go to "stash" something. Then, when you &lt;code&gt;pop&lt;/code&gt;, you pull from the last pocket you just used.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the reason we use &lt;code&gt;push&lt;/code&gt; inside the &lt;strong&gt;&lt;code&gt;gimme&lt;/code&gt; command,&lt;/strong&gt; and &lt;code&gt;pop&lt;/code&gt; inside the &lt;strong&gt;&lt;code&gt;yoink&lt;/code&gt; command.&lt;/strong&gt; It guarantees that we grab the same thing we just stashed, without getting mixed up with anything &lt;em&gt;already in&lt;/em&gt; your stash. If you've ever dug around your bag trying to find one tiny thing stuck at the bottom, you know what I'm talking about here 😁&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;woosh&lt;/code&gt; 💨
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;woosh&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  git checkout &lt;span class="nt"&gt;-b&lt;/span&gt; &lt;span class="nv"&gt;$1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git push &lt;span class="nt"&gt;-u&lt;/span&gt; origin HEAD
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the solution to your &lt;code&gt;--set-upstream&lt;/code&gt; woes. Instead of pushing to origin later, this lets you check out a new branch and push &lt;em&gt;right away&lt;/em&gt; so you don't forget.&lt;/p&gt;

&lt;p&gt;Yes, there's some cases when you don't want your local branch on the remote just yet, but this is pretty rare in my experience. I often create a branch corresponding to a given task on our Kanban board that I'm ready to push up right away.&lt;/p&gt;

&lt;p&gt;And if you've never seen the &lt;code&gt;HEAD&lt;/code&gt; parameter before... &lt;strong&gt;remember that one!&lt;/strong&gt; It's a super slick way to auto-fill the name of your current branch instead of typing it out by hand 🔥&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;boop&lt;/code&gt; 👇
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;boop&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"gimme &amp;amp;&amp;amp; woosh"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Shout out to "lord of boops" &lt;a href="https://www.jason.af" rel="noopener noreferrer"&gt;Jason Lengstorf&lt;/a&gt; for the inspiration&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This command goes full-circle. If you're somehow too lazy to check out the latest &lt;code&gt;main&lt;/code&gt; branch before starting anew, this will let you &lt;code&gt;gimme&lt;/code&gt; and &lt;code&gt;woosh&lt;/code&gt; all in one go!&lt;/p&gt;

&lt;p&gt;Not much to explain with this one. Just go forth into perfect boop-dom.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learn a little something?
&lt;/h2&gt;

&lt;p&gt;Awesome. In case you missed it, I launched an &lt;a href="https://tinyletter.com/bholmesdev" rel="noopener noreferrer"&gt;my "web wizardry" newsletter&lt;/a&gt; to explore more knowledge nuggets like this!&lt;/p&gt;

&lt;p&gt;This thing tackles the &lt;a href="https://www.swyx.io/first-principles-approach/" rel="noopener noreferrer"&gt;"first principles"&lt;/a&gt; of web development. In other words, what are all the janky browser APIs, bent CSS rules, and semi-accessible HTML that make all our web projects tick? If you're looking to go &lt;em&gt;beyond the framework&lt;/em&gt;, this one's for you dear web sorcerer 🔮&lt;/p&gt;

&lt;p&gt;&lt;a href="https://tinyletter.com/bholmesdev" rel="noopener noreferrer"&gt;Subscribe away right here&lt;/a&gt;. I promise to always teach and never spam ❤️&lt;/p&gt;

</description>
      <category>programming</category>
      <category>git</category>
      <category>bash</category>
    </item>
    <item>
      <title>4 Conference talks that changed my perspective as a web dev</title>
      <dc:creator>Ben Holmes</dc:creator>
      <pubDate>Wed, 10 Feb 2021 21:44:22 +0000</pubDate>
      <link>https://dev.to/bholmesdev/conference-talks-that-changed-my-perspective-as-a-web-dev-lfp</link>
      <guid>https://dev.to/bholmesdev/conference-talks-that-changed-my-perspective-as-a-web-dev-lfp</guid>
      <description>&lt;p&gt;I've resolved to make 2021 my biggest year of learning yet. My blogging game has been pretty sporadic up until now... but I've finally decided to go all-in on the &lt;a href="https://www.swyx.io/learn-in-public/"&gt;learning in public mantra&lt;/a&gt; with 1 post a week!&lt;/p&gt;

&lt;p&gt;I used to believe blog posts had to be shining nuggets of wisdom only I could find. But with all the notes I take on a weekly basis, why not share my journal with the world too? 😁&lt;/p&gt;

&lt;p&gt;So, here's some of the most impactful conference talks I've found in the last year or so. I hope to check-in with more entries in the future, and I'd love to &lt;strong&gt;hear your own favorite talks&lt;/strong&gt; in the comments!&lt;/p&gt;

&lt;h2&gt;
  
  
  Rich Hickey - Simple Made Easy 🧶
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.infoq.com/presentations/Simple-Made-Easy/"&gt;&lt;strong&gt;🎥 View the talk + transcript here&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you've ever heard the someone say that something is "easy, but not simple," this is probably the talk they're referencing. This is an easy recommend to programmers in general (not just web devs). That said, I think this talk is &lt;em&gt;especially&lt;/em&gt; relevant to frontend-ers nowadays with all the tools at our disposal. &lt;/p&gt;

&lt;p&gt;It feels like web frameworks and "best practices" are moving towards some powerful new opinions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Everything is a component&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Functional programming is king&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;State management is complex&lt;/strong&gt; and deserves a rethink (&lt;a href="https://www.youtube.com/watch?v=hiT4Q1ntvzg"&gt;hello state machines&lt;/a&gt; 👋)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;☝️ These are the points Rich was getting at with this talk &lt;em&gt;a decade ago!&lt;/em&gt; This is why I've resisted this talk multiple times throughout my web dev journey. As a junior getting comfortable with enterprise-grade React apps, it's helped me understand the &lt;em&gt;why&lt;/em&gt; behind my team's architectural decisions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Personal notes
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Simple&lt;/strong&gt; is an objective measure, no matter the person, reflecting how many &lt;strong&gt;interwoven pieces&lt;/strong&gt; (complexity) there are in a given system&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Easy&lt;/strong&gt; is relative to every individual, reflecting how "familiar" or "near at hand" something feels&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Agile programming encourages us to move fast without taking a step back&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Complexity and tech debt pile up when we ignore signs of bad abstraction&lt;/li&gt;
&lt;li&gt;Favorite quote of the talk: &lt;em&gt;"But programmers fire the starting pistol every 100 yards and call it a (new) sprint!"&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;

&lt;p&gt;Replace &lt;strong&gt;complecting&lt;/strong&gt; (knotted-up code with lots of interdependent pieces) with &lt;strong&gt;composing&lt;/strong&gt; (modularity a la modern frontend frameworks)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Think building a castle from Legos instead of a "knitted castle" from interweaving&lt;/li&gt;
&lt;li&gt;Separate horizontally, stratify vertically&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;

&lt;p&gt;Concrete improvements to make&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;"Stateful" variables complect &lt;em&gt;values&lt;/em&gt; with &lt;em&gt;change overtime&lt;/em&gt;&lt;/strong&gt;; make transition from one state to another predictable (see &lt;a href="https://www.youtube.com/watch?v=hiT4Q1ntvzg"&gt;state machines&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stay close to what the system does;&lt;/strong&gt; consider the &lt;em&gt;behavior&lt;/em&gt; over &lt;em&gt;implementation details&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Example: &lt;a href="https://www.freecodecamp.org/news/imperative-vs-declarative-programming-difference/"&gt;Declarative over imperative programming&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Example: test-driven-development done right (&lt;a href="https://www.youtube.com/watch?v=EZ05e7EMOLM"&gt;incredible talk by Ian Cooper here&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Work with &lt;em&gt;rules&lt;/em&gt; over conditional / switch case spaghetti&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Given some data X, here's some rules to make it become Y&lt;/li&gt;
&lt;li&gt;Lean on &lt;a href="https://www.freecodecamp.org/news/what-is-a-pure-function-in-javascript-acb887375dfe/"&gt;pure functions&lt;/a&gt;, which give you a consistent output for a given input&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Dan Abramov - The Wet Codebase 🌊
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.deconstructconf.com/2019/dan-abramov-the-wet-codebase"&gt;&lt;strong&gt;🎥 View the talk + transcript here&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's another architecture-y talk that also reaches far beyond web development. If you're unfamiliar with &lt;a href="https://overreacted.io"&gt;Dan Abramov&lt;/a&gt;, he's one of the most prolific members of the React core team from a teaching standpoint alone. So if you want advice on architecting your web apps, this is your guy 😁&lt;/p&gt;

&lt;p&gt;This talk goes hand-in-hand with &lt;a href="https://kentcdodds.com/blog/aha-programming"&gt;Kent C Dodd's post on "AHA programming"&lt;/a&gt;. Generally, they're both addressing the biggest pitfall of component-based thinking: copy / paste feels like bad practice, so you abstract &lt;em&gt;every&lt;/em&gt; piece of logic to its own little file.&lt;/p&gt;

&lt;p&gt;Sure there's a place for abstraction, but there's &lt;em&gt;also&lt;/em&gt; a place for duplication! This talk has a lot of examples and funny quotables to keep things light; definitely worth the watch.&lt;/p&gt;

&lt;h3&gt;
  
  
  Personal notes
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;If left unchecked, abstractions can become &lt;em&gt;Frankenstein&lt;/em&gt; code overtime

&lt;ul&gt;
&lt;li&gt;An abstraction &lt;em&gt;almost&lt;/em&gt; fits are use case, but not quite 👉 we wittle away that round hole to fit our square peg&lt;/li&gt;
&lt;li&gt;When bugs arise for &lt;em&gt;one&lt;/em&gt; use case, we introduce fixes affecting &lt;em&gt;tons&lt;/em&gt; of other use cases&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;👍 When abstraction is good

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Makes your code more declarative&lt;/strong&gt; / focus on a specific intent (see that Rich Hickey talk above 😉)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Avoids missed bug fixes&lt;/strong&gt; 👉 fix it once, fix everywhere&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;👎 When abstraction is bad

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Creates coupling&lt;/strong&gt; - when it doesn't &lt;em&gt;quite&lt;/em&gt; fit, you can create a monster of refactors&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Adds indirection&lt;/strong&gt; - creates layers and layers overtime; &lt;em&gt;"We avoid spaghetti code, but we create lasagna code"&lt;/em&gt; 🇮🇹&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Ways to improve going forward

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Test code that &lt;em&gt;uses&lt;/em&gt; an abstraction&lt;/strong&gt;, not the abstraction itself&lt;/li&gt;
&lt;li&gt;If you remove that abstraction later, your tests explode!&lt;/li&gt;
&lt;li&gt;Abstractions are just another implementation detail (again, &lt;a href="https://www.youtube.com/watch?v=EZ05e7EMOLM"&gt;TDD is king&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Don't commit to abstraction layers until you need them;&lt;/strong&gt; &lt;em&gt;"If a girl is into the same obscure bands as you are... that doesn't mean you're meant to be together"&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Be ready to remove abstractions later;&lt;/strong&gt; Be the one asking &lt;em&gt;"Please inline this abstraction!"&lt;/em&gt; in a PR review!&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Rich Harris - Rethinking Reactivity ⚛️
&lt;/h2&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/AdNJ3fydeao"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;In my opinion, this is &lt;strong&gt;the biggest bombshell to drop&lt;/strong&gt; since React was first revealed 💣&lt;/p&gt;

&lt;p&gt;A trigger warning is probably in order here: if you're a diehard React follower, this talk questions many practice React holds dear (including the virtual DOM itself!).&lt;/p&gt;

&lt;p&gt;But even if you disagree with Rich's points, this talk is a &lt;em&gt;serious&lt;/em&gt; landmark in the web framework canon. It also exposes what "bundlers," "compilers," and "reacting to change" all &lt;em&gt;really&lt;/em&gt; mean under the hood. If you aren't convert to a Svelte fan after this, you'll at least understand where the web has been, and where it might be heading!&lt;/p&gt;

&lt;h3&gt;
  
  
  Personal notes
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What is reactive programming?&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;It all started with spreadsheets&lt;/li&gt;
&lt;li&gt;I change a value in one cell, and other cells "react" to those changes with formulas&lt;/li&gt;
&lt;li&gt;Earliest example of &lt;strong&gt;only re-rendering values that change&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;It's 1) about tracking values and 2) updating &lt;em&gt;dependents&lt;/em&gt; on that value&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Problem with React's model&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;When state changes in a component, that &lt;em&gt;whole component&lt;/em&gt; re-evaluates itself from the top&lt;/li&gt;
&lt;li&gt;Treats your HTML like a black box; apply the change, then diff against the previous chunk&lt;/li&gt;
&lt;li&gt;Really, React doesn't know about your "state values" or how they affect the DOM!&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bad signs for efficiency:&lt;/strong&gt; I shouldn't need &lt;code&gt;useMemo&lt;/code&gt;, &lt;code&gt;useCallback&lt;/code&gt;, &lt;code&gt;shouldComponentUpdate&lt;/code&gt;, etc&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Instead of opting &lt;em&gt;out&lt;/em&gt; of reevaluating state (a la &lt;code&gt;useMemo&lt;/code&gt;), we could opt &lt;em&gt;in&lt;/em&gt; by flagging state variables that depend on other state variables

&lt;ul&gt;
&lt;li&gt;Much like a spreadsheet; write formulas that flag which variables affect a given value&lt;/li&gt;
&lt;li&gt;Svelte uses a custom &lt;code&gt;$:&lt;/code&gt; operator to "flag" state that is computed from &lt;em&gt;other&lt;/em&gt; state &lt;a href="https://svelte.dev/tutorial/reactive-declarations"&gt;(example here)&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Svelte is a compiler, not a runtime&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;In other words, a "Svelte" component compiles to JS your browser understand&lt;/li&gt;
&lt;li&gt;No "runtime" (like React's virtual DOM) needs to get imported&lt;/li&gt;
&lt;li&gt;Also means Svelte can &lt;strong&gt;bend the JS language to its will&lt;/strong&gt; &lt;/li&gt;
&lt;li&gt;Evan You, creator of VueJS - Svelte is too magical, since it lets you write JavaScript that isn't totally valid &lt;/li&gt;
&lt;li&gt;Rich Harris' response - this opinion is like believing HTML, CSS and JS should live in separate files. CSS-in-JS is weird too, so what's wrong with this?&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Some other cool demos of Svelte

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://css-tricks.com/what-i-like-about-writing-styles-with-svelte/"&gt;&lt;strong&gt;CSS scoping by component&lt;/strong&gt;&lt;/a&gt; just by using a &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; tag&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://svelte.dev/tutorial/transition"&gt;&lt;strong&gt;Transition directives&lt;/strong&gt;&lt;/a&gt; with sensible out-of-the-box options&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Shawn "swyx" Wang - Getting Closure on React Hooks 🎣
&lt;/h2&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/KJP1E-Y-xyo"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;This is a fast-paced and code-heavy talk, so you'll probably want 1x speed on this one. &lt;/p&gt;

&lt;p&gt;That said... this is &lt;em&gt;the most&lt;/em&gt; enlightening talk I've seen on React. Period. It's only 30 minutes long, but it gave me holistic understanding on how React hooks, state management, and re-rendering all work together. It also shows some huge use cases for "closure" in JS. If you have a web dev interview coming up, point to this! 😉&lt;/p&gt;

&lt;h3&gt;
  
  
  Personal notes
&lt;/h3&gt;

&lt;p&gt;Hard to write a succinct, bulleted list for this one. So, I just annotated the finished product to explain how everything works. Fair warning: it's a &lt;em&gt;lot&lt;/em&gt; to take in!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://codepen.io/bholmesdev/pen/YzpWGaz?editors=0011"&gt;🚀 Functioning codepen to see it in action&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// create an array for all the state variables in our "React app"&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;stateValues&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="c1"&gt;// start our state index at 0. We'll use this&lt;/span&gt;
  &lt;span class="c1"&gt;// to throw state into that array ☝️ everytime someone calls "useState"&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;initValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// state should be set to either:&lt;/span&gt;
    &lt;span class="c1"&gt;// 1. the initial value (aka React.useState(initValue))&lt;/span&gt;
    &lt;span class="c1"&gt;// if this is the first time our component rendered&lt;/span&gt;
    &lt;span class="c1"&gt;// 2. the value from the *last* render&lt;/span&gt;
    &lt;span class="c1"&gt;// if we're re-rendering our component (aka stateValues[index])&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;stateValues&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;initValue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// "freeze" our index to this particular useState call with _index.&lt;/span&gt;
    &lt;span class="c1"&gt;// prevents the index from changing before we try to call setState later!&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;_index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;setState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;stateValues&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;_index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;newValue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="c1"&gt;// increment index so our next useState call doesn't override the state&lt;/span&gt;
    &lt;span class="c1"&gt;// we just stored above&lt;/span&gt;
    &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// every time we re-render our app,&lt;/span&gt;
    &lt;span class="c1"&gt;// update all our state variables starting from the top&lt;/span&gt;
    &lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;C&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="c1"&gt;// "render" the component (which calls the useState function)&lt;/span&gt;
    &lt;span class="nx"&gt;C&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;C&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;// return all our functions from this foe React "module"&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;render&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;})();&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setCount&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// whenever we "render" this component with React.render, &lt;/span&gt;
    &lt;span class="c1"&gt;// just log the value of our state variable&lt;/span&gt;
    &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="na"&gt;click&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;setCount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// logs { count: 2 }&lt;/span&gt;
&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;click&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// sets the state at stateValues[0] to 3&lt;/span&gt;
&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;click&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// sets the state at stateValues[0] to 4&lt;/span&gt;
&lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// logs { count: 4 }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Learn a little something?
&lt;/h2&gt;

&lt;p&gt;Awesome. In case you missed it, I launched an &lt;a href="https://tinyletter.com/bholmesdev"&gt;my "web wizardry" newsletter&lt;/a&gt; to explore more knowledge nuggets like this!&lt;/p&gt;

&lt;p&gt;This thing tackles the &lt;a href="https://www.swyx.io/first-principles-approach/"&gt;"first principles"&lt;/a&gt; of web development. In other words, what are all the janky browser APIs, bent CSS rules, and semi-accessible HTML that make all our web projects tick? If you're looking to go &lt;em&gt;beyond the framework&lt;/em&gt;, this one's for you dear web sorcerer 🔮&lt;/p&gt;

&lt;p&gt;&lt;a href="https://tinyletter.com/bholmesdev"&gt;Subscribe away right here&lt;/a&gt;. I promise to always teach and never spam ❤️&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>architecture</category>
      <category>react</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Another way to understand JavaScript's array.reduce</title>
      <dc:creator>Ben Holmes</dc:creator>
      <pubDate>Fri, 05 Feb 2021 16:26:05 +0000</pubDate>
      <link>https://dev.to/hack4impact/another-way-to-understand-array-reduce-5757</link>
      <guid>https://dev.to/hack4impact/another-way-to-understand-array-reduce-5757</guid>
      <description>&lt;p&gt;If you've run the guantlet of &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array#instance_methods" rel="noopener noreferrer"&gt;array methods&lt;/a&gt; in JavaScript, you've probably hit this roadblock a few times:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Wait, how do I use the &lt;code&gt;reduce&lt;/code&gt; function again?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I actually led a JS bootcamp with this topic for my college's Hack4Impact chapter (&lt;a href="http://hack4impact.org/dev-bootcamp" rel="noopener noreferrer"&gt;material 100% free-to-use here!&lt;/a&gt;). Questions on &lt;code&gt;reduce&lt;/code&gt; have come up so many times, and I think I've finally found an explanation that clicks 😁 Hope it works for you too!&lt;/p&gt;

&lt;h2&gt;
  
  
  🎥 Video walkthrough
&lt;/h2&gt;

&lt;p&gt;If you prefer to learn by video tutorial, this one's for you. You can &lt;a href="https://codepen.io/bholmesdev/pen/rXJpmr?editors=0010" rel="noopener noreferrer"&gt;fork this CodePen&lt;/a&gt; for the source material to follow along 🏃‍♂️&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/P9qlPEg_MKc"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  📝 Step-by-step cheatsheet
&lt;/h2&gt;

&lt;p&gt;Let's walk our way to &lt;code&gt;reduce&lt;/code&gt; by using what we know: good ole' for loops.&lt;/p&gt;

&lt;p&gt;Here's an example. Say we have our favorite album on a CD (remember those? 💿), and our stereo tells us the length of each track in minutes. Now, we want to figure out how long the &lt;em&gt;entire album&lt;/em&gt; is.&lt;/p&gt;

&lt;p&gt;Here's a simplified approach for what we want to do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// make a variable to keep track of the length, starting at 0&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;albumLength&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="c1"&gt;// walk through the songs on the album...&lt;/span&gt;
&lt;span class="nx"&gt;album&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;songs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;song&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// and add the length of each song to our running total&lt;/span&gt;
  &lt;span class="nx"&gt;albumLength&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;song&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;minutesLong&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No too bad! Just loop over the songs, and &lt;strong&gt;accumulate&lt;/strong&gt; the album runtime while we walk through the songs. This is basically the process you'd use in real life, tallying up the album length as you skip through the tracks on your stereo.&lt;/p&gt;

&lt;p&gt;That word "accumulate" is pretty significant here though. In essence, we're taking this list of track lengths, and &lt;strong&gt;reducing&lt;/strong&gt; them to a single &lt;strong&gt;accumulated&lt;/strong&gt; number: the &lt;code&gt;albumLength&lt;/code&gt;. This process of &lt;strong&gt;reducing&lt;/strong&gt; to an &lt;strong&gt;accumulator&lt;/strong&gt; should set off a light bulb in your head: 💡 &lt;em&gt;we can use &lt;code&gt;array.reduce&lt;/code&gt;!&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Going from &lt;code&gt;forEach&lt;/code&gt; to &lt;code&gt;reduce&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Let's try reduce-ifying our function from earlier. This is a simple, 4 step process:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Change &lt;code&gt;forEach&lt;/code&gt; to &lt;code&gt;reduce&lt;/code&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;albumLength&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="nx"&gt;album&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;songs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;song&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;albumLength&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;albumLength&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;song&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;minutesLong&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Move &lt;code&gt;albumLength&lt;/code&gt; to the &lt;strong&gt;first parameter of the loop function&lt;/strong&gt;, and the initial value (0) to the &lt;strong&gt;second parameter of &lt;code&gt;reduce&lt;/code&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// accumulator up here 👇&lt;/span&gt;
&lt;span class="nx"&gt;album&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;songs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;albumLength&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;song&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;albumLength&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;albumLength&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;song&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;minutesLong&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// 👈 initial value here&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Change &lt;code&gt;albumLength =&lt;/code&gt; to a &lt;strong&gt;return statement.&lt;/strong&gt; This isn't too different conceptually, since we're still adding our song length onto our "accumulated" album length:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;album&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;songs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;albumLength&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;song&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// 👇 Use "return" instead of our = assignment&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;albumLength&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;song&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;minutesLong&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Retrieve the result of our &lt;code&gt;reduce&lt;/code&gt; loop (aka our total album length). This is just the value returned:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;totalAlbumLength&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;album&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;songs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;albumLength&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;song&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;albumLength&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;song&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;minutesLong&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;And that's it!&lt;/strong&gt; 🎉&lt;/p&gt;

&lt;h3&gt;
  
  
  So wait, why do I even need &lt;code&gt;reduce&lt;/code&gt;?
&lt;/h3&gt;

&lt;p&gt;After all that work, &lt;code&gt;reduce&lt;/code&gt; might feel like a slightly harder way of writing a &lt;code&gt;for&lt;/code&gt; loop. In a way... it kind of is 😆&lt;/p&gt;

&lt;p&gt;It offers one key benefit though: &lt;strong&gt;since &lt;code&gt;reduce&lt;/code&gt; returns our total, function chaining is a lot easier.&lt;/strong&gt; This may not be a benefit you appreciate right away, but consider this more complex scenario:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Say we have this array of arrays,&lt;/span&gt;
&lt;span class="c1"&gt;// and we want to "flatten" everything to one big array of songs&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;songsByAlbum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Rap Snitches Knishes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Beef Rap&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Gumbo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Accordion&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Meat Grinder&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Figaro&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Fazers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Anti-Matter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Krazy World&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;songs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="nx"&gt;songsByAlbum&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;albumSongs&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// "spread" the contents of each array into our big array using "..."&lt;/span&gt;
  &lt;span class="nx"&gt;songs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;songs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;albumSongs&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This isn't too hard to understand. But what if we want to do some &lt;em&gt;more&lt;/em&gt; fancy array functions on that list of &lt;code&gt;songs&lt;/code&gt;?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Ex. Make these MF DOOM songs titles all caps&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;songs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="nx"&gt;songsByAlbum&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;albumSongs&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;songs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;songs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;albumSongs&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;uppercaseSongs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;songs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;song&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;song&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toUppercase&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FHolben888%2Fpersonal-blog%2Fmain%2Fthinking-about-array-reduce%2Fmf-doom-finger-guns.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FHolben888%2Fpersonal-blog%2Fmain%2Fthinking-about-array-reduce%2Fmf-doom-finger-guns.gif" alt="MF DOOM giving finger guns"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;All caps when you spell the man name. Rest in piece &lt;a href="https://open.spotify.com/artist/2pAWfrd7WFF3XhVt9GooDL?si=McgDfNnbTJK5yIdKB_EHGQ" rel="noopener noreferrer"&gt;MF DOOM&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is fine, but what if we could "chain" these 2 modifications &lt;em&gt;together&lt;/em&gt;?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// grab our *final* result all the way at the start&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;uppercaseSongs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Rap Snitches Knishes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Beef Rap&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Gumbo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Accordion&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Meat Grinder&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Figaro&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Fazers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Anti-Matter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Krazy World&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="c1"&gt;// rewriting our loop to a "reduce," same way as before&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;songs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;albumSongs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;songs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;albumSongs&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
&lt;span class="c1"&gt;// then, map our songs right away!&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;song&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;song&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toUppercase&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Woah!&lt;/em&gt; Throwing in a &lt;code&gt;reduce&lt;/code&gt;, we just removed our standalone variables for &lt;code&gt;songsByAlbum&lt;/code&gt; and &lt;code&gt;songs&lt;/code&gt; entirely 🤯&lt;/p&gt;

&lt;p&gt;Take this example with a grain of salt though. &lt;strong&gt;This approach &lt;em&gt;can&lt;/em&gt; hurt the readability of your code&lt;/strong&gt; when you're still new to these array functions. So, just keep this &lt;code&gt;reduce&lt;/code&gt; function in your back pocket, and pull it out when you could really see it improving the quality of your code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learn a little something?
&lt;/h2&gt;

&lt;p&gt;Awesome. In case you missed it, I launched an &lt;a href="https://tinyletter.com/bholmesdev" rel="noopener noreferrer"&gt;my "web wizardry" newsletter&lt;/a&gt; to explore more knowledge nuggets like this!&lt;/p&gt;

&lt;p&gt;This thing tackles the &lt;a href="https://www.swyx.io/first-principles-approach/" rel="noopener noreferrer"&gt;"first principles"&lt;/a&gt; of web development. In other words, what are all the janky browser APIs, bent CSS rules, and semi-accessible HTML that make all our web projects tick? If you're looking to go &lt;em&gt;beyond the framework&lt;/em&gt;, this one's for you dear web sorcerer 🔮&lt;/p&gt;

&lt;p&gt;&lt;a href="https://tinyletter.com/bholmesdev" rel="noopener noreferrer"&gt;Subscribe away right here&lt;/a&gt;. I promise to always teach and never spam ❤️&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>beginners</category>
    </item>
    <item>
      <title>A shiny-on-hover effect that follows your mouse (CSS) ✨</title>
      <dc:creator>Ben Holmes</dc:creator>
      <pubDate>Thu, 28 Jan 2021 17:40:13 +0000</pubDate>
      <link>https://dev.to/hack4impact/a-shiny-on-hover-effect-that-follows-your-mouse-css-4d5b</link>
      <guid>https://dev.to/hack4impact/a-shiny-on-hover-effect-that-follows-your-mouse-css-4d5b</guid>
      <description>&lt;p&gt;Hover states are probably the most fun a developer can have when a designer isn't looking. You've seen the basics at this point; fade-ins, growing and shrinking, color shifts, &lt;a href="https://www.joshwcomeau.com/react/rainbow-button/" rel="noopener noreferrer"&gt;animated rainbow gradients&lt;/a&gt;, etc etc etc.&lt;/p&gt;

&lt;p&gt;But there was one animation that inspired me recently (props &lt;a href="https://www.youtube.com/watch?v=VBkGe1TxEuI&amp;amp;t=225s" rel="noopener noreferrer"&gt;to Keyframers&lt;/a&gt; for shouting it out!)&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1278384065087893505-154" src="https://platform.twitter.com/embed/Tweet.html?id=1278384065087893505"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1278384065087893505-154');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1278384065087893505&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;This isn't some "static" hover state that always looks the same. It actually &lt;em&gt;tracks your mouse moment&lt;/em&gt; to make the page even more interactive. This seemed like such a cool idea... that we threw it all over our &lt;a href="https://hack4impact.org" rel="noopener noreferrer"&gt;Hack4Impact&lt;/a&gt; site 😁&lt;/p&gt;

&lt;p&gt;So let's explore&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; 🎈 Why CSS variables can help us&lt;/li&gt;
&lt;li&gt;✨ How we style our button&lt;/li&gt;
&lt;li&gt;🪤 How we map mouse movements to a metallic shine&lt;/li&gt;
&lt;li&gt;🔨 How to adapt this animation to any UI framework&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Onwards!&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Our end goal
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FHolben888%2Fpersonal-blog%2Fmain%2Fcss-shiny-buttons%2Fshiny-button-demo.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FHolben888%2Fpersonal-blog%2Fmain%2Fcss-shiny-buttons%2Fshiny-button-demo.gif" alt="Demo of buttons shining on hover on hack4impact.org website"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The effect is pretty simple on the surface. Just shift the color a little bit whenever you hover over the button, plus a little circular gradient for a "metallic" sheen. &lt;/p&gt;

&lt;p&gt;But there's a bit of added spice that CSS can't pull off on its own: &lt;strong&gt;We need to track your cursor position&lt;/strong&gt; to make this interactive! Luckily, this has gotten a lot easier over the years; You won't even need a UI framework or state management to pull it off 👀&lt;/p&gt;

&lt;h2&gt;
  
  
  🎈 Brief primer on CSS variables
&lt;/h2&gt;

&lt;p&gt;In case you haven't heard, CSS variables are kind of taking web development by storm right now. They're a bit like those &lt;code&gt;$&lt;/code&gt; variables preprocessors like &lt;a href="https://sass-lang.com" rel="noopener noreferrer"&gt;SASS&lt;/a&gt; and &lt;a href="http://lesscss.org" rel="noopener noreferrer"&gt;LESS&lt;/a&gt; let you pull off, but with one huge benefit: &lt;strong&gt;you can change the value of these variables at runtime&lt;/strong&gt; using JavaScript 😱&lt;/p&gt;

&lt;p&gt;Let's see a simple example. Say we want to make a balloon pump, where you hit a button as fast as you can to "inflate" an HTML-style balloon.&lt;/p&gt;

&lt;p&gt;If we didn't know anything about CSS variables, we'd probably do some style manipulation straight from JavaScript. Here's how we'd pump up a balloon using the &lt;code&gt;transform&lt;/code&gt; property:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;balloon&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.balloon&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// make the balloon bigger by 50%&lt;/span&gt;
&lt;span class="nx"&gt;balloon&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transform&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;scale(1.5)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or, to make the balloon just a little bit bigger on every button click:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pump&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.pump&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;// keep track of the balloon's size in a JS variable&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;pump&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;balloon&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transform&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`scale(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's nothing wrong with this so far. But it has some growing pains:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We need to keep track of a CSS property (the balloon's &lt;code&gt;scale&lt;/code&gt; size) &lt;strong&gt;using a JS variable.&lt;/strong&gt; This could &lt;em&gt;ahem&lt;/em&gt; &lt;strong&gt;balloon&lt;/strong&gt; into a suite of state variables overtime as we animate more elements throughout our app.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;We're writing our CSS using strings.&lt;/strong&gt; This leaves a sour taste in my mouth personally, since we loose all our syntax highlighting + editor suggestions. It can also get nasty to maintain when we want that &lt;code&gt;size&lt;/code&gt; variable in other parts of our styles. For example, what if we wanted to change the &lt;code&gt;background-position&lt;/code&gt; as the balloon inflates? Or the &lt;code&gt;height&lt;/code&gt; and &lt;code&gt;width&lt;/code&gt;? Or some &lt;code&gt;linear-gradient&lt;/code&gt; with multiple color positions?&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  CSS variables to the rescue
&lt;/h3&gt;

&lt;p&gt;As you may have guessed, we can store this &lt;code&gt;size&lt;/code&gt; from our code as a CSS variable!&lt;/p&gt;

&lt;p&gt;We can use the same &lt;code&gt;.style&lt;/code&gt; attribute as before, this time using the &lt;code&gt;setProperty&lt;/code&gt; function to assign a value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;pump&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;balloon&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;--size&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, slide that variable into our &lt;code&gt;transform&lt;/code&gt; property from the CSS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.balloon&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;/* set a default / starting value if JS doesn't supply anything */&lt;/span&gt;
  &lt;span class="py"&gt;--size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="err"&gt;...&lt;/span&gt;
  &lt;span class="c"&gt;/* use var(...) to apply the value */&lt;/span&gt;
  &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--size&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Heck, you can ditch that &lt;code&gt;size&lt;/code&gt; variable entirely and make CSS the source of truth! Just read the value from CSS directly whenever you try to increment it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;pump&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Note: you *can't* use balloon.style here!&lt;/span&gt;
  &lt;span class="c1"&gt;// This won't give you the up-to-date value of your variable.&lt;/span&gt;
  &lt;span class="c1"&gt;// For that, you'll need getComputedStyle(...)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getComputedStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;balloon&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;getPropertyValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;--size&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// size is a string at this stage, so we'll need to cast it to a number&lt;/span&gt;
  &lt;span class="nx"&gt;balloon&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;--size&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;parseFloat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's some caveats to this of course. Namely, CSS variables &lt;strong&gt;are always strings&lt;/strong&gt; when you retrieve them, so you'll need to cast to an &lt;code&gt;int&lt;/code&gt; or a &lt;code&gt;float&lt;/code&gt; (for decimals) as necessary. The whole &lt;code&gt;.style&lt;/code&gt; vs. &lt;code&gt;getComputedStyle&lt;/code&gt; is a little weird to remember as well, so do whatever makes sense for you!&lt;/p&gt;

&lt;p&gt;Here's a fully working example to &lt;em&gt;pump&lt;/em&gt; up your confidence 🎈&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/bholmesdev/embed/vYXogza?height=600&amp;amp;default-tab=js,result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  ✨ Let's get rolling on our shiny button
&lt;/h2&gt;

&lt;p&gt;Before putting our newfound CSS variable knowledge to the test, let's jump into the styles we'll need for this button.&lt;/p&gt;

&lt;p&gt;Remember that we want a smooth gradient of color to follow our mouse cursor, like a light shining on a piece of metal. As you can imagine, we'll want a &lt;code&gt;radial-gradient&lt;/code&gt; on our &lt;code&gt;button&lt;/code&gt; that we can easily move around.&lt;/p&gt;

&lt;p&gt;We could add a gradient as a secondary background on our button (yes, you can &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Backgrounds_and_Borders/Using_multiple_backgrounds" rel="noopener noreferrer"&gt;overlay multiple backgrounds&lt;/a&gt; on the same element!). But for the sake of simplicity, let's just add another element &lt;em&gt;inside&lt;/em&gt; our button representing our "shiny" effect. We'll do this using a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-elements" rel="noopener noreferrer"&gt;pseudo-element&lt;/a&gt; to be fancy 😁&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.shiny-button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;/* add this property to our button, */&lt;/span&gt;
  &lt;span class="c"&gt;/* so we can position our shiny gradient *relative* to the button itself */&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;relative&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c"&gt;/* then, make sure our shiny effect */&lt;/span&gt;
  &lt;span class="c"&gt;/* doesn't "overflow" outside of our button */&lt;/span&gt;
  &lt;span class="nl"&gt;overflow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;hidden&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#3984ff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* blue */&lt;/span&gt;
  &lt;span class="err"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.shiny-button&lt;/span&gt;&lt;span class="nd"&gt;::after&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;/* all pseudo-elements need "content" to work. We'll make it empty here */&lt;/span&gt;
  &lt;span class="nl"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;40px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;40px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c"&gt;/* make sure the gradient isn't too bright */&lt;/span&gt;
    &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.6&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c"&gt;/* add a circular gradient that fades out on the edges */&lt;/span&gt;
    &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;radial-gradient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;#3984ff00&lt;/span&gt; &lt;span class="m"&gt;80%&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;&lt;strong&gt;Side note:&lt;/strong&gt; You may have noticed our 8-digit hex code on the gradient background. This is a neat feature that lets you add transparency to your hex codes! &lt;a href="https://css-tricks.com/8-digit-hex-codes/" rel="noopener noreferrer"&gt;More on that here.&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Great! With this in place, we should see a subtle, stationary gradient covering our button.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/bholmesdev/embed/LYbPjNQ?height=600&amp;amp;default-tab=css,result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  🪤 Now, lets track some mouse cursors
&lt;/h2&gt;

&lt;p&gt;We'll need to dig into some native browser APIs for this. You probably just listen for &lt;code&gt;click&lt;/code&gt; 99% of the time, so it's easy to forget the dozens of other event listeners at our disposal! We'll need to use the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Element/mousemove_event" rel="noopener noreferrer"&gt;&lt;code&gt;mousemove&lt;/code&gt; event&lt;/a&gt; for our purposes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.shiny-button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mousemove&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we log out or &lt;code&gt;event&lt;/code&gt; object, we'll find some useful values in here. The main one's we're focusing on are &lt;code&gt;clientX&lt;/code&gt; and &lt;code&gt;clientY&lt;/code&gt;, which tell you the mouse position &lt;strong&gt;relative to the entire screen.&lt;/strong&gt; Hover over this button to see what those values look like:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/bholmesdev/embed/yLVBNzE?height=600&amp;amp;default-tab=js,result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;This is pretty useful, but it's not &lt;em&gt;quite&lt;/em&gt; the info we're looking for. Remember that our shiny effect is positioned &lt;em&gt;relative&lt;/em&gt; to the button surrounding it. For instance, to position the effect at the top-left corner of the button, we'd need to set &lt;code&gt;top: 0; left: 0;&lt;/code&gt; So, we'd expect a reading of &lt;code&gt;x: 0 y: 0&lt;/code&gt; when we hover in our example above... But this definitely &lt;em&gt;isn't&lt;/em&gt; the values that &lt;code&gt;clientX&lt;/code&gt; and &lt;code&gt;clientY&lt;/code&gt; give us 😕&lt;/p&gt;

&lt;p&gt;There isn't a magical &lt;code&gt;event&lt;/code&gt; property for this, so we'll need to get a little creative. Remember that &lt;code&gt;clientX&lt;/code&gt; and &lt;code&gt;clientY&lt;/code&gt; give us the &lt;strong&gt;cursor position&lt;/strong&gt; relative to the window we're in. There's also this neat function called &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect" rel="noopener noreferrer"&gt;&lt;code&gt;getBoundingClientRect()&lt;/code&gt;&lt;/a&gt;, which gets the x and y position of our &lt;strong&gt;button&lt;/strong&gt; relative to the window. So if we subtract our button's position from our cursor's position... we should get our position relative to the button!&lt;/p&gt;

&lt;p&gt;This is probably best explored with visuals. Hover your mouse around to see how our &lt;code&gt;mouse&lt;/code&gt; values, &lt;code&gt;boundingClientRect&lt;/code&gt; values, and subtracted values all interact:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/bholmesdev/embed/YzpKGbo?height=600&amp;amp;default-tab=js,result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  💅 Pipe those coordinates into CSS
&lt;/h2&gt;

&lt;p&gt;Alright, let's put two and two together here! We'll pass our values from the &lt;code&gt;mousemove&lt;/code&gt; listener:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mousemove&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBoundingClientRect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--x&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientX&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--y&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientY&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, we'll add some CSS variables to that shiny pseudo-element from before:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.shiny-button&lt;/span&gt;&lt;span class="nd"&gt;::after&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;...&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="err"&gt;*&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="n"&gt;-&lt;/span&gt; &lt;span class="m"&gt;50px&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="err"&gt;*&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="n"&gt;-&lt;/span&gt; &lt;span class="m"&gt;50px&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;A couple notes here:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;We can set a default value for our variables using the second argument to &lt;code&gt;var&lt;/code&gt;. In this case, we'll use 0 for both.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;CSS variables have a weird concept of "types." Here, we're assuming we'll pass our &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt; as integers.  This makes sense from our JavaScript, but CSS has a hard time figuring out that something like &lt;code&gt;10&lt;/code&gt; &lt;em&gt;really&lt;/em&gt; means &lt;code&gt;10px&lt;/code&gt;. To fix this, &lt;strong&gt;just multiply by the unit you want using &lt;code&gt;calc&lt;/code&gt;&lt;/strong&gt; (aka &lt;code&gt;* 1px&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We subtract half the &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; from our positioning. This ensures that our shiny circle is centered up with our cursor, instead of following with the top left corner.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Fade into our effect on entry
&lt;/h3&gt;

&lt;p&gt;We're pretty much done here! Just one small tweak: if we leave this animation as-is, our shiny effect will &lt;em&gt;always&lt;/em&gt; show in some corner of our button (even when we aren't hovering).&lt;/p&gt;

&lt;p&gt;We could fix this from JavaScript to show and hide the effect. But why do that when CSS lets you style-on-hover already?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* to explain this selector, we're */&lt;/span&gt;
&lt;span class="c"&gt;/* selecting our ::after element when the .shiny-button is :hover-ed over */&lt;/span&gt;
&lt;span class="nc"&gt;.shiny-button&lt;/span&gt;&lt;span class="nd"&gt;:hover::after&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;/* show a faded shiny effect on hover */&lt;/span&gt;
  &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.shiny-button&lt;/span&gt;&lt;span class="nd"&gt;::after&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;...&lt;/span&gt;
  &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c"&gt;/* ease into view when "transitioning" to a non-zero opacity */&lt;/span&gt;
  &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;opacity&lt;/span&gt; &lt;span class="m"&gt;0.2s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Boom! Just add a one-line transition effect, and we get a nice fade-in. Here's our finished product ✨&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/bholmesdev/embed/RwobPJG?height=600&amp;amp;default-tab=css,result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  🔨 Adapt to your framework of choice
&lt;/h2&gt;

&lt;p&gt;I get it, you might be dismissing this article with all the &lt;code&gt;eventListeners&lt;/code&gt; thinking &lt;em&gt;well, I'm sure that JS looks much different in framework X.&lt;/em&gt; Luckily, the transition is pretty smooth!&lt;/p&gt;

&lt;p&gt;First, you'll need to grab a &lt;strong&gt;reference&lt;/strong&gt; to the button you're shine-ifying. In React, we can use a &lt;code&gt;useRef&lt;/code&gt; hook to retrieve this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ShinyButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// null to start&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;buttonRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// add a useEffect to check that our buttonRef has a value&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buttonRef&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;buttonRef&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;buttonRef&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;✨✨✨&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or in Svelte, we can &lt;code&gt;bind&lt;/code&gt; our element to a variable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;onMount&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;svelte&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;buttonRef&lt;/span&gt;
  &lt;span class="c1"&gt;// our ref always has a value onMount!&lt;/span&gt;
  &lt;span class="nf"&gt;onMount&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;bind:this=&lt;/span&gt;&lt;span class="s"&gt;{buttonRef}&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;✨✨✨&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Aside: I always like including Svelte examples, since they're usually easier to understand&lt;/em&gt; 😁&lt;/p&gt;

&lt;p&gt;Once we have this reference, it's business-as-usual for our property setting:&lt;/p&gt;

&lt;h3&gt;
  
  
  React example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ShinyButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;buttonRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;// throw your mousemove callback up here to "add" and "remove" later&lt;/span&gt;
  &lt;span class="c1"&gt;// might be worth a useCallback based on the containerRef as well!&lt;/span&gt;
  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;mouseMoveEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;containerRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBoundingClientRect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;containerRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;--x&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientX&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;containerRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;--y&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientY&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buttonRef&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;buttonRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mousemove&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mouseMoveEvent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// don't forget to *remove* the eventListener&lt;/span&gt;
    &lt;span class="c1"&gt;// when your component unmounts!&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;buttonRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mousemove&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mouseMoveEvent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;buttonRef&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Svelte example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;onMount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;onDestroy&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;svelte&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;buttonRef&lt;/span&gt;
  &lt;span class="c1"&gt;// again, declare your mousemove callback up top&lt;/span&gt;
  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;mouseMoveEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;buttonRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBoundingClientRect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;buttonRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;--x&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientX&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;buttonRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;--y&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientY&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nf"&gt;onMount&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;buttonRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mousemove&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mouseMoveEvent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="nf"&gt;onDestroy&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;buttonRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mousemove&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mouseMoveEvent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The main takeaway: 💡 &lt;strong&gt;don't forget to remove event listeners when your component unmounts!&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Check out our live example on Hack4Impact
&lt;/h2&gt;

&lt;p&gt;If you want to see how this works in-context, check out this CodeSandbox for our Hack4Impact site. We also added some CSS fanciness to make this effect usable on &lt;em&gt;any&lt;/em&gt; element, not just buttons ✨&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;To check out the component, &lt;a href="https://codesandbox.io/s/hack4impact-website-0kn5m?file=/components/shared/Nav/index.tsx" rel="noopener noreferrer"&gt;head over here&lt;/a&gt;.&lt;/strong&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  Learn a little something?
&lt;/h2&gt;

&lt;p&gt;Awesome. In case you missed it, I launched an &lt;a href="https://tinyletter.com/bholmesdev" rel="noopener noreferrer"&gt;my "web wizardry" newsletter&lt;/a&gt; to explore more knowledge nuggets like this!&lt;/p&gt;

&lt;p&gt;This thing tackles the &lt;a href="https://www.swyx.io/first-principles-approach/" rel="noopener noreferrer"&gt;"first principles"&lt;/a&gt; of web development. In other words, what are all the janky browser APIs, bent CSS rules, and semi-accessible HTML that make all our web projects tick? If you're looking to go &lt;em&gt;beyond the framework&lt;/em&gt;, this one's for you dear web sorcerer 🔮&lt;/p&gt;

&lt;p&gt;&lt;a href="https://tinyletter.com/bholmesdev" rel="noopener noreferrer"&gt;Subscribe away right here&lt;/a&gt;. I promise to always teach and never spam ❤️&lt;/p&gt;

</description>
      <category>css</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Mocking browser APIs (fetch, localStorage, Dates...) the easy way with Jest</title>
      <dc:creator>Ben Holmes</dc:creator>
      <pubDate>Tue, 19 Jan 2021 22:48:29 +0000</pubDate>
      <link>https://dev.to/bholmesdev/mocking-browser-apis-fetch-localstorage-dates-the-easy-way-with-jest-4kph</link>
      <guid>https://dev.to/bholmesdev/mocking-browser-apis-fetch-localstorage-dates-the-easy-way-with-jest-4kph</guid>
      <description>&lt;p&gt;I hit a snag recently trying to test a &lt;code&gt;localStorage&lt;/code&gt; helper written in React. Figuring out how to test all my state and render changes was certainly the easy part (thanks as always &lt;a href="https://github.com/testing-library/react-testing-library"&gt;React Testing Library&lt;/a&gt; 🐐).&lt;/p&gt;

&lt;p&gt;But soon, I found myself wondering... is there an easy way to "mock" a browser API like storage? Or better yet, how should I test &lt;strong&gt;any function using X API?&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;Well, hope you're hungry! We're gonna explore&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🚅 Why dependency injection doesn't feel like a silver bullet&lt;/li&gt;
&lt;li&gt;📦 How we can mock &lt;code&gt;localStorage&lt;/code&gt; using the &lt;code&gt;global&lt;/code&gt; object&lt;/li&gt;
&lt;li&gt;📶 Ways to go further mocking the &lt;code&gt;fetch&lt;/code&gt; API&lt;/li&gt;
&lt;li&gt;🔎 An alternative approach using &lt;code&gt;jest.spyOn&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Onwards!&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's grab something to eat first
&lt;/h2&gt;

&lt;p&gt;Here's a simple (and tasty) example of a function worth testing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;saveForLater&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;leftoverChili&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;whatsInTheFridge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mealPrepOfTheWeek&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;whatsInTheFridge&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// if our fridge is empty, chili's on the menu 🌶&lt;/span&gt;
        &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mealPrepOfTheWeek&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;leftoverChili&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// otherwise, we'll just bring it to our neighbor's potluck 🍽&lt;/span&gt;
      &lt;span class="nx"&gt;goToPotluck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;leftoverChili&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// if something went wrong, we're going to the potluck empty-handed 😬&lt;/span&gt;
    &lt;span class="nx"&gt;goToPotluck&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is pretty straightforward... but it's got some &lt;code&gt;localStorage&lt;/code&gt; madness baked-in. We could probably start with the &lt;strong&gt;inject all the things strategy (TM)&lt;/strong&gt; to address this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;saveForLater&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;leftoverChili&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// treat our storage calls as parameters to the function,&lt;/span&gt;
  &lt;span class="c1"&gt;// with the default value set to our desired behavior&lt;/span&gt;
  &lt;span class="nx"&gt;getFromStorage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mealPrepOfTheWeek&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nx"&gt;setInStorage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;food&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mealPrepOfTheWeek&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;food&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// then, sub these values into our function&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;whatsInTheFridge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;getFromStorage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="nx"&gt;setInStorage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;leftoverChili&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, our test file can pass some tasty mock functions we can work with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;puts the chili in the fridge when the fridge is empty&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// just make some dummy functions, where the getter returns undefined&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getFromStorage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;mockReturnValueOnce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;// then, make a mock storage object to check&lt;/span&gt;
  &lt;span class="c1"&gt;// whether the chili was put in the fridge&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;mockStorage&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;setInStorage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;mockStorage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="nx"&gt;saveForLater&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;chili&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getFromStorage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setInStorage&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;setInStorage&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toHaveBeenCalledOnce&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mockFridge&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;chili&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This isn't &lt;em&gt;too&lt;/em&gt; bad. Now we can check whether our &lt;code&gt;localStorage&lt;/code&gt; functions get called, and verify that we're sending the right values.&lt;/p&gt;

&lt;p&gt;Still, there's something a bit ugly here: &lt;strong&gt;we just restructured our code for the sake of cleaner tests!&lt;/strong&gt; I don't know about you, but I feel a little uneasy moving the internals of my function to a set of &lt;em&gt;parameters&lt;/em&gt;. And what if the unit tests move away or get rewritten years down the line? That leaves us with another odd design choice to pass onto the next developer 😕&lt;/p&gt;

&lt;h2&gt;
  
  
  📦 What if we could mock browser storage directly?
&lt;/h2&gt;

&lt;p&gt;Sure, &lt;a href="https://stackoverflow.com/questions/45111198/how-to-mock-functions-in-the-same-module-using-jest"&gt;mocking module functions &lt;em&gt;we wrote ourselves&lt;/em&gt; is pretty tough.&lt;/a&gt; But mocking native APIs is surprisingly straightforward! Let me stir the pot a little 🥘&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// let's make a mock fridge (storage) for all our tests to use&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;mockFridge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="nx"&gt;beforeAll&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setItem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;mockFridge&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getItem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;mockFridge&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nx"&gt;beforeEach&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// make sure the fridge starts out empty for each test&lt;/span&gt;
  &lt;span class="nx"&gt;mockFridge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nx"&gt;afterAll&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// return our mocks to their original values&lt;/span&gt;
  &lt;span class="c1"&gt;// 🚨 THIS IS VERY IMPORTANT to avoid polluting future tests!&lt;/span&gt;
    &lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mockReset&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mockReset&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Oh, take a look at that meaty piece in the middle! There's some big takeaways from this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Jest gives you a nice &lt;code&gt;global&lt;/code&gt; object to work with.&lt;/strong&gt; More specifically, Jest gives you access to &lt;a href="https://github.com/jsdom/jsdom"&gt;JSDOM&lt;/a&gt; out-of-the-box, which populates &lt;code&gt;global&lt;/code&gt; (a standard in Node) with a treasure trove of APIs. As we've discovered, it includes our favorite browser APIs as well!&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;We can use the &lt;code&gt;prototype&lt;/code&gt; to mock functions inside a JS class.&lt;/strong&gt; You're right to wonder &lt;em&gt;why&lt;/em&gt; we need to mock &lt;code&gt;Storage.prototype&lt;/code&gt;, rather than mocking &lt;code&gt;localStorage&lt;/code&gt; directly. In short: &lt;code&gt;localStorage&lt;/code&gt; is actually an &lt;em&gt;instance&lt;/em&gt; of a Storage &lt;em&gt;class.&lt;/em&gt; Sadly, mocking methods on a class instance (i.e. &lt;code&gt;localStorage.getItem&lt;/code&gt;) doesn't work with our &lt;code&gt;jest.fn&lt;/code&gt; approach. But don't fret! You can &lt;a href="https://stackoverflow.com/a/41434763"&gt;mock the entire &lt;code&gt;localStorage&lt;/code&gt; class&lt;/a&gt; as well if this &lt;code&gt;prototype&lt;/code&gt; madness makes you feel uneasy 😁 Fair warning though: it's a bit harder to test whether class methods were called with &lt;code&gt;toHaveBeenCalled&lt;/code&gt; compared to a plan ole' &lt;code&gt;jest.fn&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;💡 &lt;em&gt;&lt;strong&gt;Note:&lt;/strong&gt; This strategy will mock both &lt;code&gt;localStorage&lt;/code&gt; and &lt;code&gt;sessionStorage&lt;/code&gt; with the same set of functions. If you need to mock these independently, you might need to split up your test suites or &lt;a href="https://stackoverflow.com/a/41434763"&gt;mock the storage class&lt;/a&gt; as previously suggested.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now, we're good to test our original function injection-free!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;puts the chili in the fridge when the fridge is empty&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;saveForLater&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;chili&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototoype&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toHaveBeenCalledOnce&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mockStorage&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mealPrepOfTheWeek&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nx"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;chili&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's almost no setup to speak of now that we're mocking &lt;code&gt;global&lt;/code&gt; values. &lt;strong&gt;Just remember to clean the kitchen in that &lt;code&gt;afterAll&lt;/code&gt; block,&lt;/strong&gt; and we're good to go 👍&lt;/p&gt;

&lt;h2&gt;
  
  
  📶 So what else could we mock?
&lt;/h2&gt;

&lt;p&gt;Now that we're cooking with crisco, let's try our hand at some more &lt;code&gt;global&lt;/code&gt; functions. &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API"&gt;&lt;strong&gt;The &lt;code&gt;fetch&lt;/code&gt; API&lt;/strong&gt;&lt;/a&gt; is a great candidate for this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// let's fetch some ingredients from the store&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;grabSomeIngredients&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://wholefoods.com/overpriced-organic-spices&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;cumin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;paprika&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;chiliPowder&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;cumin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;paprika&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;chiliPowder&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; 
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Seems simple enough! We're just making sure that the Cumin, Paprika, and Chili Powder get fetched and returned in an array of chili spices 🌶&lt;/p&gt;

&lt;p&gt;As you might expect, we're using the same &lt;code&gt;global&lt;/code&gt; strategy as before:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fetches the right ingredients&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cumin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;McCormick ground cumin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;paprika&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Smoked paprika&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chiliPowder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Spice Islands Chili Powder&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;spices&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;cumin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;paprika&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;chiliPowder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;garlicSalt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Yuck. Fresh garlic only!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fetch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;mockImplementationOnce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="c1"&gt;// first, mock the "json" function containing our result&lt;/span&gt;
        &lt;span class="na"&gt;json&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="c1"&gt;// then, resolve this second promise with our spices&lt;/span&gt;
          &lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;spices&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}),&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;grabSomeIngredients&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;cumin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;paprika&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;chiliPowder&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not too bad! You'll probably need a second to process that doubly-nested &lt;code&gt;Promise&lt;/code&gt; we're mocking (remember, &lt;code&gt;fetch&lt;/code&gt; returns &lt;em&gt;another&lt;/em&gt; promise for the &lt;code&gt;json&lt;/code&gt; result!). Still, our test stayed pretty lean while thoroughly testing our function.&lt;/p&gt;

&lt;p&gt;You'll also notice that we used &lt;strong&gt;&lt;code&gt;mockImplementationOnce&lt;/code&gt;&lt;/strong&gt; here. Sure, we could have used the same &lt;code&gt;beforeAll&lt;/code&gt; technique as before, but we probably want to mock different implementations for &lt;code&gt;fetch&lt;/code&gt; once we get into the error scenarios. Here's how that might look:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;returns an empty array on bad fetch&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fetch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;mockImplementationOnce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fetchSomething&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="c1"&gt;// if our fetch fails, we don't get any spices!&lt;/span&gt;
    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;([])&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;returns an empty array on bad json format&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fetch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;mockImplementationOnce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;json&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fetchSomething&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;([])&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And since we're mocking implementation &lt;em&gt;once,&lt;/em&gt; there's no &lt;code&gt;afterAll&lt;/code&gt; cleanup to worry about! Pays to clean your dishes as soon as you're done with them 🧽&lt;/p&gt;

&lt;h2&gt;
  
  
  🔎 Addendum: using "spies"
&lt;/h2&gt;

&lt;p&gt;Before wrapping up, I want to point out an alternative approach: mocking &lt;code&gt;global&lt;/code&gt; using &lt;a href="https://jestjs.io/docs/en/jest-object#jestspyonobject-methodname"&gt;Jest spies&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let's refactor our &lt;code&gt;localStorage&lt;/code&gt; example from before:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="c1"&gt;// first, we'll need to make some variables to hold onto our spies&lt;/span&gt;
&lt;span class="c1"&gt;// we'll use these for clean-up later&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;setItemSpy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getItemSpy&lt;/span&gt;

&lt;span class="nx"&gt;beforeAll&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// previously: global.Storage.prototype.setItem = jest.fn(...)&lt;/span&gt;
    &lt;span class="nx"&gt;setItemSpy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jest&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;spyOn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;setItem&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mockImplementation&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;mockStorage&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="c1"&gt;// previously: global.Storage.prototype.getItem = jest.fn(...)&lt;/span&gt;
  &lt;span class="nx"&gt;getItemSpy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jest&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;spyOn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;getItem&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mockImplementation&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;mockStorage&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nx"&gt;afterAll&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// then, detach our spies to avoid breaking other test suites&lt;/span&gt;
  &lt;span class="nx"&gt;getItemSpy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mockRestore&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nx"&gt;setItemSpy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mockRestore&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Overall, this is pretty much identical to our original approach. The only difference is in the semantics; instead of &lt;strong&gt;assigning new behavior&lt;/strong&gt; to these global functions (i.e. &lt;code&gt;= jest.fn()&lt;/code&gt;), we're &lt;strong&gt;intercepting requests to these functions&lt;/strong&gt; and using our own implementation.&lt;/p&gt;

&lt;p&gt;This might feel a little "safer" to some people, since we're not explicitly overwriting the behavior of these functions anymore. But as long as you pay attention to your cleanup in the &lt;code&gt;afterAll&lt;/code&gt; block, either approach is valid 😁&lt;/p&gt;

&lt;h2&gt;
  
  
  Learn a little something?
&lt;/h2&gt;

&lt;p&gt;Awesome. In case you missed it, I launched an &lt;a href="https://tinyletter.com/bholmesdev"&gt;my "web wizardry" newsletter&lt;/a&gt; to explore more knowledge nuggets like this!&lt;/p&gt;

&lt;p&gt;This thing tackles the &lt;a href="https://www.swyx.io/first-principles-approach/"&gt;"first principles"&lt;/a&gt; of web development. In other words, what are all the janky browser APIs, bent CSS rules, and semi-accessible HTML that make all our web projects tick? If you're looking to go &lt;em&gt;beyond the framework&lt;/em&gt;, this one's for you dear web sorcerer 🔮&lt;/p&gt;

&lt;p&gt;&lt;a href="https://tinyletter.com/bholmesdev"&gt;Subscribe away right here&lt;/a&gt;. I promise to always teach and never spam ❤️&lt;/p&gt;

</description>
      <category>testing</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
