<?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: ryanfiller</title>
    <description>The latest articles on DEV Community by ryanfiller (@ryanfiller).</description>
    <link>https://dev.to/ryanfiller</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%2F341654%2F89821ecb-3a5b-48f1-9a99-a1fb37e8b763.png</url>
      <title>DEV Community: ryanfiller</title>
      <link>https://dev.to/ryanfiller</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ryanfiller"/>
    <language>en</language>
    <item>
      <title>Blackletter Logo</title>
      <dc:creator>ryanfiller</dc:creator>
      <pubDate>Thu, 13 Apr 2023 05:00:00 +0000</pubDate>
      <link>https://dev.to/ryanfiller/blackletter-logo-1ii8</link>
      <guid>https://dev.to/ryanfiller/blackletter-logo-1ii8</guid>
      <description>&lt;p&gt;When I &lt;a href="https://ryanfiller.com/blog/starting-fresh-in-2020" rel="noopener noreferrer"&gt;started my website from scratch in 2020&lt;/a&gt;, I always intended to create a new logo as one of the first things I would work on. However, other tasks took priority, and now more than three years later, I still only have my name typed out in a font at the top of the site serving as a makeshift logo. This has made it difficult to make big design decisions elsewhere on the site, as I was afraid I would put a lot of work into a design element, only to have it clash with whatever I eventually settled on for a logo. This time around, I made sure to do things in a different order when starting over.&lt;/p&gt;

&lt;p&gt;To check out the finished logo, feel free to jump straight to the end.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Blackletter
&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%2Fryanfiller.com%2F%2Fimages%2FU%2B211C.svg" 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%2Fryanfiller.com%2F%2Fimages%2FU%2B211C.svg" alt="Unicode Character U+211C"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;"Blackletter," according to &lt;a href="https://en.wikipedia.org/wiki/Blackletter" rel="noopener noreferrer"&gt;Wikipedia&lt;/a&gt;, "is a script used throughout Western Europe from approximately 1150 until the 17th century." Despite its wide usage during the European Middle Ages, the style eventually fell out of favor due to the rising popularity of less ornate and more readable styles, in addition to having &lt;a href="https://blog.ocad.ca/wordpress/visd2004-fw2020-002-m/sample-page/futurablackletter-and-their-links-to-nazi-germany/" rel="noopener noreferrer"&gt;a complicated relationship to one of the 20th century's worst events&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The style of this lettering is characterized by tall and narrow letters formed by straight, angular lines that often end in sharp final shapes that "bite," or overlap, other letters. Due to its prominent historical usage in the European Middle Ages it still lives on as a sort of shorthand for the "medieval period" for works set in the time period, especially the fantasy fiction genre.&lt;/p&gt;

&lt;h2&gt;
  
  
  Inspiration
&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%2Fryanfiller.com%2F%2Fimages%2Fblackletter-inspiration-board.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%2Fryanfiller.com%2F%2Fimages%2Fblackletter-inspiration-board.png" alt="inspiration board of things that made me think of 'blackletter' - The Addams Family, Treasure Island, Bugs Bunny in Knighty Knight Bugs, The Legend of Zelda, Final Fantasy, Lord of the Rings, Doctor Doom, Merlin, Quest 64, The Sword in the Stone, Gargoyles, Dragonheart, Dungeons &amp;amp; Dragons, Lego Castles, Hook, Shining Force II, The Hobbit, Night on Bald Mountain, Castlevania, The Chronicles of Narnia, The Three Musketeers, Castlevania III, Gauntlet, Pirates of Dark Water, Robin Hood, Warcraft II"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I began the project by researching everything I personally associated with this style of font. Surprising to me, many of the things I thought of as using "blackletter" actually use a different type of lettering like &lt;a href="https://en.wikipedia.org/wiki/Antiqua_(typeface_class)" rel="noopener noreferrer"&gt;antiqua&lt;/a&gt;, a &lt;a href="https://en.wikipedia.org/wiki/Rotunda_(script)" rel="noopener noreferrer"&gt;rotunda&lt;/a&gt; or &lt;a href="https://en.wikipedia.org/wiki/Carolingian_minuscule" rel="noopener noreferrer"&gt;minuscule&lt;/a&gt; style, or even a more generic &lt;a href="https://en.wikipedia.org/wiki/Roman_square_capitals" rel="noopener noreferrer"&gt;Roman style&lt;/a&gt; font. I found this interesting because I guess I wasn't aiming to reference any specific historic period precisely, rather I wanted to capture the general "vibe" these projects had.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rough Sketch
&lt;/h2&gt;

&lt;p&gt;I actually had this initial idea a while back and jumped directly into Adobe Illustrator to rough it out. According to the file created time, I started experimenting with this logo in April 2021.&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%2Fryanfiller.com%2F%2Fimages%2Fblackletter-logo-sketch.svg" 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%2Fryanfiller.com%2F%2Fimages%2Fblackletter-logo-sketch.svg" alt="original logo sketch from 2021"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I took a lot of inspiration from the &lt;a href="https://en.wikipedia.org/wiki/Blackletter#Textura" rel="noopener noreferrer"&gt;Textura&lt;/a&gt; form because I thought its inherent geometry fit the best with my personal design style. I also &lt;em&gt;really&lt;/em&gt; wanted to avoid the WWII connotations I mentioned above so I did my best to stay away from directly referencing the &lt;a href="https://en.wikipedia.org/wiki/Fraktur" rel="noopener noreferrer"&gt;Fraktur&lt;/a&gt; style. The logo is made up only of straight lines, with each letter constructed using a combination of a square diamond shape and a vertical rectangle.&lt;/p&gt;

&lt;h2&gt;
  
  
  Refining
&lt;/h2&gt;

&lt;p&gt;I lost interest in this logo for a while, but then something happened earlier last year that really got me back into the "medieval fantasy aesthetic" mindset.&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%2Fryanfiller.com%2F%2Fimages%2Felden-ring-ryanknight.jpg" 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%2Fryanfiller.com%2F%2Fimages%2Felden-ring-ryanknight.jpg" alt="screenshot of my Elden Ring game menu showing my level 247 knight character with 251 hours of playtime"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After months of adventuring around &lt;a href="https://www.youtube.com/watch?v=E3Huy2cdih0" rel="noopener noreferrer"&gt;The Lands Between&lt;/a&gt;, battling all sorts of knights, wizards, and monsters, I felt compelled to find a way to make this logo work.&lt;/p&gt;

&lt;h3&gt;
  
  
  Letter Stems
&lt;/h3&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%2Fryanfiller.com%2F%2Fimages%2Fblackletter-logo-r-comparison.svg" 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%2Fryanfiller.com%2F%2Fimages%2Fblackletter-logo-r-comparison.svg" alt="outlined version of letter r, after on the left and the right. the after version is shorter and thinner"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One of the first changes I made was to slim down all of the vertical letter stems. In the original sketch each stem had the same width as one of the diamonds rotated 45°, but for the updated version I reduced the width to only half of the width of one of the serif finals. This added some visual contrast to the logo and made it more realistic in terms of what a calligraphy pen could produce.&lt;/p&gt;

&lt;h3&gt;
  
  
  X-Height
&lt;/h3&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%2Fryanfiller.com%2F%2Fimages%2Fblackletter-logo-rf-square.svg" 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%2Fryanfiller.com%2F%2Fimages%2Fblackletter-logo-rf-square.svg" alt="a monogram version of the logo showing only the letters 'rf', a square is drawn around them"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fryanfiller.com%2F%2Fimages%2Fblackletter-logo-rf-comparison.svg" 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%2Fryanfiller.com%2F%2Fimages%2Fblackletter-logo-rf-comparison.svg" alt="a new version of the 'rf' monogram overlaid on the old version showing that the new version is shorter"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From the start, I had envisioned a version of the logo that could work as a monogram with only my initials. While working on the lockup, I realized that it would work best if it fit inside a square so it could be used for various &lt;a href="https://en.wikipedia.org/wiki/Favicon" rel="noopener noreferrer"&gt;favicon images&lt;/a&gt; that my site would eventually need.&lt;/p&gt;

&lt;p&gt;This ultimately ended up deciding the final height of the letters so that the ascender and descender of the "f" would line up with the imagined outline, as would the bottom bottom terminal of the "r". To fit into the square the overall &lt;a href="https://en.wikipedia.org/wiki/X-height" rel="noopener noreferrer"&gt;x-height&lt;/a&gt; and &lt;a href="https://en.wikipedia.org/wiki/Body_height_(typography)" rel="noopener noreferrer"&gt;body height&lt;/a&gt; of the letters was reduced a decent amount from the original sketch.&lt;/p&gt;

&lt;p&gt;[[clearfix]]&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%2Fryanfiller.com%2F%2Fimages%2Fblackletter-logo-vertical-alignment.svg" 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%2Fryanfiller.com%2F%2Fimages%2Fblackletter-logo-vertical-alignment.svg" alt="logo with vertical dotted lines overlaid to show that each letter has a consistent stem width"&gt;&lt;/a&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%2Fryanfiller.com%2F%2Fimages%2Fblackletter-logo-horizontal-alignment.svg" 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%2Fryanfiller.com%2F%2Fimages%2Fblackletter-logo-horizontal-alignment.svg" alt="letter with horizontal dotted liens overlaid to show that each letter has a consistent baseline, x-height, and body height  "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the x-height, body height, stem width, and terminal size were established, I reworked the rest of the letters for consistency with the updated "r" and "f."&lt;/p&gt;

&lt;h3&gt;
  
  
  Grid Layout
&lt;/h3&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%2Fryanfiller.com%2F%2Fimages%2Fblackletter-logo-outline-grid.svg" 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%2Fryanfiller.com%2F%2Fimages%2Fblackletter-logo-outline-grid.svg" alt="outline of full logo overlaid on a grid system showing uniform alignment for all the shapes"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I had two main goals while creating each individual letter form: to use the minimum number of shapes necessary, and to strictly adhered to a 6x6 grid system created from the size of the base diamond shape.&lt;/p&gt;

&lt;p&gt;I also decided to combine the "f" and "i" characters into an "ﬁ" ligature was both for consistency in the letter spacing of my last name and also just because I am a fan of ligatured letters in general.&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%2Fryanfiller.com%2F%2Fimages%2Fblackletter-logo-gaps.svg" 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%2Fryanfiller.com%2F%2Fimages%2Fblackletter-logo-gaps.svg" alt="logo with diagonal dotted lines overlaid showing the consistent angled gaps between the r and y, f and i, and e bowl"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When I was in design school I remember a professor telling me - &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Once An Accident, Twice A Coincidence, Three Times A Pattern&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I looked this quote up and expected it to have been said by a famous designer, but apparently this is actually an &lt;a href="https://en.wikipedia.org/wiki/Ian_Fleming" rel="noopener noreferrer"&gt;Ian Flemming&lt;/a&gt; quote from the James Bond novel &lt;a href="https://en.wikipedia.org/wiki/Goldfinger_(novel)" rel="noopener noreferrer"&gt;Goldfinger&lt;/a&gt;. Regardless of who coined the phrase, I made sure to repeat the small, diagonal spacing at least three times — once between the "r" and "y", once in the "ﬁ" ligature, and once in the open counter of the "e".&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%2Fryanfiller.com%2F%2Fimages%2Fblackletter-logo-block-alignment.svg" 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%2Fryanfiller.com%2F%2Fimages%2Fblackletter-logo-block-alignment.svg" alt="logo with diagonal dotted lines overlaid showing alignment between different letter ascenders, descenders, and terminals"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With that spacing nailed down I designed the remaining letters to create as much diagonal alignment among their ascenders, descenders, and terminals as possible. Because of the previous constraints around x-height and overall letter height not every element could align perfectly.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Finished Logo
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Progress on the New Site
&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%2Fryanfiller.com%2F%2Fimages%2Fbeta-screenshot-1.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%2Fryanfiller.com%2F%2Fimages%2Fbeta-screenshot-1.png" alt="screenshot of beta.ryanfiller.com that corresponds with the additions from this blog post"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Follow along with my redesign at &lt;a href="http://beta.ryanfiller.com/" rel="noopener noreferrer"&gt;beta.ryanfiller.com&lt;/a&gt; (which may or may not currently match what is shown at the time of publishing this blog post.)&lt;/p&gt;

</description>
      <category>design</category>
      <category>logo</category>
      <category>typography</category>
      <category>css</category>
    </item>
    <item>
      <title>Building an Alert Component in Svelte</title>
      <dc:creator>ryanfiller</dc:creator>
      <pubDate>Wed, 30 Jun 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/ryanfiller/building-an-alert-component-in-svelte-57k5</link>
      <guid>https://dev.to/ryanfiller/building-an-alert-component-in-svelte-57k5</guid>
      <description>&lt;p&gt;If you were on the internet in the 90s then you probably remember what life was like before pop-up blockers. Even the person who invented pop-up ads &lt;a href="https://www.forbes.com/sites/jaymcgregor/2014/08/15/the-man-who-invented-pop-up-ads-says-im-sorry/?sh=7dc238134ebe"&gt;regrets doing it&lt;/a&gt;. Using the internet today can feel the same as 30 years ago, with alerts and banners everywhere. With things like &lt;a href="https://en.wikipedia.org/wiki/General_Data_Protection_Regulation"&gt;GDPR&lt;/a&gt; cookie warnings starting in 2018 and COVID alerts in 2020 the web looks more and more like &lt;a href="https://2018.bloomca.me/en"&gt;this parody site&lt;/a&gt; with every passing year. What's worse is that according to this &lt;a href="https://accessibe.com/blog/knowledgebase/we-analyzed-10000000-pages-and-heres-where-most-fail-with-ada-and-wcag-21-compliance"&gt;accessibe.com article&lt;/a&gt; the large majority of these pop-ups don't pass basic &lt;a href="https://www.w3.org/WAI/standards-guidelines/wcag/"&gt;WCAG&lt;/a&gt; accessibility guidelines. These modals and alerts often contain agreements that need acceptance before proceeding. This means that people with different browsing needs can often be blocked from the entire site.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--O_u05hil--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tpiufcs4nsj1mgq3a7in.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--O_u05hil--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tpiufcs4nsj1mgq3a7in.png" alt="alert example"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All that said, sometimes, there &lt;em&gt;are&lt;/em&gt; things you still want to show a user before they proceed. I needed such an alert to warn users that there's most likely unexpectedly broken code in the "lab" section of my site. &lt;/p&gt;

&lt;p&gt;This post will walk through how I used Svelte to build an accessible alert that also won't annoy users too badly. It also incorporates a cache-busting mechanism in case the content ever changes and users need to see it again.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Basic Component
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;&amp;lt;Alert /&amp;gt;&lt;/code&gt; component markup is pretty straightforward. It accepts a few &lt;code&gt;props&lt;/code&gt; — &lt;code&gt;show&lt;/code&gt; for defining when the alert is visible, a &lt;code&gt;title&lt;/code&gt; to display as an internal header, and a &lt;code&gt;close&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;Since this is going to be a reusable component, I'm using the Svelte &lt;a href="https://svelte.dev/docs#slot"&gt;&lt;code&gt;&amp;lt;slot /&amp;gt;&lt;/code&gt; element&lt;/a&gt; to pass content into the main body of the component.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!-- alert.svelte --&amp;gt;
&amp;lt;script&amp;gt;
  export let show
  export let close
  export let title
&amp;lt;/script&amp;gt;

{#if show}
  &amp;lt;div&amp;gt;
    &amp;lt;section&amp;gt;
      &amp;lt;header&amp;gt;
        &amp;lt;strong&amp;gt;
          {title}
        &amp;lt;/strong&amp;gt;
        &amp;lt;button on:click={close}&amp;gt;
          Close
        &amp;lt;/button&amp;gt;
      &amp;lt;/header&amp;gt;

      &amp;lt;div&amp;gt;
        &amp;lt;slot /&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/section&amp;gt;
  &amp;lt;/div&amp;gt;
{/if}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The HTML &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; Element
&lt;/h2&gt;

&lt;p&gt;When trying to make accessible content, people tend to reach for adding &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA"&gt;ARIA attributes&lt;/a&gt; to their HTML elements. But, the first rule of thumb for ARIA is actually to try not to use ARIA.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Many of these widgets were later incorporated into HTML5, and developers should prefer using the correct semantic HTML element over using ARIA, if such an element exists.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;HTML5 introduced the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog"&gt;&lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; element&lt;/a&gt; for content like this, so it will be the foundattion for my component. The &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; element will be understood by modern screen readers and also comes with built-in conveniences like an &lt;code&gt;open&lt;/code&gt; attribute to toggle visibility and a &lt;code&gt;::backdrop&lt;/code&gt; pseudo element to handle the overlay on the content behind it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!-- alert.svelte --&amp;gt;
&amp;lt;script&amp;gt;
  export let show
  export let close
  export let title
&amp;lt;/script&amp;gt;

{#if show}
  &amp;lt;dialog open on:click={close}&amp;gt;
    &amp;lt;section&amp;gt;
      &amp;lt;header&amp;gt;
        &amp;lt;strong&amp;gt;
          {title}
        &amp;lt;/strong&amp;gt;
        &amp;lt;button on:click={close}&amp;gt;
          Close
        &amp;lt;/button&amp;gt;
      &amp;lt;/header&amp;gt;

      &amp;lt;div&amp;gt;
        &amp;lt;slot /&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/section&amp;gt;
  &amp;lt;/dialog&amp;gt;
{/if}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It might seem weird to be attaching a click event to fire the &lt;code&gt;close&lt;/code&gt; function to the actual &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; element, but there's a reason for that I'll cover in a bit.&lt;/p&gt;

&lt;p&gt;Unfortunately, Safari &lt;a href="https://caniuse.com/?search=dialog"&gt;doesn't fully support the &lt;code&gt;&amp;lt;dialog /&amp;gt;&lt;/code&gt;&lt;/a&gt; tag. For now, that means ARIA is required, although maybe as browser support improves this could one day be removed. &lt;/p&gt;

&lt;p&gt;The good news is that rather than render nothing, Safari will still render the element but instead treat it like a normal &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;. Because it will be rendered as a non-semantic element, I can mark it with the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/dialog_role"&gt;ARIA &lt;code&gt;role='dialog'&lt;/code&gt;&lt;/a&gt; so that any Safari screen reader will still treat it correctly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!-- alert.svelte --&amp;gt;
&amp;lt;script&amp;gt;
  export let show
  export let close
  export let title
&amp;lt;/script&amp;gt;

{#if show}
  &amp;lt;dialog
    open
    on:click={close}
    role='dialog'
  &amp;gt;
    &amp;lt;section&amp;gt;
      &amp;lt;header&amp;gt;
        &amp;lt;strong&amp;gt;
          {title}
        &amp;lt;/strong&amp;gt;
        &amp;lt;button on:click={close}&amp;gt;
          Close
        &amp;lt;/button&amp;gt;
      &amp;lt;/header&amp;gt;

      &amp;lt;div&amp;gt;
        &amp;lt;slot /&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/section&amp;gt;
  &amp;lt;/dialog&amp;gt;
{/if}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In order to be fully accessible, any &lt;code&gt;role&lt;/code&gt; element needs to meet two other criteria:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The dialog must be properly labeled&lt;/li&gt;
&lt;li&gt;Keyboard focus must be managed correctly &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To address the first point, two more attributes can be added to the &lt;code&gt;&amp;lt;dialog /&amp;gt;&lt;/code&gt; tag — &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_aria-labelledby_attribute"&gt;&lt;code&gt;aria-labelledby&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_aria-describedby_attribute"&gt;&lt;code&gt;aria-describedby&lt;/code&gt;&lt;/a&gt; — and pointed to their corresponding elements using a generated &lt;code&gt;id&lt;/code&gt;. &lt;code&gt;aria-labelledby&lt;/code&gt; should point to the primary identifier of the element, the &lt;code&gt;&amp;lt;header /&amp;gt;&lt;/code&gt;. &lt;code&gt;aria-describedby&lt;/code&gt; needs to identify additional context, which in this case is the main &lt;code&gt;&amp;lt;div /&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I'll get back to the second criteria later in the post.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!-- alert.svelte --&amp;gt;
&amp;lt;script&amp;gt;
  export let show
  export let close
  export let title

  const id = slugify(title)
&amp;lt;/script&amp;gt;

{#if show}
  &amp;lt;dialog
    open
    on:click={close}
    role='dialog'
    aria-labelledby={`${id}-title`}
    aria-describedby={`${id}-content`}
  &amp;gt;
    &amp;lt;section&amp;gt;
      &amp;lt;header id={`${id}-title`}&amp;gt;
        &amp;lt;strong&amp;gt;
          {title}
        &amp;lt;/strong&amp;gt;
        &amp;lt;button on:click={close}&amp;gt;
          Close
        &amp;lt;/button&amp;gt;
      &amp;lt;/header&amp;gt;

      &amp;lt;div id={`${id}-content`}&amp;gt;
        &amp;lt;slot /&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/section&amp;gt;
  &amp;lt;/dialog&amp;gt;
{/if}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Stacking Context
&lt;/h2&gt;

&lt;p&gt;Visually, one of the most important features of a modal or alert is that it appears on top of the rest of the site content. It might seem sufficient to just add a very high &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/z-index"&gt;&lt;code&gt;z-index&lt;/code&gt;&lt;/a&gt;, like &lt;code&gt;9999&lt;/code&gt;, but because of how &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context"&gt;&lt;code&gt;stacking context&lt;/code&gt;&lt;/a&gt; works this sometimes still isn't enough to get an element to the forefront of the page. Josh W Comeau has a &lt;a href="https://www.joshwcomeau.com/css/stacking-contexts/"&gt;good article&lt;/a&gt; that explains why &lt;code&gt;z-index&lt;/code&gt; is more complicated than just assigning the highest number.&lt;/p&gt;

&lt;p&gt;Instead of relying on only CSS, I wanted my modal to take advantage of the fact that the HTML document will stack elements according to DOM source order. This means that the closer to the  end of a document an element is, the higher it will appear by default on the z-axis.&lt;/p&gt;

&lt;p&gt;My site is already set up in such a way that each page is created with a &lt;code&gt;&amp;lt;Page /&amp;gt;&lt;/code&gt; Svelte component that I use to include shared page elements like the header and the footer. Adding a place for the alert to render at the end of the pages is as easy as adding a &lt;a href="https://svelte.dev/docs#slot_name"&gt;named &lt;code&gt;&amp;lt;slot /&amp;gt;&lt;/code&gt;&lt;/a&gt;. Because I don't want errant HTML to render on pages that don't need it, I'm also checking to see if an alert is present using the &lt;a href="https://svelte.dev/docs#slots_object"&gt;&lt;code&gt;$$slots&lt;/code&gt; object&lt;/a&gt; that Svelte provides.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!-- page.svelte --&amp;gt;
&amp;lt;SEO {...$$props} /&amp;gt;

&amp;lt;div id='site'&amp;gt;
  &amp;lt;Header /&amp;gt;

  &amp;lt;main&amp;gt;
    &amp;lt;slot /&amp;gt;
  &amp;lt;/main&amp;gt;

  &amp;lt;Footer /&amp;gt;
&amp;lt;/div&amp;gt;

{#if $$slots.alert}
  &amp;lt;slot name='alert' /&amp;gt;
{/if}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The particular alert I'm building needs to go on any pages in the &lt;code&gt;/lab&lt;/code&gt; section of my site, so I need to add the code for the actual alert to the &lt;code&gt;&amp;lt;Lab /&amp;gt;&lt;/code&gt; component that serves as the template for those subpages. To learn more about how exactly templates on my blog work, I have &lt;a href="https://www.ryanfiller.com/blog/building-a-better-svelte-data-flow#how-does-mdsvex-work"&gt;another post&lt;/a&gt; that goes into depth, or there are the official &lt;a href="https://mdsvex.com/docs#layouts"&gt;&lt;code&gt;mdsvex&lt;/code&gt; docs&lt;/a&gt;. Either way, just know that anything added to the &lt;code&gt;&amp;lt;Lab /&amp;gt;&lt;/code&gt; component outside of the default &lt;code&gt;&amp;lt;slot /&amp;gt;&lt;/code&gt; will appear on every page that matches the url &lt;code&gt;/lab/[slug]&lt;/code&gt;. The &lt;code&gt;&amp;lt;Lab /&amp;gt;&lt;/code&gt; template is also responsible for managing the &lt;code&gt;showAlert&lt;/code&gt; and &lt;code&gt;closeAlert&lt;/code&gt; props.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!-- lab.svelte --&amp;gt;
&amp;lt;script&amp;gt;
  import Page from './page.svelte'
  import Alert from '../components/misc/alert.svelte'

  let showAlert = true

  const closeAlert = () =&amp;gt; { 
    showAlert = false
  }
&amp;lt;/script&amp;gt;

&amp;lt;Page {...$$props} &amp;gt;

  &amp;lt;slot /&amp;gt;

  &amp;lt;Alert
    title='Caution!'
    show={showAlert}
    slot='alert'
  &amp;gt;
    &amp;lt;p&amp;gt;
      Some features in the section require JavaScript, 
      might not work great on mobile, 
      and may not have been thoroughly tested.
    &amp;lt;/p&amp;gt;

    &amp;lt;button on:click={closeAlert}&amp;gt;
      That's okay.  
    &amp;lt;/button&amp;gt;
  &amp;lt;/Alert&amp;gt;

&amp;lt;/Page&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using the &lt;code&gt;slot='alert'&lt;/code&gt; prop on an element inside of the &lt;code&gt;&amp;lt;Page /&amp;gt;&lt;/code&gt; component will render it inside of the &lt;code&gt;&amp;lt;slot name='alert' /&amp;gt;&lt;/code&gt; in the parent component and add it to the bottom of the page.&lt;/p&gt;

&lt;h2&gt;
  
  
  Basic CSS
&lt;/h2&gt;

&lt;p&gt;Many of the styles applied to the alert will vary depending on the site, but there are a few positioning styles that I wanted to cover.&lt;/p&gt;

&lt;p&gt;The first thing to do is to make the &lt;code&gt;&amp;lt;dialog /&amp;gt;&lt;/code&gt; element cover the entire screen using &lt;a href="https://developer.mozilla.org/en-US/docs/Learn/CSS/CSS_layout/Positioning#fixed_positioning"&gt;&lt;code&gt;position: fixed&lt;/code&gt;&lt;/a&gt;. &lt;code&gt;position: fixed&lt;/code&gt; works similarly to &lt;code&gt;position: absolute&lt;/code&gt;, but instead of using the closest position parent like &lt;code&gt;absolute&lt;/code&gt; does, &lt;code&gt;fixed&lt;/code&gt; will position the element relative to the browser window. By combining this position with &lt;code&gt;height&lt;/code&gt; and &lt;code&gt;width&lt;/code&gt; of &lt;code&gt;100%&lt;/code&gt; and a &lt;code&gt;top&lt;/code&gt;, &lt;code&gt;right&lt;/code&gt;, &lt;code&gt;bottom&lt;/code&gt;, and &lt;code&gt;left&lt;/code&gt; of &lt;code&gt;0&lt;/code&gt;, the &lt;code&gt;&amp;lt;dialog /&amp;gt;&lt;/code&gt; box will be stretched to cover the entire viewport. Just to be safe, I gave it an incredibly high &lt;code&gt;z-index&lt;/code&gt; as well, even though we shouldn't need it because of the document ordering from earlier.&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;dialog&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;fixed&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="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;right&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;left&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;bottom&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;z-index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;9999&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;100%&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;100%&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="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;justify-content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;align-items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&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="nb"&gt;transparent&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;The page-height element is also set to &lt;code&gt;display: flex&lt;/code&gt; with both &lt;code&gt;align-items&lt;/code&gt; and &lt;code&gt;justify-content&lt;/code&gt; set to &lt;code&gt;center&lt;/code&gt; to keep the content in the center of the screen.&lt;/p&gt;

&lt;p&gt;At first it might seem odd to set &lt;code&gt;background: transparent;&lt;/code&gt; on the element, since usually these types of alerts show a dark overlay while covering the site. This, again, has to do with the &lt;code&gt;&amp;lt;dialog /&amp;gt;&lt;/code&gt; element not being supported in Safari. Instead of using its native &lt;code&gt;::backdrop&lt;/code&gt; I have to fake this functionality with a normal &lt;code&gt;::after&lt;/code&gt; pseudo element, stretched to cover its entire parent with &lt;code&gt;position: absolute&lt;/code&gt;. It also has a &lt;code&gt;z-index: -1&lt;/code&gt; to make sure that it stays behind the &lt;code&gt;&amp;lt;section /&amp;gt;&lt;/code&gt; element that will contain the main content of the alert.&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;dialog&lt;/span&gt;&lt;span class="nd"&gt;::after&lt;/span&gt; &lt;span class="p"&gt;{&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;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;block&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;top&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;right&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;bottom&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;left&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;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;black&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;.5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;z-index&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="nl"&gt;pointer-events&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="nt"&gt;section&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;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;50rem&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="no"&gt;white&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;It is entirely possible to set a background color directly on the &lt;code&gt;&amp;lt;dialog /&amp;gt;&lt;/code&gt; itself, but I wanted to use the &lt;code&gt;opacity&lt;/code&gt; property to adjust the alpha value to make a transparent version of one of my site's existing colors.&lt;/p&gt;

&lt;p&gt;One other important note here is to make sure and set &lt;code&gt;pointer-events: none;&lt;/code&gt; on the &lt;code&gt;::after&lt;/code&gt; element that is covering the &lt;code&gt;&amp;lt;dialog /&amp;gt;&lt;/code&gt;. Setting the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/pointer-events"&gt;&lt;code&gt;pointer-events&lt;/code&gt; property&lt;/a&gt; to &lt;code&gt;none&lt;/code&gt; will let any clicks pass through the &lt;code&gt;::after&lt;/code&gt; element and activate the &lt;code&gt;on:click&lt;/code&gt; event attached to the background &lt;code&gt;&amp;lt;dialog /&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LZkyrRBS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/acyujvxbqawjg4rik1ml.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LZkyrRBS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/acyujvxbqawjg4rik1ml.png" alt="Microsoft Edge browser showing 3D z-index of the page"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Stacking elements like this can quickly get confusing, so I highly recommend using the new &lt;a href="https://blogs.windows.com/msedgedev/2020/01/23/debug-z-index-3d-view-edge-devtools/"&gt;3D View DevTools in Microsoft Edge&lt;/a&gt;. This screenshot shows the &lt;code&gt;&amp;lt;dialog /&amp;gt;&lt;/code&gt; element covering the viewport, the &lt;code&gt;&amp;lt;section /&amp;gt;&lt;/code&gt; at the forefront of the site, and the rest of the site content being covered by the alert and it's overlay.&lt;/p&gt;

&lt;p&gt;[[clearfix]]&lt;/p&gt;

&lt;h2&gt;
  
  
  Managing &lt;code&gt;tabindex&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/tabindex"&gt;tabindex&lt;/a&gt; is an HTML attribute that describes the order in which the browser will focus on elements when a user navigates the page using the &lt;code&gt;Tab&lt;/code&gt; key. Browsers will automatically assign based on document order, but the HTML spec allows for manipulation in some scenarios. Much like ARIA, it is usually best practice to leave this alone unless you have a very good reason to manually change it.&lt;/p&gt;

&lt;p&gt;I have to manually swap a few &lt;code&gt;tabindex&lt;/code&gt; attributes to make everything else on the page inert to someone tabbing through the site while the alert is open.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!-- page.svelte --&amp;gt;
&amp;lt;SEO {...$$props} /&amp;gt;

&amp;lt;div
  id='site'
  tabindex={$$slots.alert ? -1 : 0}
&amp;gt;
  &amp;lt;Header /&amp;gt;

  &amp;lt;main&amp;gt;
    &amp;lt;slot /&amp;gt;
  &amp;lt;/main&amp;gt;

  &amp;lt;Footer /&amp;gt;
&amp;lt;/div&amp;gt;

{#if $$slots.alert}
  &amp;lt;slot name='alert' /&amp;gt;
{/if}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To make everything within the main body of the site in the &lt;code&gt;&amp;lt;Page /&amp;gt;&lt;/code&gt; component non-focusable when the alert is active, I set a &lt;code&gt;tabindex&lt;/code&gt; of &lt;code&gt;-1&lt;/code&gt; to remove it and its children from the native document tab flow. Because the only two things on the page are the &lt;code&gt;#site&lt;/code&gt; and the &lt;code&gt;&amp;lt;Alert /&amp;gt;&lt;/code&gt;, a user shouldn't be able to focus on any element in the background. When &lt;code&gt;$$slots.alert&lt;/code&gt; is empty, the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_Operator"&gt;ternary&lt;/a&gt; will return &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/Falsy"&gt;falsy&lt;/a&gt; and set &lt;code&gt;tabindex='0'&lt;/code&gt;, which will return the element to its original, unset tab order.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;tabindex='-1'&lt;/code&gt; means that a user can't focus on the element using their keyboard, but it also makes any element, even things like a regular &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;, focusable with JavaScript.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A negative value (usually tabindex="-1") means that the element is not reachable via sequential keyboard navigation, but could be focused with JavaScript or visually by clicking with the mouse. It's mostly useful to create accessible widgets with JavaScript.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!-- alert.svelte --&amp;gt;
&amp;lt;script&amp;gt;
  export let show
  export let close
  export let title

  const id = slugify(title)
&amp;lt;/script&amp;gt;

{#if show}
  &amp;lt;dialog
    open
    on:click={close}
    role='dialog'
    aria-labelledby={`${id}-title`}
    aria-describedby={`${id}-content`}
    tabindex='-1'
  &amp;gt;
    &amp;lt;section&amp;gt;
      &amp;lt;header id={`${id}-title`}&amp;gt;
        &amp;lt;strong&amp;gt;
          {title}
        &amp;lt;/strong&amp;gt;
        &amp;lt;button on:click={close}&amp;gt;
          Close
        &amp;lt;/button&amp;gt;
      &amp;lt;/header&amp;gt;

      &amp;lt;div id={`${id}-content`}&amp;gt;
        &amp;lt;slot /&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/section&amp;gt;
  &amp;lt;/dialog&amp;gt;
{/if}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By also adding &lt;code&gt;tabindex='-1'&lt;/code&gt; to the &lt;code&gt;&amp;lt;dialog /&amp;gt;&lt;/code&gt; element inside the alert, JavaScript can make sure a user's focus is set when it is opened.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trapping Focus
&lt;/h2&gt;

&lt;p&gt;Another accessibility requirement for modal content is that it "traps focus," meaning that a keyboard user cannot navigate outside of the area, analogous to how an overlay on the rest of the site prevents a mouse user from clicking on background elements. This is the second rule from the &lt;code&gt;role&lt;/code&gt; element guidlines. The &lt;a href="https://www.w3.org/TR/wai-aria-practices-1.1/examples/dialog-modal/dialog.html#kbd_label"&gt;ARIA dialog best practices guide&lt;/a&gt; explain what this means pretty explicitly:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Key&lt;/th&gt;
&lt;th&gt;Function&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Tab&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Moves focus to next focusable element inside the dialog. When focus is on the last focusable element in the dialog, moves focus to the first focusable element in the dialog.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Shift + Tab&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Moves focus to previous focusable element inside the dialog. When focus is on the first focusable element in the dialog, moves focus to the last focusable element in the dialog.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Escape&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Closes the dialog.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;To handle the &lt;code&gt;escape&lt;/code&gt; key the special &lt;a href="https://svelte.dev/docs#svelte_window"&gt;&lt;code&gt;&amp;lt;svelte:window /&amp;gt;&lt;/code&gt; component&lt;/a&gt; can accept an event listener to create a global way to exit the modal.&lt;/p&gt;

&lt;p&gt;The main component can use a &lt;a href="https://svelte.dev/docs#use_action"&gt;Svelte Action&lt;/a&gt; to attach and remove the events listeners for trapping focus.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!-- alert.svelte --&amp;gt;
&amp;lt;script&amp;gt;
  export let show
  export let close
  export let title

  const id = slugify(title)

  import focusTrap from 'actions/focus-trap.js'
&amp;lt;/script&amp;gt;

&amp;lt;svelte:window on:keydown={event =&amp;gt; {event.key === 'Escape' ? close() : null}}/&amp;gt;

{#if show}
  &amp;lt;dialog
    open
    on:click={close}
    role='dialog'
    aria-labelledby={`${id}-title`}
    aria-describedby={`${id}-content`}
    tabindex='-1'
    use:focusTrap
  &amp;gt;
    &amp;lt;section&amp;gt;
      &amp;lt;header id={`${id}-title`}&amp;gt;
        &amp;lt;strong&amp;gt;
          {title}
        &amp;lt;/strong&amp;gt;
        &amp;lt;button on:click={close}&amp;gt;
          Close
        &amp;lt;/button&amp;gt;
      &amp;lt;/header&amp;gt;

      &amp;lt;div id={`${id}-content`}&amp;gt;
        &amp;lt;slot /&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/section&amp;gt;
  &amp;lt;/dialog&amp;gt;
{/if}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An &lt;code&gt;action&lt;/code&gt; is just a function that will be passed the &lt;code&gt;node&lt;/code&gt; on which the &lt;code&gt;use:&lt;/code&gt; directive was called. The function is called as soon as the element is created, which in the case of my alert means whenever the &lt;code&gt;show&lt;/code&gt; prop becom &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The first thing I want to do when an alert is created is focus on it, which I can do with the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLOrForeignElement/focus"&gt;&lt;code&gt;.focus()&lt;/code&gt; method&lt;/a&gt; thanks to the &lt;code&gt;tabindex='-1&lt;/code&gt; from earlier.&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;// focus-trap.js&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;focusTrap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;focus&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;The next step is to get a list of every element that should be focusable within the &lt;code&gt;&amp;lt;dialog /&amp;gt;&lt;/code&gt; box. Taking inspiration from &lt;a href="https://zellwk.com/blog/keyboard-focusable-elements/"&gt;this article by Zell Liew&lt;/a&gt;, I used a list of natively focusable elements and created a &lt;code&gt;focusableElements&lt;/code&gt; array, filtering out any that should be skipped.&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;// focus-trap.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;elements&lt;/span&gt; &lt;span class="o"&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;a&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;button&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;input&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;textarea&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;select&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;details&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;[tabindex]&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="nx"&gt;focusTrap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;focus&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;focusableElements&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;element&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="nx"&gt;elements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&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="p"&gt;))]&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hasAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;disabled&lt;/span&gt;&lt;span class="dl"&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;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tabIndex&lt;/span&gt; &lt;span class="o"&gt;===&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, borrowing more logic from &lt;a href="https://hiddedevries.nl/en/blog/2017-01-29-using-javascript-to-trap-focus-in-an-element"&gt;this article by Hidde de Vries&lt;/a&gt;, the &lt;code&gt;&amp;lt;dialog /&amp;gt;&lt;/code&gt; can listen for all &lt;code&gt;keydown&lt;/code&gt; events and check if they were the &lt;code&gt;Tab&lt;/code&gt; key. &lt;/p&gt;

&lt;p&gt;If a user has pressed the &lt;code&gt;Tab&lt;/code&gt; key, I want to prevent the default response and implement my own. The two things I need to know for this custom behavior are which element is currently in focus, which I can do by checking the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/activeElement"&gt;&lt;code&gt;document.activeElement&lt;/code&gt;&lt;/a&gt; against my &lt;code&gt;focusableElements&lt;/code&gt; array, and whether the user intends to move forwards or backwards through the DOM, which I can do by checking whether or not they have included the &lt;code&gt;shiftKey&lt;/code&gt;. The idea is to call &lt;code&gt;.focus()&lt;/code&gt; on the next &lt;code&gt;focusableElements&lt;/code&gt; item when the user presses just &lt;code&gt;Tab&lt;/code&gt;, and on the previous element if they have pressed &lt;code&gt;Shift + Tab&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;// focus-trap.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;elements&lt;/span&gt; &lt;span class="o"&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;a&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;button&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;input&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;textarea&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;select&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;details&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;[tabindex]&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="nx"&gt;focusTrap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;focus&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;focusableElements&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;element&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="nx"&gt;elements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&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="p"&gt;))]&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hasAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;disabled&lt;/span&gt;&lt;span class="dl"&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;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tabIndex&lt;/span&gt; &lt;span class="o"&gt;===&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;element&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;keydown&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&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;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Tab&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&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;keyCode&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;9&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;currentElementIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;focusableElements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;indexOf&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;activeElement&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;preventDefault&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;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shiftKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;currentElementIndex&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="nx"&gt;focusableElements&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;focusableElements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;focus&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
          &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;focusableElements&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;currentElementIndex&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;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;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;currentElementIndex&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;focusableElements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
          &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;focusableElements&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="nx"&gt;focus&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
          &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;focusableElements&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;currentElementIndex&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;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;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;Navigating the &lt;code&gt;focusableElements&lt;/code&gt; array is slightly more complicated because the user needs to loop through the elements if they move forwards from the last element or backwards from the first.&lt;/p&gt;

&lt;p&gt;Dealing with focus trapping can get much more complicated than what I need to account for in my fairly simple alert. Luckily, there are existing libraries out there to help deal with these complicated situations. If I were dealing with this again, I might reach for something like &lt;a href="https://github.com/focus-trap/focus-trap"&gt;&lt;code&gt;focus-trap&lt;/code&gt;&lt;/a&gt;, or maybe even &lt;a href="https://github.com/Duder-onomy/svelte-focus-trap"&gt;&lt;code&gt;svelte-focus-trap&lt;/code&gt;&lt;/a&gt; which takes this same logic and pre-packages it into an &lt;code&gt;action&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Persisting data with &lt;code&gt;localStorage&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The very last thing to do is make sure that users don't see the same alert over and over, every time they re-visit the site. Since this data is going to be specific to a user and I want it to persist between visits, &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage"&gt;&lt;code&gt;localStorage&lt;/code&gt;&lt;/a&gt; is a good candidate. &lt;/p&gt;

&lt;p&gt;When a user closes the alert, instead of just toggling the &lt;code&gt;showAlert&lt;/code&gt; variable, the function also needs to write to the browser's storage. &lt;code&gt;localStorage&lt;/code&gt; will store key-value pairs, so the function can set &lt;code&gt;false&lt;/code&gt; for &lt;code&gt;'labAlert'&lt;/code&gt; and this will exist until a user manually clears their browser data.&lt;/p&gt;

&lt;p&gt;When the page loads, the component also needs to look into &lt;code&gt;localStorage&lt;/code&gt; and see if the user has closed the modal in the past. Svelte component code runs in both the client, where the &lt;code&gt;window&lt;/code&gt; object exists, and on the server, where &lt;code&gt;window&lt;/code&gt; will be undefined. Before trying to access &lt;code&gt;window.localStorage&lt;/code&gt; the component needs to check if &lt;code&gt;typeof window !== 'undefined'&lt;/code&gt; to avoid erroring on the server. This check isn't necessary inside of &lt;code&gt;closeAlert&lt;/code&gt; since running that function can only be triggered by a user in their browser and not ever on the server. It's also important to check if the returned value is equal to a string value of &lt;code&gt;'false'&lt;/code&gt; rather than the boolean &lt;code&gt;false&lt;/code&gt; because &lt;code&gt;localStorage&lt;/code&gt; will coerce any set value to a string.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!-- lab.svelte --&amp;gt;
&amp;lt;script&amp;gt;
  import Page from './page.svelte'
  import Alert from '../components/misc/alert.svelte'

  let showAlert = true
  if (typeof window !== 'undefined' &amp;amp;&amp;amp; window.localStorage.getItem('labAlert') === 'false') {
    showAlert = false
  }

  const closeAlert = () =&amp;gt; { 
    showAlert = false
    window.localStorage.setItem('labAlert', false)
  }
&amp;lt;/script&amp;gt;

&amp;lt;Page {...$$props} &amp;gt;

  &amp;lt;slot /&amp;gt;

  &amp;lt;Alert
    title='Caution!'
    show={showAlert}
    slot='alert'
  &amp;gt;
    &amp;lt;p&amp;gt;
      Some features in the section require JavaScript, 
      might not work great on mobile, 
      and may not have been thoroughly tested.
    &amp;lt;/p&amp;gt;

    &amp;lt;button on:click={closeAlert}&amp;gt;
      That's okay.  
    &amp;lt;/button&amp;gt;
  &amp;lt;/Alert&amp;gt;

&amp;lt;/Page&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But wait... what if the content in the modal changes and a user needs to see new, important data before they proceed? This can be accomplished by storing the &lt;em&gt;content of the alert&lt;/em&gt; in &lt;code&gt;localStorage&lt;/code&gt; instead of only a one time boolean value.&lt;/p&gt;

&lt;p&gt;The text content of the alert can be abstracted to a variable that will hold the HTML as a string. It can then be used in the body of the component using Svelte's &lt;a href="https://svelte.dev/docs#html"&gt;&lt;code&gt;@html&lt;/code&gt; template syntax&lt;/a&gt;. When a user calls the &lt;code&gt;closeAlert&lt;/code&gt; function, instead of storing &lt;code&gt;'false'&lt;/code&gt;, the function can use the browser's &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/btoa"&gt;&lt;code&gt;btoa&lt;/code&gt; method&lt;/a&gt; to convert the &lt;code&gt;alertContent&lt;/code&gt; to a &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/Base64"&gt;base64 string&lt;/a&gt; and store it. Since &lt;code&gt;btoa(alertContent)&lt;/code&gt; will return a different string given new content, when the component checks &lt;code&gt;localStorage.getItem('labAlert')&lt;/code&gt;, &lt;code&gt;showAlert&lt;/code&gt; will be set only if a user has seen this exact content before.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!-- lab.svelte --&amp;gt;
&amp;lt;script&amp;gt;
  import Page from './page.svelte'
  import Alert from '../components/misc/alert.svelte'

  const alertContent = `
    &amp;lt;p&amp;gt;
      Some features in the section require JavaScript, 
      might not work great on mobile, 
      and may not have been thoroughly tested.
    &amp;lt;/p&amp;gt;
  `

  let showAlert = true
  if (typeof window !== 'undefined' &amp;amp;&amp;amp; window.localStorage.getItem('labAlert') === btoa(alertContent)) {
    showAlert = false
  }

  const closeAlert = () =&amp;gt; { 
    showAlert = false
    window.localStorage.setItem('labAlert', btoa(alertContent))
  }
&amp;lt;/script&amp;gt;

&amp;lt;Page {...$$props} &amp;gt;

  &amp;lt;slot /&amp;gt;

  &amp;lt;Alert
    title='Caution!'
    show={showAlert}
    slot='alert'
  &amp;gt;
    {@html alertContent}

    &amp;lt;button on:click={closeAlert}&amp;gt;
      That's okay.  
    &amp;lt;/button&amp;gt;
  &amp;lt;/Alert&amp;gt;

&amp;lt;/Page&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;btoa&lt;/code&gt;, just like &lt;code&gt;localStorage&lt;/code&gt; only exists in the browser so it will need the same &lt;code&gt;window&lt;/code&gt; check to not error on the server. Since &lt;code&gt;btoa&lt;/code&gt; (which stands for "binary to ASCII") returns a string, it can be used directly in the &lt;code&gt;===&lt;/code&gt; check, unlike a boolean that needs to be coerced.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Final Product
&lt;/h2&gt;

&lt;p&gt;The final component should be something that has both an ergonomic experience for the developer, and a non-intrusive experience for the site user. Hopefully I managed to accomplish both.&lt;/p&gt;

&lt;p&gt;As usual, thanks to the &lt;a href="https://discord.com/channels/457912077277855764/"&gt;Svelte discord&lt;/a&gt; for helping me figure out a few aspects of the code, particularly &lt;code&gt;kursus75&lt;/code&gt; and &lt;code&gt;c-bandy&lt;/code&gt; with help around &lt;code&gt;localStorage&lt;/code&gt; and Svelte &lt;code&gt;stores&lt;/code&gt; and &lt;code&gt;GrygrFlzr&lt;/code&gt; for help with my deploy.&lt;/p&gt;

&lt;p&gt;I hope this walkthrough helps people build their own inclusive version of this component. If this does help you build something cool, shoot me a link on &lt;a href="https://twitter.com/ryanfiller_"&gt;Twitter&lt;/a&gt; because I'd love to see it! Reach out with any questions or concerns too, especially if there are any errors I need to fix with this component's accessibility.&lt;/p&gt;

&lt;p&gt;Also head over to the &lt;a href="https://www.ryanfiller.com//lab"&gt;&lt;code&gt;/lab&lt;/code&gt;&lt;/a&gt; section of my site to see this component in action, as well as all the weird experiments I've got there.&lt;/p&gt;

</description>
      <category>code</category>
      <category>svelte</category>
      <category>ui</category>
      <category>a11y</category>
    </item>
    <item>
      <title>Why My Blog Stopped Using Deploy Previews</title>
      <dc:creator>ryanfiller</dc:creator>
      <pubDate>Mon, 31 May 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/ryanfiller/why-my-blog-stopped-using-deploy-previews-5eka</link>
      <guid>https://dev.to/ryanfiller/why-my-blog-stopped-using-deploy-previews-5eka</guid>
      <description>&lt;p&gt;I love Netlify. I think it's a wonderful service that provides &lt;em&gt;tremendous&lt;/em&gt; value to many developers and teams. But, like so many things in the modern world, I think it’s easy to overlook some of the behind-the-scenes consequences of convenience. This post examines the cost of what some of those conveniences, how to change some default settings to lessen their impact, and what some situational alternatives can be.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are Deploy Previews?
&lt;/h2&gt;

&lt;p&gt;One of the core tenants of &lt;a href="https://jamstack.org/"&gt;JAMstack&lt;/a&gt; is that a site builds, then it deploys. Netlify, like any other host for &lt;a href="https://jamstack.org/glossary/ssg/"&gt;statically built sites&lt;/a&gt;, makes use of "&lt;a href="https://jamstack.org/glossary/immutable/"&gt;immutable deploys&lt;/a&gt;."&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;While a "mutable" item can change (be mutated) over time, an "immutable" item cannot. Once created, an immutable deploy of a website becomes an artifact which will not change. Instead, deploys result in new versions or instances of the site, and traffic is routed to them accordingly.&lt;/p&gt;

&lt;p&gt;When paired with &lt;a href="https://jamstack.org/glossary/atomic/"&gt;atomic deploys&lt;/a&gt;, immutable builds make it possible for sites to enjoy abilities such as instant rollbacks and versioning, and help to ensure that the code and assets of a website can be maintained in a known state.&lt;/p&gt;



&lt;p&gt;&lt;a href="https://jamstack.org/glossary/immutable/"&gt;jamstack.org&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A &lt;a href="https://www.netlify.com/blog/2016/07/20/introducing-deploy-previews-in-netlify/"&gt;Preview Deploy&lt;/a&gt; is something special that Netlify adds to atomic deployment.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Deploy Previews work by deploying every pull request from your Git repository to a unique URL; completely different from the one your main site uses. You and your team can see how those changes look before they’re merged into the main branch and deployed to production.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Preview Deploys are useful because they let you test a new feature in a production-like environment, share a url to get feedback before a release, or easily cross-browser check a site.&lt;/p&gt;

&lt;p&gt;The Deploy Preview settings for a site live at &lt;code&gt;Settings &amp;gt; Deploys &amp;gt; Deploy Contexts&lt;/code&gt;, and the defaults look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5sJGdn9X--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ryanfiller.com/images/netlify-deploy-settings-default.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5sJGdn9X--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ryanfiller.com/images/netlify-deploy-settings-default.png" alt="the default deploy settings for netlify - Deploy Previews: Any pull request against your production branch / Branch deploys: Deploy only the production branch"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The different options for each setting look like this:&lt;/p&gt;

&lt;p&gt;One interesting thing is that the &lt;code&gt;Any pull request against your production branch&lt;/code&gt; option will still build a preview even for a &lt;a href="https://github.blog/2019-02-14-introducing-draft-pull-requests/"&gt;draft pull request&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  With Great Power... Comes Great Responsibility
&lt;/h2&gt;

&lt;p&gt;I can’t find a source for the first person who said this, but it is a well known best practice to “commit small; commit often.” While this is generally the best way to use version control, this could cause &lt;em&gt;a lot&lt;/em&gt; of builds using Netlify’s default settings.&lt;/p&gt;

&lt;p&gt;This is a habit I picked up at my day job where I work on a team, but I usually open a pull request fairly soon after I create a new branch. I know most people probably don’t work this way, but I make heavy use of git’s &lt;a href="https://git-scm.com/docs/git-rebase"&gt;&lt;code&gt;rebase&lt;/code&gt;&lt;/a&gt; functionality and keeping an open PR is a good safety net when changing the commit history.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ylscdv3n--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ryanfiller.com/images/github-merged-rebased-pr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ylscdv3n--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ryanfiller.com/images/github-merged-rebased-pr.png" alt="screenshot of a rebased and merged pr, https://github.com/ryanfiller/portfolio-svelte/pull/30"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Any pull request regardless of open, closed, or merged status, keeps a history of commits that have been force pushed to it. This PR, merged months ago, still lets me access code from the commits &lt;a href="https://github.com/ryanfiller/portfolio-svelte/commit/23555ed3194c60a85e8959c1b38354c0ac2ccefa"&gt;23555ed&lt;/a&gt;, &lt;a href="https://github.com/ryanfiller/portfolio-svelte/commit/d58c98986d5e031309cc1498bca8ff0f93696ae6"&gt;d58c989&lt;/a&gt;, and &lt;a href="https://github.com/ryanfiller/portfolio-svelte/commit/902238287edb73fb975d68ffb76c5b8b71676d07"&gt;9022382&lt;/a&gt;. If you mess up during a rebase and push something you didn't mean to, having an open PR can be a &lt;em&gt;much&lt;/em&gt; easier way of recovering code than trying to fish it out of the &lt;a href="https://git-scm.com/docs/git-reflog"&gt;local &lt;code&gt;reflog&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Though once this PR is open, Netlify's pipeline will become aware of it and will start building a preview deploy any time you push code.&lt;/p&gt;

&lt;p&gt;Also, some tools, like &lt;a href="https://dependabot.com/"&gt;dependabot&lt;/a&gt;, might automatically come with a starter project and &lt;a href="https://github.com/ryanfiller/bearded-robots-gatsby/pulls"&gt;keep making PRs&lt;/a&gt; after you've forgotten about a project.&lt;/p&gt;

&lt;p&gt;Even Netlify's own &lt;a href="https://www.netlifycms.org/"&gt;CMS&lt;/a&gt; might be creating deploys without you realizing it. NetlifyCMS is a &lt;a href="https://bejamas.io/blog/git-based-cms-vs-api-first-cms/"&gt;git-based CMS&lt;/a&gt;, so of course it will be making commits as content is added or changed. But, even with the &lt;a href="https://www.netlifycms.org/docs/configuration-options/#publish-mode"&gt;&lt;code&gt;editorial_workflow&lt;/code&gt;&lt;/a&gt; option enabled, the CMS will open a draft PR and still create a deploy.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--m_42zM4a--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ryanfiller.com/images/editorial_workflow_draft_pr_build.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--m_42zM4a--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ryanfiller.com/images/editorial_workflow_draft_pr_build.png" alt="netlify cms editoral workflow draft post, github draft pr, and netlify preview build screenshots"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;None of this is meant to knock these tools at all! I've &lt;a href="https://www.ryanfiller.com/blog/tips-and-tricks-ive-learned-about-gatsby-with-netlifycms"&gt;written in the past&lt;/a&gt; about how much I enjoy NetlifyCMS and I still use it for small freelance sites that need to be editable by clients. That point I'm trying to make is that it is important to know how a tool works when you use it. In this case creating a post will create a pull request, and then every subsequent save of that page will create a commit, and thus a deploy.&lt;/p&gt;

&lt;p&gt;This is not something I always knew I needed to be careful about. &lt;a href="https://github.com/ryanfiller/portfolio-gatsby-v2/pull/67"&gt;One of my posts&lt;/a&gt; alone generated almost 40 separate deploys.&lt;/p&gt;

&lt;h2&gt;
  
  
  Some Downsides to "Single Use Deploys"
&lt;/h2&gt;

&lt;p&gt;One thing that might make someone start paying attention to how many deploys they are creating is &lt;a href="https://www.netlify.com/pricing/faq/"&gt;Build Minute&lt;/a&gt; usage. Netlify's plans only offer so many minutes per month before the next tier of billing kicks in. I'm sure everyone wants to avoid incurring any unexpected costs for a site, but I wanted to look at the other cost - the deploys themselves.&lt;/p&gt;

&lt;p&gt;Because of the nature of how Netlify's roll backs work, deploys &lt;strong&gt;never go away&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When paired with &lt;a href="https://jamstack.org/glossary/atomic/"&gt;atomic deploys&lt;/a&gt;, immutable builds make it possible for sites to enjoy abilities such as instant rollbacks and versioning, and help to ensure that the code and assets of a website can be maintained in a known state.&lt;/p&gt;



&lt;p&gt;&lt;a href="https://jamstack.org/glossary/immutable/"&gt;jamstack.org&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I reached out to Netlify about how exactly their deploy process works. Their customer service rep told me this was getting close to "technical secrets territory," but was able to link me to this helpful blog post by &lt;a href="https://twitter.com/cassidoo"&gt;Cassidy Williams&lt;/a&gt;. The section about &lt;a href="https://www.netlify.com/blog/2021/03/08/incremental-static-regeneration-its-benefits-and-its-flaws/#what-happens-when-you-deploy-a-jamstack-project"&gt;what happens when you deploy a JAMstack project&lt;/a&gt; demonstrates (with gifs!) how the deploy process happens.&lt;/p&gt;

&lt;p&gt;Netlify's deploys work a little bit like git commits — each one builds on previous ones, which is why you're able to easily "roll back" to a previous atomic state. In order to maintain the ability to always and instantly revert to a previous deploy, Netlify doesn't let you remove them.&lt;/p&gt;

&lt;p&gt;And this isn't just production deploys, this applies to preview deploys as well. To be honest, I'm not sure why this is and I couldn't find a definitive answer anywhere. It could be that these deploys are cached or reused in some way to support production builds, or it could just be that there is no meaningful technical distinction between build types in Netlify's system.&lt;/p&gt;

&lt;p&gt;As far as I &lt;em&gt;can&lt;/em&gt; tell, unless you delete a site from Netlify altogether, once you create a deploy it exists forever. &lt;a href="https://5e0b504fecfc7e00083838f7--2020-ryanfiller-gatsby.netlify.app/blog/starting-fresh-in-2020"&gt;Here&lt;/a&gt; is the first draft of the first post for this blog from December 31, 2019 at 7:42am. It's probably riddled with typos. &lt;a href="https://5a68053f0b79b754c796ddf1--2018-ryanfiller.netlify.app/page-2/"&gt;Here&lt;/a&gt; is my first commit to my first Gatsby project while I was working through their tutorial, deployed January 23, 2018 at 10:02pm. &lt;a href="https://59bd735c6f4c506c3503ed30--2016-ryanfiller.netlify.app/"&gt;Here&lt;/a&gt; is my &lt;em&gt;very first&lt;/em&gt; deploy ever to Netlify, from September 16, 2017 at 1:54pm, just called "refactor."&lt;/p&gt;

&lt;p&gt;It might seem cool that these sites exist forever, but ask yourself how often you &lt;em&gt;really&lt;/em&gt; need to go back and look at the state of a site from over a year ago? Or even a few months ago? Also, consider this tweet from &lt;a href="https://twitter.com/gerrymcgovern"&gt;Gerry McGovern&lt;/a&gt; -&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SguHFuhj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ryanfiller.com/images/world_wide_waste_book_cover.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SguHFuhj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ryanfiller.com/images/world_wide_waste_book_cover.jpg" alt="the book cover of 'World Wide Waste'"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Last year I read Gerry's book, &lt;a href="https://gerrymcgovern.com/books/world-wide-waste/"&gt;&lt;em&gt;World Wide Waste&lt;/em&gt;&lt;/a&gt;. The gist of the book is to try and get everyone to consider the physical world ramifications of their digital world choices. One series of facts really stood out to me -&lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Around 90% of data is never accessed three months after it is first stored.&lt;/li&gt;
&lt;li&gt;80% of all digital data is never accessed or used again after it is stored.&lt;/li&gt;
&lt;li&gt;Businesses typically only analyze around 10% of the data they collect.&lt;/li&gt;
&lt;li&gt;90% of unstructured data is never analyzed.&lt;/li&gt;
&lt;li&gt;90% of all sensor data collected from Internet of Things devices is never used.&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;&lt;a href="https://gerrymcgovern.com/books/world-wide-waste/"&gt;from Gerry McGovern's website&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For a lot of people, myself sometimes included, these Deploy Previews are artifacts that are never even looked at.&lt;/p&gt;

&lt;h2&gt;
  
  
  Estimating the Cost of This Site
&lt;/h2&gt;

&lt;p&gt;After seeing &lt;a href="https://twitter.com/gerrymcgovern/status/1383017700218310659"&gt;Gerry's tweet&lt;/a&gt; I wanted to try to calculate a number for the amount of CO2 my blog has generated via hosting my deploys. I &lt;em&gt;really&lt;/em&gt; want to stress that this is a ballpark number. I'm not an expert on things like compression and bandwidth and I'm working with a lot of assumptions and averages. Hopefully I'll get fairly close.&lt;/p&gt;

&lt;p&gt;The first thing I did is determine how big each deploy is. This is kind of a tough number to get for a few reasons. First, the site gets bigger as I add more content and features to it, so I know I'll be looking at some kind of average. Second, roughly halfway between &lt;a href="https://www.ryanfiller.com/blog/starting-fresh-in-2020"&gt;relaunching in 2020&lt;/a&gt; and now I changed frameworks from &lt;a href="https://www.gatsbyjs.com/"&gt;Gatsby&lt;/a&gt; to &lt;a href="https://sapper.svelte.dev/"&gt;Sapper&lt;/a&gt; which definitely changed the overall bundle size of my site.&lt;/p&gt;

&lt;p&gt;I looked around in Netlify to see if they had any info on storage size but the closest metric I could find was bandwidth used per month. Again, I'm not an expert in this, so the best way I could figure out the size of a site was to use &lt;a href="https://ricks-apps.com/osx/sitesucker/index.html"&gt;SiteSucker&lt;/a&gt; to download it and check its size on my local disk. After getting a rough estimate for month and averaging them, these are the numbers I got.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Gatsby&lt;/th&gt;
&lt;th&gt;Sapper&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Average Size&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;79759.73 kb (0.08 gb)&lt;/td&gt;
&lt;td&gt;33000.00 kb (0.03 gb)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;I use &lt;a href="https://www.netlify.com/products/large-media/"&gt;Netlify Large Media&lt;/a&gt; to handle hosting media assets and those &lt;em&gt;should&lt;/em&gt; be versioned outside of site deploys and then only pulled in by url reference. However, for the demonstration here I'm going to leave them in the calculation.&lt;/p&gt;

&lt;p&gt;Now that I knew about how big each deploy is, the next step was to figure out how many times the site has deployed. This number was pretty easy to get. For each project I went to the &lt;code&gt;Project &amp;gt; Deploys&lt;/code&gt; tab, counted the number of deploys per page then multiplied by the number of pages. I did this both for my Production and Preview deploys since neither can be removed.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Gatsby&lt;/th&gt;
&lt;th&gt;Sapper&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Preview Deploys&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;73&lt;/td&gt;
&lt;td&gt;205&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total Deploys&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;319&lt;/td&gt;
&lt;td&gt;261&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The extremely large number of Sapper deploys vs Gatsby deploys seems to be because I was working with a feature I needed to test against a bot crawler, and this wouldn't work running the site locally.&lt;/p&gt;

&lt;p&gt;To get a final number for each, I multiplied these totals by the average size of each deploy.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Gatsby&lt;/th&gt;
&lt;th&gt;Sapper&lt;/th&gt;
&lt;th&gt;Total&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Average Size * Preview Deploys&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;5.84 gb&lt;/td&gt;
&lt;td&gt;6.15 gb&lt;/td&gt;
&lt;td&gt;11.99 gb&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Average Size * Total Deploys&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;25.52 gb&lt;/td&gt;
&lt;td&gt;7.83 gb&lt;/td&gt;
&lt;td&gt;33.35 gb&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If I were to hypothetically stop working on my site and leave it with its current number of deploys for an entire year, that means I can take this total number and multiply it by Gerry's calculation of 30 grams per 1gb of stored data. That comes out to 910.5 grams, or just over two pounds. Based on this &lt;a href="https://climatekids.nasa.gov/review/carbon/gasoline.html"&gt;random nasa.gov link&lt;/a&gt; I found, that's about one 10th of a gallon of burned gasoline.&lt;/p&gt;

&lt;p&gt;About a third of that overall number comes from Deploy Previews, and I want to reference two of the numbers from &lt;em&gt;World Wide Waste&lt;/em&gt; again -&lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Around 90% of data is never accessed three months after it is first stored.&lt;/li&gt;
&lt;li&gt;80% of all digital data is never accessed or used again after it is stored.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;These are deploys I simply do not need to keep forever. And because these deploys are out there and can't be removed, they'll compound this storage cost every year that they continue to exist. There is an argument to be made that a lot of those Production Deploys aren't needed either, but theoretically Netlify should keep them around in case I ever need to roll back to one of them.&lt;/p&gt;

&lt;p&gt;This might seem insignificant, but keep in mind this is only one project of mine. Granted I don't actively work on them nearly as much as I do my blog, but I currently have &lt;em&gt;two dozen&lt;/em&gt; sites hosted on Netlify. Multiply this across Netlify's &lt;a href="https://million-devs.netlify.com/"&gt;over 1,000,000 devs&lt;/a&gt; and the scope is anything but insignificant.&lt;/p&gt;

&lt;p&gt;If every Netlify user has only one site that they have deployed as much as this blog, that means the equivalent 100,000 gallons of gasoline burned just for one year of storage alone. According to &lt;a href="https://www.epa.gov/energy/greenhouse-gas-equivalencies-calculator"&gt;epa.gov&lt;/a&gt;, that's the same as driving 2,233,479 miles in a car in the same amount of time. To offset this amount of C02 you would need to plant 14,695 trees and let them grow for &lt;em&gt;ten years&lt;/em&gt;!&lt;/p&gt;

&lt;h2&gt;
  
  
  What Can You Do?
&lt;/h2&gt;

&lt;p&gt;I'm certainly not suggesting that anybody &lt;em&gt;not&lt;/em&gt; use Netlify. On the other hand, I have made some changes to how and why I use Netlify by tweaking my Deploy Settings.&lt;/p&gt;

&lt;p&gt;First, I have &lt;code&gt;Deploy Previews&lt;/code&gt; set to &lt;code&gt;Don’t deploy pull requests&lt;/code&gt;. This makes sure that my "make a PR as a rebase safety net" workflow doesn't create deploys, and neither do things like &lt;code&gt;dependabot&lt;/code&gt;. Second, instead of &lt;code&gt;Branch Deploys&lt;/code&gt; set to &lt;code&gt;Deploy only the production branch&lt;/code&gt; I use &lt;code&gt;Let me add individual branches&lt;/code&gt; where I set up a dedicated &lt;code&gt;staging&lt;/code&gt; branch that I can merge into any time I want to manually generate a preview.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--x_c454_S--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ryanfiller.com/images/netlify-deploy-settings-custom.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--x_c454_S--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ryanfiller.com/images/netlify-deploy-settings-custom.png" alt="my deploy settings for netlify - Deploy Previews: Don't deploy pull requests / Branch deploys: staging"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I've also made a conscious effort to only use my &lt;code&gt;staging&lt;/code&gt; branch when I'm trying out a new feature that needs to be tested in as close to the actual production environment as possible. For things like getting a blog post proofread or trying to do a quick check of some styles on a mobile device I've switched over to using &lt;a href="https://surge.sh/"&gt;surge.sh&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  surge.sh
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zbegTrTz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ryanfiller.com/images/surge-sh-logo.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zbegTrTz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ryanfiller.com/images/surge-sh-logo.svg" alt="surge.sh logo, its a walrus"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;surge.sh is not a new tool. It, along with &lt;a href="https://codeship.com"&gt;CodeShip&lt;/a&gt;, is what I used to use to host and deploy my site before I moved to using Netlify. surge.sh and Netlify are similar in some ways, with surge.sh being a more stripped down version of the same type of static hosting service. After &lt;a href="https://surge.sh/help/getting-started-with-surge"&gt;installing the &lt;code&gt;surge cli&lt;/code&gt;&lt;/a&gt;, any directory can be instantly deployed to a surge.sh url.&lt;/p&gt;

&lt;p&gt;The CLI accepts a number of configuration flags for options such as which directory to publish and which url to publish use. To make this all a little more automatic, I added a &lt;code&gt;surge&lt;/code&gt; script to the &lt;code&gt;scripts&lt;/code&gt; section of my &lt;code&gt;package.json&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"surge": "npm run export &amp;amp;&amp;amp; surge --project ./ __sapper__ /export --domain beta.ryanfiller.com"

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This lets me run &lt;code&gt;npm run surge&lt;/code&gt; which will export the static version of my site and push it to the subdomain &lt;a href="http://beta.ryanfiller.com/"&gt;&lt;code&gt;beta.ryanfiller.com&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;One nice thing about this is that by pushing to the same url over and over again, I can overwrite the version of the site that was previously hosted there. I like this because it means that when I link someone to a preview of a blog post they don't have to sift through different versions of hashed urls. They can visit the &lt;code&gt;beta&lt;/code&gt; subdomain and see the draft that I most recently pushed.&lt;/p&gt;

&lt;p&gt;I reached out about how surge.sh works behind the scenes, and they were actually nice enough to let me in on a semi-secret upcoming version of the CLI. This version was described as "unreleased but actually quite stable", and I've had no issues using it. A preview of it can be found (at the time of me writing this, anyways) by bumping your version up to the current release candidate.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install -g surge@edge

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After this version of &lt;code&gt;surge&lt;/code&gt; is installed you can run &lt;code&gt;surge --help&lt;/code&gt; and see a list of all the new available commands. There's a &lt;em&gt;ton&lt;/em&gt; of good stuff here, but I wanted to highlight a few that are relevant to the topic of website size.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
bash&lt;br&gt;
$ surge -h&lt;/p&gt;

&lt;p&gt;Surge.sh ⚡ Static Web Publishing 0.24.0-rc.6&lt;/p&gt;

&lt;p&gt;surge list  list all project revisions&lt;br&gt;
  surge discard  remove revision from system&lt;br&gt;
  surge audit  audit edgenode state&lt;br&gt;
  surge teardown  tear down a published project&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
To be honest, until seeing this new list of commands and running some of them, I had a pretty big misconception about how surge.sh actually works. I was under the impression that when you push to an existing url the old site was gone and replaced with the new one. Running `surge list` against some of my old deploys actually showed me that every version of them was still around, similar to Netlify. The big win here, to me, is being able to run `surge discard` against a particular deploy and remove it once you're done with it. And of course, if you want to remove a project entirely you can run `surge teardown`. `surge audit` was also super cool, it shows a list of deploys with their unique `id`, total file count, and how many megabytes of space they're taking up on the surge.sh servers.

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;$ surge audit beta.ryanfiller.com&lt;/p&gt;

&lt;p&gt;sfo-15 1619787001755 255 files 55.35 MB&lt;br&gt;
   sjc-00 1619787001755 255 files 55.35 MB&lt;br&gt;
   jfk-01 1619787001755 255 files 55.35 MB&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


This makes surge.sh an awesome tool, and one that puts more power back in your hands as far as managing what artifacts from your site persist forever. Netlify is a pretty big ecosystem at this point, with [SSL](https://en.wikipedia.org/wiki/Transport_Layer_Security), Large Media, Functions, Auth, Analytics, and so much else, but for some simple projects in the future I think I'm going to look at hosting them solely on surge.sh

## My Netlify Wish List

This isn't meant to be a list of _demands_, only a list of things I'd do if I had some magic wishes and could make changes. I'm sure that some of these are a _lot_ more technically challenging than they seem on the surface. In no particular order:

### Don't Automatically Deploy Draft PRs

[GitHub added draft pull requests](https://github.blog/2019-02-14-introducing-draft-pull-requests/) pretty recently, but it does seem like this data is available in their [REST API](https://docs.github.com/en/github/searching-for-information-on-github/searching-on-github/searching-issues-and-pull-requests#search-for-draft-pull-requests). It looks like GitLab also already supports [draft merge requests](https://docs.gitlab.com/ee/user/project/merge_requests/drafts.html) and Bitbucket [is working on a similar feature](https://jira.atlassian.com/browse/BCLOUD-12503).

Maybe in the future Netlify could add an additional configuration knob to decide whether or not a draft should create a build?

This would also solve the issue where NetlifyCMS accidentally creates a deploy on every save of the page.

### Add More Information to Outgoing Webhooks

I didn't even touch on this in this post, but it would be nice to customize [Deploy Notifications](https://docs.netlify.com/site-deploys/notifications/) and [Build Hooks](https://docs.netlify.com/configure-builds/build-hooks/) a little more. My main blog uses these to [syndicate posts](https://www.ryanfiller.com/blog/getting-data-in-and-out-of-gatsby) to [another domain](https://ryan.beardedrobots.com/), and builds of the secondary site are currently triggered by both Production and Previews Deploys of the main site.

There are already a lot of options around Deploy Preview in `Settings &amp;gt; Deploys &amp;gt; Deploy Notifications`, maybe somehow loop these into the `Outgoing Webhook` option?

### Change The Default Settings

I know that within the last few weeks Netlify has announced a [cool, new feature for Deploy Previews](https://www.netlify.com/products/deploy-previews/). Honestly, I'm kind of jealous that such an integrated tool didn't exist when I worked at an agency and needed to gather client feedback. But, especially since, as of now, every deploy is forever, is an automatic deploy for every PR something that every site needs turned on by default?

Previews are a useful feature for a lot of people, but I'd love to know if the number of deploys created that were never even looked at would go down if this was an opt-in rather than opt-out setting.

### Let Me Remove Deploy Previews

I know this one is a stretch, but I would _love_ to be able to remove all of the one-off previews of my sites that I either didn't mean to make or don't need anymore. I have a feeling that of all my list this is the most technically complex — I understand that granting users this power makes the stack of deploys a bit like a [game of Jenga](https://en.wikipedia.org/wiki/Jenga) where you hope you're not removing a deploy you might need to roll back to later.

## Some Final Thoughts on Netlify

[Incremental Static Regeneration](https://www.netlify.com/tags/incremental-static-regeneration/) is an up-and-coming Netlify feature, and is the actual topic of [Cassidy's article](https://www.netlify.com/blog/2021/03/08/incremental-static-regeneration-its-benefits-and-its-flaws/#what-happens-when-you-deploy-a-jamstack-project) that I linked to earlier. [Jason Lengstorf](https://twitter.com/jlengstorf) and [Phil Hawksworth](https://twitter.com/philhawksworth) were recently guests on two podcasts that I listen to ([ShopTalk Show #464](https://shoptalkshow.com/464/) and [Toolsday #130](https://spec.fm/podcasts/toolsday/wKcuYe6l)) to talk about this feature. ISR is mostly unrelated to the topic of this post, but in both podcast episodes the phrase "burn the electricity to build a bunch of rarely visited pages" was used and it caught my attention both times. This post isn't meant to disparage Deploy Previews in any way. It sounds like Netlify already has put some thought into servers doing needless work during the build process, I would love to see them adopt a similar train of thought about "burning electricity" with hosting as well.

## Acknowledgments and Disclaimers

Thank you to [Gerry McGovern](https://twitter.com/gerrymcgovern) for writing both the book and the tweet that inspired this post. _World Wide Waste_ really has changed how I approach most everything I do online. Also, thank you to [Scott Parker](https://mobile.twitter.com/scottpieparker) and [Brock Whitten](https://twitter.com/sintaxi) from Netlify and surge.sh respectively for doing their best to answer my cryptic "hey, but how does your product _really_ work?" emails. The resources they provided me with helped to fill in a lot of details I was missing about how these technologies work.

I'm wouldn't be surprised there's holes in my math or overall logic. There's probably some type of server-side compression or way to use [gzip](https://www.gzip.org/) I'm not accounting for. Or I didn't factor in how Large Media works correctly. I also didn't account for redundancy with Netlify's [CDN](https://jamstack.org/glossary/cdn/) or the fact that [some of these servers are powered by renewable energy](https://www.netlify.com/sustainability/). This wasn't meant to be a hard breakdown of the numbers, just some quick math to show how much things we tend to not think about can add up at scale.

If you have questions, comments, or concerns about the numbers I showed and how I came up with them, please let me know! Math is not always my strong suit and I'd love to update this post with more correct information.

If this post was meaningful to you in any way, share it! I really want to emphasize that my goal isn't to shame anyone for the tools they use, but instead to get more people to think through the long-term consequences of their short-term actions, especially online in digital spaces.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>code</category>
      <category>greenweb</category>
      <category>jamstack</category>
      <category>devops</category>
    </item>
    <item>
      <title>Building a Color Scheme Toggle</title>
      <dc:creator>ryanfiller</dc:creator>
      <pubDate>Fri, 30 Apr 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/ryanfiller/building-a-color-scheme-toggle-dark-mode-series-28oa</link>
      <guid>https://dev.to/ryanfiller/building-a-color-scheme-toggle-dark-mode-series-28oa</guid>
      <description>&lt;p&gt;In my &lt;a href="http://localhost:3000/blog/choosing-theme-colors#converting-a-palette-to-dark-mode"&gt;last post&lt;/a&gt; I covered how to choose a flexible set of colors and use &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme"&gt;&lt;code&gt;prefers-color-scheme&lt;/code&gt;&lt;/a&gt; to switch them out depending on a user's color preference. This post is about taking that a step further and build a toggle so a user can set and save a color theme.&lt;/p&gt;

&lt;p&gt;My site is built using &lt;a href="https://sapper.svelte.dev/"&gt;Sapper&lt;/a&gt;, so some of the code will be &lt;a href="https://svelte.dev/"&gt;Svelte&lt;/a&gt;-specific. However, most of these concepts are broad enough that they should be applicable other frameworks, or even plain JavaScript. I'll make sure to point out anything that is relying on a Svelte-specific API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up Colors
&lt;/h2&gt;

&lt;p&gt;Using &lt;code&gt;css-in-js&lt;/code&gt; is a &lt;a href="https://css-tricks.com/the-differing-perspectives-on-css-in-js/"&gt;hotly topic debated&lt;/a&gt;, with some valid pros and cons. I've gone back and forth on whether or not to use it, I currently think you should &lt;a href="https://en.wikipedia.org/wiki/Separation_of_concerns#HTML,_CSS,_JavaScript"&gt;separate your concerns&lt;/a&gt;. This method isn't a &lt;code&gt;css-in-js&lt;/code&gt; set up, but I think it's helpful to have a single &lt;a href="https://en.wikipedia.org/wiki/Single_source_of_truth"&gt;source of truth&lt;/a&gt; for any configuration.&lt;/p&gt;

&lt;p&gt;The color chart component I used to &lt;a href="https://dev.to/blog/choosing-theme-colors#choosing-a-color-palette"&gt;build my light and dark themes&lt;/a&gt; needs data from JavaScript. I want this chart to work as an server-side rendered component, so I need to be able to pass it an array of colors without needing to query them from the DOM. In my project I have a &lt;code&gt;styles.js&lt;/code&gt; configuration file that looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const colors = {
  black: '#080025',
  white: '#fefdf2',
  ...
}

const themes = {
  light: {
    text: colors['black'],
    background: colors['white'],
    ...
  },
  dark: {
    text: colors['white'],
    background: colors['black'],
    ...
  }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The two themes also live in this same file, and are constructed by choosing colors from the larger &lt;code&gt;colors&lt;/code&gt; object.&lt;/p&gt;

&lt;p&gt;The mechanics of getting and setting &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/--*"&gt;css custom properties&lt;/a&gt; is kind of verbose, so I created two helpers methods:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function getCustomProperty(property) {
  if (typeof(window) != 'undefined') {
    return getComputedStyle(document.documentElement).getPropertyValue(`--${property}`).replace(/(\"|\')/g, '').trim()
  } else {
    return ''
  }
}

function setCustomProperty(property, value) {
  if (typeof(window) != 'undefined') {
    document.documentElement.style.setProperty(`--${property}`, value)
  }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Svelte will error if it tries to access the &lt;code&gt;window&lt;/code&gt; object during server-side rendering, so it's important to &lt;a href="https://sapper.svelte.dev/docs/#Server-side_rendering"&gt;make sure &lt;code&gt;window&lt;/code&gt; exists&lt;/a&gt; before trying to access any &lt;code&gt;document&lt;/code&gt; properties. I'm doing this because I specifically know I have to account for server-side rendering, but this catch is a best practice to keep the function from throwing any unexpected errors.&lt;/p&gt;

&lt;p&gt;The last helper method is one that will take an array of theme colors, map through and apply the &lt;code&gt;setCustomProperty()&lt;/code&gt; function, then join them into a string of CSS variables.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const setTheme = (theme) =&amp;gt; {
  return Object.entries(theme).map(color =&amp;gt; {
    const [name, value] = color
    return `--color${capitalize(name)}: var(--color${capitalize(value)});`
  }).join('\n')
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This might all seem like a &lt;em&gt;lot&lt;/em&gt; of legwork just to set some color variables, but these helper functions will be super useful later, I promise.&lt;/p&gt;

&lt;h2&gt;
  
  
  Designing the Toggle
&lt;/h2&gt;

&lt;p&gt;Josh W Comeau has a &lt;a href="https://www.joshwcomeau.com/react/dark-mode"&gt;great post&lt;/a&gt; about how he built The Peferct Dark Mode for his site. I thought the section about &lt;a href="https://www.joshwcomeau.com/react/dark-mode/#our-requirements"&gt;requirements&lt;/a&gt; was especially helpful, and is the general plan I'm going to follow for my toggle. My goals were mostly the same as his, but I chose to tackle them in a different order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The site should respect the user's Operating System preference if it is set and supported, if not it will default to light theme.&lt;/li&gt;
&lt;li&gt;The user should be able to click a button to toggle themes.&lt;/li&gt;
&lt;li&gt;If the user has disabled browser JavaScript, the toggle should &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/Graceful_degradation"&gt;not appear&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The site should remember the theme the user toggled to, and should use this over their OS preference.&lt;/li&gt;
&lt;li&gt;The site should &lt;strong&gt;not&lt;/strong&gt; show a flash of the wrong theme while loading.&lt;/li&gt;
&lt;li&gt;It should not be possible to mismatch the state between the theme and the toggle button.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Some Prerequisites
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://hankchizljaw.com/wrote/create-a-user-controlled-dark-or-light-mode/#heading-dealing-with-the-prefers-color-scheme-media-query"&gt;This post&lt;/a&gt; by Andy Bell (who for some reason goes by Hank Chizljaw sometimes) puts together a pretty clever way to handle user color preferences. To borrow some of the scaffolding from his post, there are a few constants we need to set.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// what we will store this in local storage as
const LS_KEY = 'user-color-scheme'

// an HTML data-attribute to keep track of user preference
const DOM_ATTR = `data-${LS_KEY}`

// the name of the CSS variable we'll be reading and overwriting
const CSS_PROP = LS_KEY

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  1. Reading a User's OS Preference
&lt;/h3&gt;

&lt;p&gt;In &lt;a href="https://dev.to/blog/choosing-theme-colors"&gt;part two&lt;/a&gt; of this series I briefly showed how to &lt;a href="https://dev.to/blog/choosing-theme-colors#converting-a-palette-to-dark-mode"&gt;put colors behind &lt;code&gt;prefers-color-scheme&lt;/code&gt;&lt;/a&gt;. That feels like the most obvious tool to reach for to accomplish goal #1. Using this media query will read a user’s configured preference from their browser, but there’s some nuance to consider knowing that we’ll be making a button to manually toggle this. There isn’t a way for any code from a browser to change a user’s Operating System level preferences, so we need to use a different method to toggle the theme.&lt;/p&gt;

&lt;p&gt;Because it isn't possible to call any JavaScript inside of the special Svelte &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; tag we have to get creative. We can call the &lt;code&gt;setTheme()&lt;/code&gt; function inside of a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals"&gt;template string&lt;/a&gt; combined with the Svelte &lt;a href="https://svelte.dev/docs#html"&gt;&lt;code&gt;@html&lt;/code&gt; interpolation&lt;/a&gt; to build out the contents of a regular &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; tag. This entire section needs to be wrapped in a &lt;a href="https://svelte.dev/docs#svelte_head"&gt;&lt;code&gt;&amp;lt;svelte:head&amp;gt;&lt;/code&gt; tag&lt;/a&gt; so that the framework will hoist it to the appropriate place in the document &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;svelte:head&amp;gt;
  {@html `
    &amp;lt;style&amp;gt;
      :root {
        --${CSS_PROP}: 'light';
        ${setTheme(themes.light)}
      }

      @media (prefers-color-scheme: dark) {
        :root {
          --${CSS_PROP}: 'dark';
          ${setTheme(themes.dark)}
        }
      }
    &amp;lt;/style&amp;gt;
  `}
&amp;lt;/svelte:head&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you're not using Svelte, you can do the same thing by calling &lt;code&gt;document.getElementsByTagName('head')&lt;/code&gt; and then using the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Node/appendChild"&gt;&lt;code&gt;appendChild()&lt;/code&gt; method&lt;/a&gt; to add a &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; tag.&lt;/p&gt;

&lt;p&gt;Since we also want to track this OS preference in JavaScript later, we’re going to borrow another idea from Andy’s post and set another variable to flag OS preference. In order to easily find and change this later, we want to use the &lt;code&gt;CSS_PROP&lt;/code&gt; variable from earlier.&lt;/p&gt;

&lt;p&gt;The site will render the light theme by default, if a user has no OS level preference, or if their system does not support &lt;code&gt;prefers-color-scheme&lt;/code&gt; at all. If the OS signals to the browser that the user prefers a dark color scheme we can use this CSS variable later to provide the right set of colors. By tracking the theme with a single value, this also makes it easier to set up the toggle button later.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Clicking the Toggle Button
&lt;/h3&gt;

&lt;p&gt;The click event logic for the toggle button starts pretty straightforward. I abstracted it into several smaller functions that will have more complexity added later.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const getOpposite = (currentMode) =&amp;gt; {
  return currentMode === 'dark'
    ? 'light'
    : 'dark'
}

const setPreference = (newPreference) =&amp;gt; {
  if (window) {
    document.documentElement.setAttribute(DOM_ATTR, newPreference)
    setCustomProperty(CSS_PROP, newPreference)
  }
}

const toggleColorScheme = () =&amp;gt; {
  let currentPreference = getCustomProperty(CSS_PROP)
  const newPreference = getOpposite(currentPreference)
  setPreference(newPreference)
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A user clicks the button and a chain of helper functions will fire. The first will read the &lt;code&gt;--user-color-scheme&lt;/code&gt; variable we set in step #1. This functions call a second function to get the opposite value, and then calls &lt;code&gt;setPreference()&lt;/code&gt;. &lt;code&gt;setPreference()&lt;/code&gt; overwrites the CSS variable and sets the &lt;code&gt;data-user-color-scheme&lt;/code&gt; attribute on the page’s &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt; element. To know what state we’re toggling &lt;em&gt;from&lt;/em&gt; we’ll look to the &lt;code&gt;CSS_PROP&lt;/code&gt; custom property we set in the &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; tag. If it is equal to &lt;code&gt;'dark'&lt;/code&gt; we know the user has an OS level preference and &lt;code&gt;getOpposite()&lt;/code&gt; will return &lt;code&gt;'light'&lt;/code&gt;. If it’s &lt;code&gt;'light'&lt;/code&gt; or &lt;code&gt;undefined&lt;/code&gt;, then &lt;code&gt;getOpposite()&lt;/code&gt; will return &lt;code&gt;'dark'&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We need to update our &lt;code&gt;:root&lt;/code&gt; CSS logic a little to allow the chosen theme to take precedence over any OS level theme.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;svelte:head&amp;gt;
  {@html `
    &amp;lt;style&amp;gt;
      :root {
        --${CSS_PROP}: 'light';
        ${setTheme(themes.light)}
      }

      @media (prefers-color-scheme: dark) {
        :root {
          --${CSS_PROP}: 'dark';
        }

        :root:not([data-user-color-scheme]) {
          ${setTheme(themes.dark)}
        }
      }

      [data-user-color-scheme='dark'] {
        ${setTheme(themes.dark)}
      }
    &amp;lt;/style&amp;gt;
  `}
&amp;lt;/svelte:head&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can use the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/:not"&gt;CSS &lt;code&gt;:not&lt;/code&gt; pseudo selector&lt;/a&gt; to check if the &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; attribute has been set by the toggle button. If it does not exist we can rely on the wrapping &lt;code&gt;prefers-color-scheme&lt;/code&gt; media query.&lt;/p&gt;

&lt;p&gt;This might seem a little confusing, but the logic works like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The site will always default to the light theme.&lt;/li&gt;
&lt;li&gt;If the OS is set to dark theme &lt;em&gt;and&lt;/em&gt; the user has not made a choice, the site will show the dark theme because of the &lt;code&gt;prefers-color-scheme&lt;/code&gt; setting.&lt;/li&gt;
&lt;li&gt;If the user has chosen dark theme with the toggle, the site will show that because of the &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; attribute.&lt;/li&gt;
&lt;li&gt;If the user has chosen light theme with the toggle, there is no explicit state for this so the site will fall back to the default light theme.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The last step in setting up the &lt;code&gt;toggleColorScheme()&lt;/code&gt; function is to deal with how Svelte optimizes code for SSR. Because &lt;code&gt;toggleColorScheme()&lt;/code&gt; and &lt;code&gt;getCustomProperty()&lt;/code&gt; both make calls to the &lt;code&gt;window&lt;/code&gt; object, this code breaks when it runs server-side. To work around this, we can use a &lt;a href="https://sapper.svelte.dev/docs#Third-party_libraries_that_depend_on_window"&gt;trick from the Svelte docs&lt;/a&gt; and reassign them after the &lt;code&gt;window&lt;/code&gt; object exists. If you aren't using Svlete this shouldn't be an issue, but if it is in your framework there's likely a similar workaround out there.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;script&amp;gt;
  let currentColorScheme
  let toggleColorScheme
  onMount(() =&amp;gt; {
    toggleColorScheme = () =&amp;gt; {
      const currentPreference = getCustomProperty(CSS_PROP)
      const newPreference = getOpposite(currentPreference)
      setPreference(newPreference)
    }
  })
&amp;lt;/script&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Intentionally Blocking JavaScript
&lt;/h3&gt;

&lt;p&gt;Putting blocking JavaScript in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; of a website is something that typically should be avoided. The page rendering process is &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Performance/How_browsers_work#parsing"&gt;complicated&lt;/a&gt;, but this is the most relevant concept to understand:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tags — particularly those without an async or defer attribute — block rendering, and pause the parsing of HTML.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You wouldn't want to load, say, all of &lt;a href="https://www.npmjs.com/package/jquery"&gt;jquery&lt;/a&gt; at the top of your page before you even need to use it. Since we don’t want this toggle to appear for users who don’t allow JavaScript and therefore can't interact with it, we need to run a script that intentionally blocks the rest of the page from loading. The &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; element needs to have a &lt;code&gt;data-no-js&lt;/code&gt; attribute added by default, and a quick script will run before loading the rest of the page to remove it.&lt;/p&gt;

&lt;p&gt;Just like with the CSS block earlier, the trick here is to again use the &lt;code&gt;@html&lt;/code&gt; interpolation. Rather than hook into the regular Svelte &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag, we want to render a string so that when the page is rendered at build time this code gets baked into the actual page source. This means it will execute as soon as the browser comes across it and not be optimized by Svelte to be non-blocking.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;svelte:head&amp;gt;
  {@html `
    &amp;lt;script&amp;gt;
      document.body.removeAttribute('data-no-js')
    &amp;lt;/script&amp;gt;
  `}
&amp;lt;/svelte:head&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since any user with JavaScript will immediately have this attribute removed, we can now target a CSS class of &lt;code&gt;.needs-js&lt;/code&gt; only when this attribute is present. This temporarily blocks rendering and happens before the rest of the page loads, so neither type of user should get a blip of the toggle when they should or shouldn’t see it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;body[data-no-js] .needs-js {
  display: none;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Keeping Data in LocalStorage
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage"&gt;&lt;code&gt;localStorage&lt;/code&gt;&lt;/a&gt; is a browser API that can be used to store a list of key value pairs per website. These values will persist even after a user leaves a website, making this the perfect place to store a user's color scheme preference after they set it.&lt;/p&gt;

&lt;p&gt;We can add a line to our &lt;code&gt;setPreference()&lt;/code&gt; function and use the &lt;code&gt;LS_KEY&lt;/code&gt; constant we added earlier.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const setPreference = (newPreference) =&amp;gt; {
  if (window) {
    document.documentElement.setAttribute(DOM_ATTR, newPreference)
    setCustomProperty(CSS_PROP, newPreference)
    window.localStorage.setItem(LS_KEY, newPreference)
  }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, when a user clicks the button, this script will modify the &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; tag attribute, update the CSS variable, &lt;em&gt;and&lt;/em&gt; save either &lt;code&gt;'light'&lt;/code&gt; or &lt;code&gt;'dark'&lt;/code&gt; in &lt;code&gt;localStorage&lt;/code&gt; so we can read it again when they come back to the site later.&lt;/p&gt;

&lt;p&gt;The actual logic for clicking the button needs a slight update as well. We want data persisted in &lt;code&gt;localStorage&lt;/code&gt; to override any OS preference. We can use &lt;code&gt;window.localStorage.getItem&lt;/code&gt; and our &lt;code&gt;LS_KEY&lt;/code&gt; to check and see if a user has any existing data. If they do, we need to pass it to &lt;code&gt;setPreference()&lt;/code&gt; instead of their &lt;code&gt;CSS_PROP&lt;/code&gt; value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;toggleColorScheme = () =&amp;gt; {
  const currentPreference = window.localStorage.getItem(LS_KEY) || getCustomProperty(CSS_PROP)
  const newPreference = getOpposite(currentPreference)
  setPreference(newPreference)
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;code&gt;existingUserPreference&lt;/code&gt; is &lt;code&gt;undefined&lt;/code&gt; the function will fall back to their OS preference and &lt;code&gt;setPreference()&lt;/code&gt; will save this new value in &lt;code&gt;localStorage&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Reading Data Without Flashes
&lt;/h3&gt;

&lt;p&gt;In order to check the stored value whenever a user visits the page, it might seem like the best way to do this is an &lt;a href="https://svelte.dev/docs#onMount"&gt;&lt;code&gt;onMount&lt;/code&gt; function&lt;/a&gt; as soon as the page loads.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;onMount(() =&amp;gt; {
  const existingPreference = window.localStorage.getItem(LS_KEY)
  setPreference(existingPreference)
})

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you've been coding along you likely see the problem here. If you haven't, here's an example of the issue we're facing.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
          Sorry, your browser doesn't support embedded videos.&lt;br&gt;
          a flash of the wrong theme, brief light theme on the right, brief dark theme on the left &lt;/p&gt;

&lt;p&gt;&lt;code&gt;onMount&lt;/code&gt; fires &lt;em&gt;after&lt;/em&gt; the component loads, which means that if the user's OS preference doesn't match the value they have saved in &lt;code&gt;localStorage&lt;/code&gt; they will see a flash of that theme before &lt;code&gt;setPreference()&lt;/code&gt; can fire and sort out the appropriate classes on the &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; tag. That's no good.&lt;/p&gt;

&lt;p&gt;We're actually going to use the same trick here that we did to remove &lt;code&gt;data-no-js&lt;/code&gt; before the page fully loaded.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;svelte:head&amp;gt;
  {@html `
    &amp;lt;script&amp;gt;
      document.body.removeAttribute('data-no-js')
      var existingUserPreference = window.localStorage.getItem('${LS_KEY}')
      var setPreference = ${setPreference.toString()}
      var getCustomProperty = ${getCustomProperty.toString()}
      var setCustomProperty = ${setCustomProperty.toString()}
      setPreference(existingUserPreference, getCustomProperty, setCustomProperty, '${LS_KEY}', '${DOM_ATTR}', '${CSS_PROP}')
    &amp;lt;/script&amp;gt;
  `}
&amp;lt;/svelte:head&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This might look slightly strange, but there are two things going on here.&lt;/p&gt;

&lt;p&gt;First, just like with the &lt;code&gt;removeAttribute('data-no-js')&lt;/code&gt; script, we need everything to be self-contained. This means we need a complete string that creates a new function and not just a reference to an existing assignment from the Svelte &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag. Luckily, the &lt;code&gt;function&lt;/code&gt; prototype contains a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/toString"&gt;&lt;code&gt;.toString()&lt;/code&gt; method&lt;/a&gt; that will stringify and return the entire function block. By combining this with Svelte’s &lt;code&gt;@html&lt;/code&gt; interpolation we can redefine the same functions in the inline script.&lt;/p&gt;

&lt;p&gt;The second caveat is that in order to stay properly encapsulated, &lt;code&gt;setPreference()&lt;/code&gt; will need to accept a few more arguments. We can stringify &lt;code&gt;setCustomProperty()&lt;/code&gt; the same way, but we'll need to pass &lt;em&gt;that instance&lt;/em&gt; of &lt;code&gt;setCustomProperty()&lt;/code&gt; into &lt;code&gt;setPreference()&lt;/code&gt; in order for it to work correctly. The same is true with the stringified and interpolated versions of all of our getters, setters, and constants.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const setPreference = (newPreference, setCustomProperty, LS_KEY, DOM_ATTR, CSS_PROP) =&amp;gt; {
  if (window) {
    document.documentElement.setAttribute(DOM_ATTR, newPreference)
    setCustomProperty(CSS_PROP, newPreference)
    window.localStorage.setItem(LS_KEY, newPreference)
  }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’ll also need to update the calls to &lt;code&gt;setPreference()&lt;/code&gt; inside of &lt;code&gt;toggleColorScheme()&lt;/code&gt;. It needs to accept and use the instances of those functions and constants from the Svelte instance rather than the inline &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;toggleColorScheme = () =&amp;gt; {
  const currentPreference = window.localStorage.getItem(LS_KEY) || currentColorScheme
  const newPreference = getOpposite(currentPreference)
  setPreference(newPreference, setCustomProperty, LS_KEY, DOM_ATTR, CSS_PROP)
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It might seem like putting this many blocking scripts on a page could start to get into performance-harming territory. Josh has a &lt;a href="https://www.joshwcomeau.com/react/dark-mode/#blocking-html"&gt;note on his post&lt;/a&gt; where he tested this. I also did my own test here using &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Console/time"&gt;&lt;code&gt;console.time()&lt;/code&gt;&lt;/a&gt; and found that the entire block of scripts runs in less than 1ms.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Keeping Everything in Sync
&lt;/h3&gt;

&lt;p&gt;The last concern is making sure that a user is never able to load a page or click the toggle in such a way that they see the wrong theme with the wrong button.&lt;/p&gt;

&lt;p&gt;The best way to keep things as in-sync as possible is to try to rely on a single source of truth and let everything read from there. For me, the &lt;code&gt;CSS_PROP&lt;/code&gt; on &lt;code&gt;:root&lt;/code&gt; is the single source of truth. It reads from &lt;code&gt;localStorage&lt;/code&gt; if it exists and then falls back to being set by the initial &lt;code&gt;media-query&lt;/code&gt;. JavaScript then adds it to the &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; tag and updates the value in &lt;code&gt;localStorage&lt;/code&gt; if it has changed. This very specific set of dominos is why I avoided using something like &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia"&gt;window.matchMedia&lt;/a&gt; to read the user's &lt;code&gt;prefers-color-scheme&lt;/code&gt; value directly.&lt;/p&gt;

&lt;p&gt;In order to fire off this chain of events correctly, we need to make one more change to the &lt;code&gt;setPreference()&lt;/code&gt; function to correctly update the &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; tag during the blocking period. When &lt;code&gt;setPreference()&lt;/code&gt; is called from the inline script, it may or may not find an existing preference in &lt;code&gt;localStorage&lt;/code&gt;. We need to add an &lt;code&gt;else&lt;/code&gt; condition to read the &lt;code&gt;CSS_PROP&lt;/code&gt; value and update the rest of the page accordingly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const setPreference = (newPreference, getCustomProperty, setCustomProperty, LS_KEY, DOM_ATTR, CSS_PROP) =&amp;gt; {
  if (window) {
    if (newPreference) {
      document.documentElement.setAttribute(DOM_ATTR, newPreference)
      setCustomProperty(CSS_PROP, newPreference)
      window.localStorage.setItem(LS_KEY, newPreference)
    } else {
      const OS = getCustomProperty(CSS_PROP)
      document.documentElement.setAttribute(DOM_ATTR, OS)
      setCustomProperty(CSS_PROP, OS)
    }
  }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Making sure the button never shows the wrong state also means looking to a single source of truth. We can actually style the toggle button based directly off of the inline style that the &lt;code&gt;setCustomProperty&lt;/code&gt; helper class applies. For Svelte we'll have to use the &lt;a href="https://svelte.dev/docs#style"&gt;&lt;code&gt;:global()&lt;/code&gt; style modifier&lt;/a&gt; to escape the style encapsulation if everything isn't in the same file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:global([style*='light'] .color-scheme-toggle-button) {
  ...
}

:global([style*='dark'] .color-scheme-toggle-button) {
  ...
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The selector &lt;code&gt;[style*='...']&lt;/code&gt; is using a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors"&gt;matcher&lt;/a&gt; to target any element with an inline &lt;code&gt;style&lt;/code&gt; attribute that contains either version of the set &lt;code&gt;--user-color-scheme&lt;/code&gt; value. Since we've gone through steps to make sure all other logic checks this same source, styling based directly on this is much safer than using another JavaScript function to try to toggle CSS classes on the button element.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Code
&lt;/h2&gt;

&lt;p&gt;The final code for the entire component should look something like &lt;a href="https://github.com/ryanfiller/snippets/blob/main/color-scheme-toggle-demo.svelte"&gt;this&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The actual code as it exists on my site is a bit more abstracted, but you can find the &lt;code&gt;data-no-js&lt;/code&gt; functionality in my &lt;a href="https://github.com/ryanfiller/portfolio-svelte/blob/8fd00f52b10f7a1a1f17dfffcc8cd962c0ca04ae/src/template.html"&gt;&lt;code&gt;template.html&lt;/code&gt; file&lt;/a&gt;, the setting of my CSS variables from JSON in my &lt;a href="https://github.com/ryanfiller/portfolio-svelte/blob/8fd00f52b10f7a1a1f17dfffcc8cd962c0ca04ae/src/components/layout/styles.svelte"&gt;&lt;code&gt;styles.svelte&lt;/code&gt; file&lt;/a&gt;, the rest of the theme and button logic in the &lt;a href="https://github.com/ryanfiller/portfolio-svelte/blob/8fd00f52b10f7a1a1f17dfffcc8cd962c0ca04ae/src/components/layout/color-scheme-toggle.svelte"&gt;component file&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As always, reach out on &lt;a href="https://twitter.com/ryanfiller_"&gt;Twitter&lt;/a&gt; or &lt;a href="https://github.com/ryanfiller/"&gt;GitHub&lt;/a&gt; with any questions, comments, or concerns. Happy theming!&lt;/p&gt;

</description>
      <category>code</category>
      <category>colors</category>
      <category>css</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Building a Better Svelte Data Flow</title>
      <dc:creator>ryanfiller</dc:creator>
      <pubDate>Sun, 28 Mar 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/ryanfiller/building-a-better-svelte-data-flow-5n3</link>
      <guid>https://dev.to/ryanfiller/building-a-better-svelte-data-flow-5n3</guid>
      <description>&lt;p&gt;This blog has been running as a &lt;a href="https://svelte.dev/" rel="noopener noreferrer"&gt;Svelte&lt;/a&gt;-powered &lt;a href="https://sapper.svelte.dev/" rel="noopener noreferrer"&gt;Sapper&lt;/a&gt; app since October 2020. Before changing frameworks from &lt;a href="https://www.gatsbyjs.com/" rel="noopener noreferrer"&gt;Gatsby&lt;/a&gt; I put together a &lt;a href="https://sapper-goals.netlify.app/" rel="noopener noreferrer"&gt;test site&lt;/a&gt; and wrote a &lt;a href="https://www.ryanfiller.com/blog/a-deep-dive-into-sapper" rel="noopener noreferrer"&gt;blog post&lt;/a&gt; about what I learned about configuring Sapper. I found a set up that worked for me, so I &lt;a href="https://github.com/ryanfiller/portfolio-svelte/commit/12a1de926c681ca182be8fca1030b42c4c9ff3cf" rel="noopener noreferrer"&gt;switched my site over&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The data flow I explored on my test site worked perfectly enough until I ran into a snag — I wanted to compose a post with information from two different sources without having to load data client side and rerender. I built a solution that I think combines the best of Sapper’s native data flow with mdsvex’s powerful preprocessing.&lt;/p&gt;

&lt;p&gt;This post is a somewhat deep dive, so if it's confusing checkout the &lt;a href="https://www.ryanfiller.com/blog/a-deep-dive-into-sapper" rel="noopener noreferrer"&gt;original Sapper/mdsvex post&lt;/a&gt; and come back after that.&lt;/p&gt;

&lt;p&gt;If you want to skip the technical explanations of Sapper and mdsvex and get right to how I solved the problem, start here.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does Sapper work?
&lt;/h2&gt;

&lt;p&gt;Sapper uses file based routing where files and folders are used to build out the page structure of the final site. The &lt;a href="https://sapper.svelte.dev/docs#Routing" rel="noopener noreferrer"&gt;Sapper documentation&lt;/a&gt; has a section about how this works, but if you're new to the framework I think it's more informative to look at the &lt;code&gt;/routes&lt;/code&gt; directory in the &lt;a href="https://github.com/sveltejs/sapper-template/tree/master/src/routes" rel="noopener noreferrer"&gt;&lt;code&gt;sapper-template&lt;/code&gt; starter repo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Each file in &lt;code&gt;/routes&lt;/code&gt; corresponds to a url endpoint on the site. So &lt;code&gt;/routes/about&lt;/code&gt; will produce &lt;code&gt;/about.html&lt;/code&gt;, &lt;code&gt;/routes/about/me&lt;/code&gt; produces &lt;code&gt;/about/me.html&lt;/code&gt; and so on with no limit for how deep nesting goes. This post is mostly about routes that use dynamic parameters, examples of which can be found in the &lt;a href="https://github.com/sveltejs/sapper-template/tree/master/src/routes/blog" rel="noopener noreferrer"&gt;&lt;code&gt;sapper-template&lt;/code&gt;'s blog directory&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;sapper-template&lt;/code&gt; skips over this step for brevity, but normally a site will contain a collection of markdown files containing the content for each post. In order for Sapper to use this content, two dynamic routes need to exist — &lt;code&gt;index.json.js&lt;/code&gt; and &lt;code&gt;[slug].json.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;index.json.js&lt;/code&gt; will use the &lt;a href="https://nodejs.org/api/fs.html" rel="noopener noreferrer"&gt;&lt;code&gt;node&lt;/code&gt; file system API&lt;/a&gt; to look through the markdown files and build a list. &lt;code&gt;[slug].json.js&lt;/code&gt; needs to use something like &lt;a href="https://github.com/unifiedjs/unified" rel="noopener noreferrer"&gt;&lt;code&gt;unified&lt;/code&gt;&lt;/a&gt; to turn the markdown into valid HTML.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4j4s4lko5evu96hulvma.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4j4s4lko5evu96hulvma.png" alt="chart that shows blog/[slug].md - fs &amp;amp; unified() &amp;lt; blog/index.json, blog/[slug].json"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;.svelte&lt;/code&gt; files inside the &lt;code&gt;/routes&lt;/code&gt; directory get access to a special &lt;a href="https://sapper.svelte.dev/docs#Preloading" rel="noopener noreferrer"&gt;&lt;code&gt;preload()&lt;/code&gt; function&lt;/a&gt; that can be called from the &lt;a href="https://svelte.dev/docs#script_context_module" rel="noopener noreferrer"&gt;&lt;code&gt;&amp;lt;script context='module'&amp;gt;&lt;/code&gt;&lt;/a&gt; tag. Inside this function pages can access any &lt;code&gt;params&lt;/code&gt; captured via the brackets in their file name. &lt;code&gt;[slug].svelte&lt;/code&gt; can use &lt;code&gt;params.slug&lt;/code&gt; and &lt;a href="https://sapper.svelte.dev/docs#this_fetch" rel="noopener noreferrer"&gt;&lt;code&gt;fetch()&lt;/code&gt;&lt;/a&gt; to make a call against the corresponding &lt;code&gt;/[slug].json&lt;/code&gt; route and get data.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkmrt1597zk6retf87mjz.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkmrt1597zk6retf87mjz.png" alt="chart that shows browser visit - request blog/post - blog/post.svelte - fetch( raw `post.json` endraw ) - blog/post.json - preload(){ return data } - blog/post.html"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If the app is being run in &lt;a href="https://sapper.svelte.dev/docs#sapper_build" rel="noopener noreferrer"&gt;&lt;code&gt;sapper build&lt;/code&gt; mode&lt;/a&gt;, &lt;code&gt;[slug].svelte&lt;/code&gt; will fetch data and generate a page whenever a user visits a url that corresponds to an existing markdown file. Building a site with &lt;a href="https://sapper.svelte.dev/docs#Exporting" rel="noopener noreferrer"&gt;&lt;code&gt;sapper export&lt;/code&gt;&lt;/a&gt; will crawl any links on a site and pre-build and statically export any pages it finds.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6qvx3xllex46go6gpri3.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6qvx3xllex46go6gpri3.png" alt="chart that shows blog/[slug].md - npm run sapper export - blog/[slug].hmtl"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One final important Sapper concept is &lt;a href="https://sapper.svelte.dev/docs#Layouts" rel="noopener noreferrer"&gt;layouts&lt;/a&gt;. A file named &lt;code&gt;_layout.svelte&lt;/code&gt; will automatically be rendered for any page within that route, with the component passed into the &lt;code&gt;_layout&lt;/code&gt;'s default &lt;a href="https://svelte.dev/docs#slot" rel="noopener noreferrer"&gt;&lt;code&gt;&amp;lt;slot /&amp;gt;&lt;/code&gt;&lt;/a&gt;. This is helpful for any elements that will be repeated on every final page, like a header and footer or navigation. One very important consideration, however, is that because of how data flows from parent to child, a &lt;code&gt;_layout&lt;/code&gt; does &lt;em&gt;not&lt;/em&gt; have access to the &lt;code&gt;$$props&lt;/code&gt; object that its child receives.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2okfeafrs8g9j07kq9xb.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2okfeafrs8g9j07kq9xb.png" alt="char that shows log/_layout.svelte - &amp;lt;slot /&amp;gt; - blog/[slug].svelte - svelte.compile() - blog/[slug].html"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How does mdsvex work?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://mdsvex.com/" rel="noopener noreferrer"&gt;mdsvex&lt;/a&gt; is a tool that lets you embed functional Svelte components right into Markdown files. It also hooks into Sapper's call to &lt;a href="https://svelte.dev/docs#svelte_preprocess" rel="noopener noreferrer"&gt;&lt;code&gt;svelte.preprocess&lt;/code&gt;&lt;/a&gt; to take away some of the manual work of creating pages. mdsvex is a preprocessor for Sapper, so all of the same rules for the Sapper &lt;code&gt;/routes&lt;/code&gt; directory still apply.&lt;/p&gt;

&lt;p&gt;Instead of having to deal with dynamically named &lt;code&gt;[slug].svelte&lt;/code&gt; files, mdsvex will take any files with a &lt;a href="https://mdsvex.com/docs#extensions" rel="noopener noreferrer"&gt;given extension&lt;/a&gt; and create Svelte components. Because these files live within the &lt;code&gt;/routes&lt;/code&gt; directory Sapper will see them as pages and create routes for them.&lt;/p&gt;

&lt;p&gt;!chart that shows blog/[slug].md - svelte.preprocess([ mdsvex() ]) - blog/[slug].svelte - npm run sapper export - blog/[slug].html](&lt;a href="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u11gr370ryjegmejf6ax.png" rel="noopener noreferrer"&gt;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u11gr370ryjegmejf6ax.png&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Under the hood, &lt;code&gt;mdsvex&lt;/code&gt; fetches data similarly to Sapper, but &lt;a href="https://mdsvex.com/docs#frontmatter-1" rel="noopener noreferrer"&gt;uses the markdown's frontmatter&lt;/a&gt; to create a data object and pass it into the component as &lt;a href="https://svelte.dev/docs#Attributes_and_props" rel="noopener noreferrer"&gt;props&lt;/a&gt;. This takes the place of manually running &lt;code&gt;fetch&lt;/code&gt; inside of the &lt;code&gt;preload&lt;/code&gt; function. mdsvex will parse markdown into HTML, and can be given a Svelte &lt;code&gt;layout&lt;/code&gt; component that will function similar to a Sapper &lt;code&gt;_layout&lt;/code&gt;. Unlike a &lt;code&gt;_layout&lt;/code&gt;, an mdsvex &lt;code&gt;layout&lt;/code&gt; &lt;em&gt;will&lt;/em&gt; be passed the &lt;code&gt;$$props&lt;/code&gt; object.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvmnfvwhx7wifpvvf4qwj.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvmnfvwhx7wifpvvf4qwj.png" alt="chart that shows blog/post.md - mdsvex.compile() - {frontmatter} - $$props - template.svelte - &amp;lt;slot /&amp;gt; - blog/post.svelte"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once these Svelte pages are created, the same flow for Sapper applies. In order for mdsvex to work with Sapper, all scripts need to be run with the &lt;a href="https://mdsvex.com/docs#with-sapper" rel="noopener noreferrer"&gt;&lt;code&gt;--ext&lt;/code&gt; flag&lt;/a&gt;. This flag will allow Sapper to treat non &lt;code&gt;.svelte&lt;/code&gt; files as routes and pick them up during the &lt;code&gt;sapper export&lt;/code&gt; process. This is an important distinction — instead of relying on Sapper to crawl and generate each page, mdsvex will always create a Svelte file and corresponding route. Another important caveat here is that mdsvex-generated components will not run a &lt;code&gt;preload&lt;/code&gt; function. These genreated components are regular Svelte components and don't work the same as Sapper page routes.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F045j1yq2fi88oyynnumy.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F045j1yq2fi88oyynnumy.png" alt="chart that shows blog/[slug].md - npm run sapper export - blog/[slug].svelte - svelte.compile() - blog/[slug].html"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Shortcomings
&lt;/h2&gt;

&lt;p&gt;Locking in to a workflow usually comes with a series of tradeoffs. By far the largest benefit of using mdsvex is the ability to put components directly into markdown files. I was able to write over a dozen posts this way before running into any complications. mdsvex is great at taking data from one &lt;code&gt;.md&lt;/code&gt; file and transforming it into one &lt;code&gt;.svelte&lt;/code&gt; file, but what if data needs to come from &lt;em&gt;two&lt;/em&gt; sources?&lt;/p&gt;

&lt;p&gt;My real world example of this popped up when I was adding the post-as-a-series feature to my blog. Each series would have a &lt;code&gt;.json&lt;/code&gt; file containing a &lt;code&gt;name&lt;/code&gt; and short description. Each post in that series contains a frontmatter &lt;code&gt;series&lt;/code&gt; field. The posts' &lt;code&gt;series&lt;/code&gt; field would need to correspond to a series &lt;code&gt;name&lt;/code&gt; in order to link to extra data. This would function similar to a &lt;a href="https://www.w3schools.com/sql/sql_foreignkey.asp" rel="noopener noreferrer"&gt;&lt;code&gt;primary key&lt;/code&gt; and &lt;code&gt;foreign key&lt;/code&gt; database relationship&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;One way to link between this data would be to do the work in the &lt;code&gt;node&lt;/code&gt; code that runs to generated each &lt;code&gt;post.json&lt;/code&gt; file.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp5hm3vne7lvn0y14252t.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp5hm3vne7lvn0y14252t.png" alt="chart that shows blog/post.md, blog/series.json &amp;gt; fs &amp;amp; unified() - blog/post.json - npm run sapper export - blog/post.svelte"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A different way to do this would be to run &lt;code&gt;fetch()&lt;/code&gt; against multiple endpoints and return one final &lt;code&gt;data&lt;/code&gt; object to the component.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3l3codm0sayvz9uuyja7.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3l3codm0sayvz9uuyja7.png" alt="chart that shows blog/post.svelte &amp;lt; fetch( raw `post.json` endraw ) &amp;amp; fetch( raw `series.json` endraw ) = blog/post.json, blog/series.json &amp;gt; preload(){ return data } - blog/post.svelt"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Because I'm set on using components in my markdown, I have to use mdsvex. It works a little differently as it's data sources are one-in, one-out. Getting extra data from the &lt;code&gt;blog/series.json&lt;/code&gt; route would need to be done inside of an &lt;a href="https://svelte.dev/docs#onMount" rel="noopener noreferrer"&gt;&lt;code&gt;onMount() callback&lt;/code&gt;&lt;/a&gt;. This would happen moments &lt;em&gt;after&lt;/em&gt; a user loads the page and wouldn't happen during server side prerendering at all. This would trigger a flash of content loading, which I really want to avoid. "Cumulative Layout Shift," or "CLS" is one of Google's new &lt;a href="https://web.dev/cls/" rel="noopener noreferrer"&gt;Web Vitals&lt;/a&gt;, so this would hurt my &lt;a href="https://developers.google.com/web/tools/lighthouse" rel="noopener noreferrer"&gt;Lighthouse&lt;/a&gt; scores and SEO page rankings.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb7p1821n2eoza5lvs4ci.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb7p1821n2eoza5lvs4ci.png" alt="chart that shows blog/post.svelte - render - blog/post.html - onMount(){ () =&amp;gt; fetch() } - blog/series.json - render again - blog/post.html"&gt;&lt;/a&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%2Fwww.ryanfiller.com%2Fimages%2Ffetch-loading-on-mount-slow.mp4" 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%2Fwww.ryanfiller.com%2Fimages%2Ffetch-loading-on-mount-slow.mp4" alt="data being loading in after render via  raw `onMount` endraw "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Fetching data in other places
&lt;/h3&gt;

&lt;p&gt;The other option is to run &lt;code&gt;fetch()&lt;/code&gt; from a Sapper &lt;code&gt;_layout&lt;/code&gt; file that wraps each page, but this presents an interesting problem. Because of the way that Svelte &lt;em&gt;"surgically updates the DOM,"&lt;/em&gt; the &lt;code&gt;_layout.svelte&lt;/code&gt; file is actually mounted and rendered &lt;em&gt;once&lt;/em&gt; and its &lt;code&gt;&amp;lt;slot /&amp;gt;&lt;/code&gt; content is dynamically changed out when Sapper changes routes.&lt;/p&gt;

&lt;p&gt;You can force a refetch of a data here using a &lt;a href="https://svelte.dev/docs#3_%24_marks_a_statement_as_reactive" rel="noopener noreferrer"&gt;reactive statement&lt;/a&gt; and pass it down, but because of the way Svelte's lifecycle works this will result in the same problem where data is loaded after initial render.&lt;/p&gt;

&lt;h2&gt;
  
  
  Combining both page composition methods
&lt;/h2&gt;

&lt;p&gt;Let's review what we know at this point —&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sapper can use a dynamic component to take slug from a url, turn it into a file path, then fetch and preload data.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;fs&lt;/code&gt; can make a list of files, then &lt;code&gt;sapper export&lt;/code&gt; can crawl it to produce HTML pages.&lt;/li&gt;
&lt;li&gt;mdsvex will generate a component from any file in a directory, even if the routes are not crawled by Sapper.&lt;/li&gt;
&lt;li&gt;In order to get data at the right time, it needs to be fetched from the &lt;code&gt;preload&lt;/code&gt; method of a page.&lt;/li&gt;
&lt;li&gt;Anything done in a &lt;code&gt;preload()&lt;/code&gt; from a route can be server rendered by &lt;code&gt;sapper export&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So what would an ideal data flow look like?&lt;/p&gt;

&lt;p&gt;A browser (or the Sapper crawl process) would visit a url and load a dynamic &lt;code&gt;[slug].svelte&lt;/code&gt; route. This route would then be able to call a &lt;code&gt;preload&lt;/code&gt; function and &lt;code&gt;fetch&lt;/code&gt; any &lt;code&gt;json&lt;/code&gt; data, as well as find the &lt;code&gt;.md&lt;/code&gt; content that's been transformed by &lt;code&gt;mdsvex&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F23fi2f8vs3w1d5aefrze.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F23fi2f8vs3w1d5aefrze.png" alt="chart that shows browser visit - request blog/[slug] - blog/[slug].svelte - preload() &amp;lt; log/[slug].json, blog/[slug].md &amp;gt; server side render - blog/[slug].html"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Getting data
&lt;/h3&gt;

&lt;p&gt;This is pretty much the default Sapper workflow, but we need a way to loop in components generated by &lt;code&gt;mdsvex&lt;/code&gt; &lt;em&gt;without&lt;/em&gt; letting it take over as the de facto route generator. There are three important features that can work together to do this —&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sapper files and directories with a leading underscore &lt;a href="https://sapper.svelte.dev/docs#File_naming_rules" rel="noopener noreferrer"&gt;do not create routes&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;mdsvex can &lt;a href="https://mdsvex.com/docs#frontmatter-1" rel="noopener noreferrer"&gt;import &lt;code&gt;{ metadata }&lt;/code&gt; from any file's frontmatter&lt;/a&gt;, but it &lt;em&gt;also&lt;/em&gt; exports the generated &lt;code&gt;default&lt;/code&gt; transformed page body.&lt;/li&gt;
&lt;li&gt;mdsvex will generate these components even if a route is &lt;em&gt;not&lt;/em&gt; created.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If a &lt;code&gt;.md&lt;/code&gt; file lives directly inside &lt;code&gt;routes/blog&lt;/code&gt;, &lt;code&gt;mdsvex&lt;/code&gt; is going to turn it into a page and Sapper will load it for a given url instead of the generic &lt;code&gt;[slug].svelte&lt;/code&gt;. The first thing to do is avoid this by moving all of the posts into a &lt;code&gt;_content&lt;/code&gt; directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;└─ blog/
  ├─ _content/
  │ ├─ post1/
  │ │ └─ index.md
  │ └─ post2/
  │ └─ index.md
  ├─ [slug].svelte
  └─ _series.json

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The next step is to manually find these files from &lt;code&gt;[slug].svelte&lt;/code&gt;'s &lt;code&gt;preload&lt;/code&gt; function. For this we need to add the &lt;a href="https://www.npmjs.com/package/@rollup/plugin-dynamic-import-vars" rel="noopener noreferrer"&gt;&lt;code&gt;@rollup/plugin-dynamic-import-vars&lt;/code&gt;&lt;/a&gt; package so we can use the &lt;code&gt;slug&lt;/code&gt; to find and load the component that &lt;code&gt;mdsvex&lt;/code&gt; generates. &lt;code&gt;import&lt;/code&gt; statements won't usually work with dynamic &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals" rel="noopener noreferrer"&gt;template strings&lt;/a&gt;, but &lt;code&gt;@rollup/plugin-dynamic-import-vars&lt;/code&gt; will let us import from a file path that includes a &lt;code&gt;slug&lt;/code&gt; variable.&lt;/p&gt;

&lt;p&gt;With the plugin installed, &lt;code&gt;[slug].svelte&lt;/code&gt; can load this file, get the &lt;code&gt;default&lt;/code&gt; export containing the &lt;code&gt;.md&lt;/code&gt; content and the &lt;code&gt;metadata&lt;/code&gt; export containing the frontmatter, &lt;em&gt;and&lt;/em&gt; still make a &lt;code&gt;fetch()&lt;/code&gt; call to &lt;code&gt;/series.json&lt;/code&gt; and sync up the data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;script context='module'&amp;gt;
  export async function preload(page) {

    const { slug } = page.params

    const component = await import(`./_content/${slug}/index.md`)

    const series = await this.fetch(`/blog/series.json`)
      .then(response =&amp;gt; response.json())
      .then(series =&amp;gt; logicToLinkUpPostToSeries(...))
    )

    return {
      page: component.default,
      metadata: component.metadata,
      series: series
    }
  }
&amp;lt;/script&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since all of this happens within an &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function" rel="noopener noreferrer"&gt;&lt;code&gt;asynchronous&lt;/code&gt;&lt;/a&gt; &lt;code&gt;preload&lt;/code&gt; function the page will wait to render until the data is resolved and we can avoid components loading in after initial render.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rendering the page
&lt;/h3&gt;

&lt;p&gt;There are two things to be aware of for rendering this data.&lt;/p&gt;

&lt;p&gt;First, is &lt;code&gt;component.default&lt;/code&gt;. If you &lt;code&gt;console.log()&lt;/code&gt; the import it will show different two ydrthings depending on where it is logged.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// console.log(component.default) from &amp;lt;script context='module'&amp;gt; tag
{ render: [Function: render], '$$render': [Function: $$render] }

// console.log(page) from regular &amp;lt;script&amp;gt; tag
class Post { constructor(options) }

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What's going on here is best explained in the &lt;a href="https://sapper.svelte.dev/docs#Routing" rel="noopener noreferrer"&gt;Routing section of the Sapper docs&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When a user first visits the application, they will be served a server-rendered version of the route in question, plus some JavaScript that 'hydrates' the page and initialises a client-side router. From that point forward, navigating to other pages is handled entirely on the client for a fast, app-like feel.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In short, this means that the Sapper server will render the component using the &lt;code&gt;render()&lt;/code&gt; function to return stringified HTML. Then the Svelte client will hydrate that HTML by instantiating the &lt;code&gt;class&lt;/code&gt; at run time in the browser. Instead of sorting this out manually, the entire component can be passed into a special &lt;a href="https://svelte.dev/docs#svelte_component" rel="noopener noreferrer"&gt;&lt;code&gt;&amp;lt;svelte:component /&amp;gt;&lt;/code&gt; tag&lt;/a&gt; that will automatically take care of running each at the appropriate time.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F64n9cgk9olxnuvck3izo.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F64n9cgk9olxnuvck3izo.png" alt="chart that shows blog/page.svelte - svelte.compile() &amp;lt;     function render(), class Page &amp;gt; &amp;lt;svelte:component /&amp;gt; - client hydration"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The second concern is getting data to the correct component. Since some of the work is done behind the scenes by compilers, we can't always imperatively pass &lt;code&gt;props&lt;/code&gt; down from one component to its direct children. Svelte provides a way around this with the &lt;a href="https://svelte.dev/docs#setContext" rel="noopener noreferrer"&gt;&lt;code&gt;context&lt;/code&gt; API&lt;/a&gt;. Rather than having to send a prop directly down the chain from parent to child, &lt;code&gt;context&lt;/code&gt; provides a way to skip links in the chain and get data from a parent to any deeply nested child.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;App&amp;gt;
  &amp;lt;_layout&amp;gt;
    &amp;lt;mdsvexLayout&amp;gt;
      &amp;lt;[slug].svelte&amp;gt;
        &amp;lt;svelte:component this={Page} /&amp;gt;
      &amp;lt;/[slug].svelte&amp;gt;
    &amp;lt;/mdsvexLayout&amp;gt;
  &amp;lt;/_layout&amp;gt;
&amp;lt;/App&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After getting the &lt;code&gt;series.json&lt;/code&gt; data inside of &lt;code&gt;[slug].svelte&lt;/code&gt; we can call &lt;code&gt;setContext&lt;/code&gt; to store data inside of a context object. Since the &lt;code&gt;&amp;lt;Page /&amp;gt;&lt;/code&gt; component generated by &lt;code&gt;mdsvex&lt;/code&gt; will be a child component we can call &lt;code&gt;getContext&lt;/code&gt; and check if the current page has a value for &lt;code&gt;series&lt;/code&gt; and the UI can act accordingly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; // [slug].svelte
&amp;lt;script context='module'&amp;gt;
  export async function preload(page) {
    ...
  }
&amp;lt;/script&amp;gt;

&amp;lt;script&amp;gt;
  export let page
  export let series
  import { setContext } from 'svelte'
  setContext('series', series)
&amp;lt;/script&amp;gt;

&amp;lt;svelte:component this={page} /&amp;gt;

// page.svelte
&amp;lt;script&amp;gt;
  import { getContext } from 'svelte'
  const series = getContext('series')
&amp;lt;script/&amp;gt;

{#if series}  
  ...
{/if}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The final data flow works like this.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa6y9jf8gdgxtyw8ruih6.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa6y9jf8gdgxtyw8ruih6.png" alt="chart that shows blog/[slug].md, blog/[series].json &amp;gt; npm run export - template.svelte - &amp;lt;slot /&amp;gt; - blog/[slug].svelte - preload() &amp;lt; blog/[slug].json, blog/[slug].md &amp;gt; server side render - blog/[slug].html"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  My &lt;code&gt;sapper-mdsvex-starter&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;If this sounds cool or useful to you (and I hope it does!), I've put together a &lt;a href="https://github.com/ryanfiller/sapper-mdsvex-starter" rel="noopener noreferrer"&gt;starter template repo&lt;/a&gt; that can be used to get a blog with this data flow up and running.&lt;/p&gt;

&lt;p&gt;This data flow is &lt;em&gt;weird&lt;/em&gt;. I couldn't find anything else online using a similar set up, or even a different tool chain to solve the same problem. This was able to solve my use case, but if there's a better way or just any feedback, feel free to let me know in the &lt;a href="https://github.com/ryanfiller/sapper-mdsvex-starter/issues" rel="noopener noreferrer"&gt;GitHub issues&lt;/a&gt;, on &lt;a href="https://twitter.com/ryanfiller_" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;, or on the &lt;a href="https://discord.com/channels/457912077277855764/" rel="noopener noreferrer"&gt;Svelte discord&lt;/a&gt; where I'm &lt;code&gt;@ryfill&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Special thanks
&lt;/h2&gt;

&lt;p&gt;Huge thank you to everyone in the Svelte discord for helping me rubber duck through this code, especially &lt;a href="https://twitter.com/babichjacob" rel="noopener noreferrer"&gt;Jacob Babich&lt;/a&gt;. Also thank you to &lt;a href="https://twitter.com/kevmodrome" rel="noopener noreferrer"&gt;kev&lt;/a&gt; and &lt;a href="https://twitter.com/evilpingwin" rel="noopener noreferrer"&gt;pngwn&lt;/a&gt; for fact checking this post.&lt;/p&gt;

</description>
      <category>code</category>
      <category>svelte</category>
      <category>sapper</category>
      <category>jamstack</category>
    </item>
    <item>
      <title>Choosing Theme Colors</title>
      <dc:creator>ryanfiller</dc:creator>
      <pubDate>Thu, 25 Feb 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/ryanfiller/choosing-theme-colors-dark-mode-series-495k</link>
      <guid>https://dev.to/ryanfiller/choosing-theme-colors-dark-mode-series-495k</guid>
      <description>&lt;p&gt;Choosing a color palette for a website is hard. Its even more complicated when trying to choose colors that will work with both a dark-on-light and &lt;a href="https://en.wikipedia.org/wiki/Light-on-dark_color_scheme"&gt;light-on-dark&lt;/a&gt; theme. I recently re-themed my site and here are some things I learned along the way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing a color palette
&lt;/h2&gt;

&lt;p&gt;Picking a color palette is something I have always struggled with, even as far back as college. There are plenty of tools online that use algorithms to help you choose colors, &lt;a href="https://coolors.co/"&gt;Coolors&lt;/a&gt; and &lt;a href="https://color.adobe.com/create/color-wheel"&gt;Adobe Kuler&lt;/a&gt; are two favorites of my favorites.&lt;/p&gt;

&lt;p&gt;A trick I learned in design school is to find an image with a vibe you like and sample colors from it as a starting point. &lt;a href="https://en.wikipedia.org/wiki/Color_theory"&gt;Color theory&lt;/a&gt; is complicated, and I think it's okay to lean on experts who has already put in the work.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Good artists borrow, great artists steal.&lt;/p&gt;



&lt;p&gt;&lt;del&gt;Pablo Picasso&lt;/del&gt; Ryan Filler&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;(&lt;em&gt;Really I just wanted to make this joke, but the &lt;a href="https://www.uvu.edu/arts/applause/posts/stealing.html"&gt;actual history of this quote&lt;/a&gt; was too ironic not to also share.&lt;/em&gt;)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tMA_1ZQN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ryanfiller.com/images/moebius-starwatcher-2.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tMA_1ZQN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ryanfiller.com/images/moebius-starwatcher-2.jpg" alt="Starwatcher II, Moebius"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Copyright law seems kind of fuzzy on if it's legal to lift a color palette from an established brand. For instance, I wouldn't recommend just copying, say, Google's red, yellow, green, and blue. Photographs, advertisements, fine art, comics, even film and video game stills, though, are fair game. &lt;a href="https://www.instagram.com/colorpalette.cinema/"&gt;colorpalette.cinema&lt;/a&gt; is a great Instagram account that posts color pallets made from scenes of popular movies.&lt;/p&gt;

&lt;p&gt;The color palette for my 2020 re-theme was built by selecting colors from &lt;em&gt;Starwatcher II&lt;/em&gt;, an illustration by &lt;a href="https://en.wikipedia.org/wiki/Jean_Giraud"&gt;Jean "Moebius" Giraud&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;[[clearfix]]&lt;/p&gt;

&lt;p&gt;Another pro-tip I learned in design college was to never use straight #000000 black or #ffffff white in a design. A design, even in print, will look much more intentional with a slightly desaturated black and a slightly temperature-shifted white instead of using the default colors. I try to keep this in mind when sampling colors from images and try to instead grab any dark color to stand in for "black" on my site. In fact, the color I chose for "black," #080025, is actually a very deep purple and "white," #fefdf2, is a very pale yellow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keeping accessibility in mind
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YQBlGFnD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ryanfiller.com/images/color-scheme-chart-all.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YQBlGFnD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ryanfiller.com/images/color-scheme-chart-all.png" alt="all colors chosen for my website"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One of the hardest parts about choosing colors for the web is finding the right balance between what looks good and what is high contrast enough for all users. I've built a &lt;a href="https://colors.ryanfiller.com/"&gt;tool to help with this&lt;/a&gt; (&lt;a href="https://www.ryanfiller.com/blog/svelte-sanity-and-serverless-functions"&gt;learn how I built it in this post&lt;/a&gt;), but finding a combination that works can still be hard work.&lt;/p&gt;

&lt;p&gt;Something that worked out well for me is to focus on making sure the first two rows and first two columns of this chart are all passing scores. In most scenarios, text will either appear as black or white on a colored background, or as a color on a black or white background. If there are other specific color combinations you know you want to use, make sure they also pass the &lt;a href="https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum.html"&gt;WCAG color contrast ratio&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Naming colors
&lt;/h2&gt;

&lt;p&gt;In modern web development, you're probably storing these color values in some kind of variable. I was a user of &lt;a href="https://sass-lang.com/documentation/variables"&gt;SCSS variables&lt;/a&gt; for a long time, but I've switched over to using &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/--*"&gt;native CSS custom properties&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Naming these variables can be challenging because you want to pick a name that's meaningful, unique, and searchable. I'm a fan of prefixing every variable with &lt;code&gt;color-&lt;/code&gt; to help my text editor give me a tab completion list when I start typing the word &lt;code&gt;color&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZmoZ8VoB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ryanfiller.com/images/named-color-variables.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZmoZ8VoB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ryanfiller.com/images/named-color-variables.png" alt="$color variable autocompleting in VS Code"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For more complex color palettes I like to name my colors with the &lt;a href="https://tanzu.vmware.com/content/blog/name-your-css-swatches-after-crayola-colors"&gt;Crayola naming convention&lt;/a&gt; so that each variant of, say "blue" has a unique name. These name variations can be tricky to choose out on your own, so I like to use &lt;a href="https://chir.ag/projects/name-that-color"&gt;this site&lt;/a&gt; to generate them. My friend Josh wrote &lt;a href="https://josh.beardedrobots.com/posts/crayola-naming/"&gt;an article that goes more in depth on this strategy&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;My color list is pretty simple so I named each color with a dark and light variant and stored them as CSS variables on the &lt;code&gt;:root&lt;/code&gt; element.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:root {
  --colorBlack: #080025;
  --colorWhite: #fefdf2;
  --colorPurpleLight: #a176b6;
  --colorPurpleDark: #5651a7;
  --colorBlueLight: #4f81c0;
  --colorBlueDark: #155f91;
  --colorOrangeLight: #f16a1f;
  --colorOrangeDark: #ac284f;
  --colorGrayLight: #5e828f;
  --colorGrayDark: #4e6773;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Narrowing down a theme
&lt;/h2&gt;

&lt;p&gt;I think there are six minimum colors required for any website color palette —&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PXXVRaQA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ryanfiller.com/images/color-scheme-chart-light.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PXXVRaQA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ryanfiller.com/images/color-scheme-chart-light.png" alt="light mode colors"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;text&lt;/strong&gt; - the main text color of the page&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;background&lt;/strong&gt; - the main background color of the page&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;primary&lt;/strong&gt; - a brand's primary color&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;highlight&lt;/strong&gt; - a standout color, used to highlight buttons or other calls-to-action&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;active&lt;/strong&gt; - another standout color, using to indicate a hover state or focused element&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;disabled&lt;/strong&gt; - a muted color to show an element cannot be interacted with&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's not to say those are the &lt;em&gt;only&lt;/em&gt; colors, but I personally think restraint is key to building consistent designs. Many design systems will use dark and light variations of each color for variety and increase the amount of accessible color combinations. I wanted my color palette to be sparse yet flexible and only have two versions of each color.&lt;/p&gt;

&lt;p&gt;To set the theme colors I created a second set of variables. Each of these new variables describes how the color would be used and has one of the original named colors assigned to it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:root {
  --colorText: var(--colorBlack);
  --colorBackground: var(--colorWhite);
  --colorPrimary: var(--colorPurpleDark);
  --colorHighlight: var(--colorBlueDark);
  --colorActive: var(--colorOrangeLight);
  --colorDisabled: var(--colorGrayLight);
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Converting a palette to Dark Mode
&lt;/h2&gt;

&lt;p&gt;I kept the same six utility colors from the default light mode theme, but reassigned new values for dark mode.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--e44SfdYu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ryanfiller.com/images/color-scheme-chart-dark.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--e44SfdYu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ryanfiller.com/images/color-scheme-chart-dark.png" alt="dark mode colors"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The closest color I have to an official "brand color" is #5651a7. This is the color I use for my page banners, and for the &lt;a href="https://www.ryanfiller.com/blog/automatic-social-share-images"&gt;social media image that is generated for each post&lt;/a&gt; so for branding reasons I kept it as my primary color.&lt;/p&gt;

&lt;p&gt;In addition to switching the &lt;code&gt;text&lt;/code&gt; and &lt;code&gt;background&lt;/code&gt; colors, I also picked brighter &lt;code&gt;highlight&lt;/code&gt; and &lt;code&gt;active&lt;/code&gt; colors that would stand out better on a dark background. I swapped &lt;code&gt;disabled&lt;/code&gt; for the darker gray variant since light text would be appearing on it and I wanted to ensure it was still easy to read.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@media (prefers-color-scheme: dark) {
  :root {
    --colorText: var(--colorWhite);
    --colorBackground: var(--colorBlack);
    --colorPrimary: var(--colorPurpleDark);
    --colorHighlight: var(--colorOrangeDark);
    --colorActive: var(--colorBlueLight);
    --colorDisabled: var(--colorGrayDark);
  }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I’ll cover this much more in part three, but it’s worth noting that media queries don’t increase &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity"&gt;specificity&lt;/a&gt;. To make sure these variables are overwritten you’ll want to put the &lt;code&gt;prefers-color-scheme: dark&lt;/code&gt; rules lower in the cascade.&lt;/p&gt;

&lt;h2&gt;
  
  
  Other considerations for Dark Mode
&lt;/h2&gt;

&lt;p&gt;The first instinct to convert a theme to dark mode is to switch the foreground and background colors. This will get a design pretty far, but there's more to consider than only these two colors. When I started my &lt;a href="https://www.ryanfiller.com/blog/starting-fresh-in-2020"&gt;site over in 2020&lt;/a&gt; with the default &lt;a href="https://github.com/gatsbyjs/gatsby-starter-default"&gt;Gatsby theme&lt;/a&gt; it came with some nice default colors. Beyond black and white there was also brand purple and several gray variants. When switching to a dark-background theme, some of the darker gray text is unreadable, and vice-versa with light grays on a light background.&lt;/p&gt;

&lt;p&gt;This awesome article by &lt;a href="https://twitter.com/adhuhamism"&gt;Adhuhamism&lt;/a&gt; on &lt;a href="https://css-tricks.com/a-complete-guide-to-dark-mode-on-the-web/#design"&gt;CSS-Tricks&lt;/a&gt; talks about other things to consider beyond just colors — like modifying the contrast ratio of images and making modifications to text weights and other subtle UI elements like borders and shadows.&lt;/p&gt;

</description>
      <category>code</category>
      <category>design</category>
      <category>colors</category>
      <category>branding</category>
    </item>
    <item>
      <title>A Short History of Interface Colors</title>
      <dc:creator>ryanfiller</dc:creator>
      <pubDate>Mon, 01 Feb 2021 01:23:54 +0000</pubDate>
      <link>https://dev.to/ryanfiller/a-short-history-of-interface-colors-16gc</link>
      <guid>https://dev.to/ryanfiller/a-short-history-of-interface-colors-16gc</guid>
      <description>&lt;p&gt;In the last year or so, adding a dark mode toggle to websites is a trend that has exploded for personal blogs and big brands. Thanks to some browser features and system APIs dark mode has become easier than ever to implement. I recently added this feature to my own site, and here are some things I learned along the way.&lt;/p&gt;

&lt;h2&gt;
  
  
  What even is Dark Mode?
&lt;/h2&gt;

&lt;p&gt;"Dark mode" refers to a &lt;a href="https://en.wikipedia.org/wiki/Light-on-dark_color_scheme"&gt;light-on-dark color scheme&lt;/a&gt;, or one where the background is a dark color with light text and graphics. This aesthetic has always been popular for applications like terminals and code editors, but has been making a comeback in more mainstream user interface design.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where did Dark Mode come from?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---Y3C0cRV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ryanfiller.com/images/oscillograph-asteroids.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---Y3C0cRV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ryanfiller.com/images/oscillograph-asteroids.jpg" alt="Asteroids-like video game played on an oscillograph"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you look back on the &lt;a href="https://eyeondesign.aiga.org/a-brief-history-of-dark-mode-from-the-matrix-like-displays-of-the-early-80s-to-today/"&gt;history of computer interfaces&lt;/a&gt;, light text on a dark background is originally how computer interfaces initially appeared. This had to do with the physical hardware limitations of computer monitors — &lt;a href="https://en.wikipedia.org/wiki/Cathode-ray_tube"&gt;cathode-ray tube&lt;/a&gt; displays were black in the off position and would use beams of colored RGB light to "paint" text and graphics onto a screen. Some of the earliest computer displays used only a single color to paint &lt;a href="https://en.wikipedia.org/wiki/Vector_monitor"&gt;vector graphics&lt;/a&gt; on to primitive screens.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--27iF6A5y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ryanfiller.com/images/xerox_star_gui.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--27iF6A5y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ryanfiller.com/images/xerox_star_gui.jpg" alt="Xerox Star workstation GUI"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As computers became more powerful and interfaces more sophisticated it was easier to fill entire screens with light. The first consumer &lt;a href="https://en.wikipedia.org/wiki/Graphical_user_interface"&gt;graphical user interface&lt;/a&gt; (or GUI) was introduced by &lt;a href="https://en.wikipedia.org/wiki/PARC_(company)"&gt;Xerox&lt;/a&gt; in 1973 and light themed interfaces have &lt;a href="https://www.webdesignerdepot.com/2009/03/operating-system-interface-design-between-1981-2009/"&gt;been the norm in home computing ever since&lt;/a&gt;. My best guess for why this trend is that with the rise of &lt;a href="https://en.wikipedia.org/wiki/Desktop_publishing"&gt;home desktop publishing&lt;/a&gt; users were more comfortable working with an interface that resembled paper.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xPZ1CS73--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ryanfiller.com/images/windows-95-desktop.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xPZ1CS73--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ryanfiller.com/images/windows-95-desktop.png" alt="Windows 95 desktop"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By the early 90s, computers were becoming a fixture in most homes almost all GUIs were in full color. Many of these operating systems allowed users to pick from predefined lists of themes to recolor their interfaces, some of which were light-on-dark styles and some of which were dark-on-light styles.&lt;/p&gt;

&lt;h2&gt;
  
  
  Modern Dark Mode
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pJ42O0bz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ryanfiller.com/images/apple-human-interface-guidelines-light-dark-mode.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pJ42O0bz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ryanfiller.com/images/apple-human-interface-guidelines-light-dark-mode.png" alt="Apple Human Interface Guidelines example of light versus dark mode"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What we think of as "dark mode" today really took off around the end of 2019. Apple introduced a system-wide preference for dark mode on desktops with their &lt;a href="https://en.wikipedia.org/wiki/MacOS_Mojave#User_interface"&gt;Mojave OS&lt;/a&gt; and for handheld devices with &lt;a href="https://en.wikipedia.org/wiki/IOS_13#User_interface"&gt;iOS 13&lt;/a&gt;, and Android did the same with &lt;a href="https://en.wikipedia.org/wiki/Android_10#User_experience"&gt;Android 10&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It has always been possible to create a custom theme implementation for a website or application using some combination of CSS and JavaScript. A big change to web development came in 2020 when all major web browsers began supporting the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme"&gt;&lt;code&gt;prefers-color-scheme&lt;/code&gt; media query&lt;/a&gt;. This is a flag that is passed from a user's operating system level preference into the browser, and CSS could check to see what that preference was and make decisions based on it. Along with the addition of &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties"&gt;CSS variables&lt;/a&gt;, web developers were granted a powerful set of tools to dynamically change styles based on these user preferences.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why should a site support Dark Mode?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  For Eye Health
&lt;/h3&gt;

&lt;p&gt;Although the facts seem to be debated, many users claim that using a dark theme reduces strain on their eyes. There does seem to be evidence that this is contextually true — light themes make sense for bright environments and dark themes for dark environments. There is also evidence to suggest that when using screens immediately before bedtime, &lt;a href="https://www.health.harvard.edu/staying-healthy/blue-light-has-a-dark-side"&gt;a dark theme will reduce the amount of blue light the eyes take in and make it easier to fall asleep&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  For Less Energy Consumption
&lt;/h3&gt;

&lt;p&gt;Less debatable than the topic of eye strain is the fact that &lt;a href="https://www.ifixit.com/News/16952/does-dark-mode-really-save-battery-on-your-phone"&gt;with certain screen types using a dark theme can conserve energy&lt;/a&gt;. This is because &lt;em&gt;not&lt;/em&gt; having to light up background pixels with white light can lead to less battery usage. As more and more devices are built with &lt;a href="https://en.wikipedia.org/wiki/OLED"&gt;OLED screens&lt;/a&gt; this can add up to huge savings over time and at scale. Users can consume less electricity, charge their phones less, replace their batteries with less frequency, and overall have a positive effect on the environment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Just Because
&lt;/h3&gt;

&lt;p&gt;The most important, in my opinion, is to honor a user's choice. In fact, that ethos is &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Cascade#origin_of_css_declarations"&gt;baked right into the design of CSS&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The user (or reader) of the web site can choose to override styles in many browsers using a custom user stylesheet designed to tailor the experience to the user's wishes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;While there may not be science to confirm that it is actually better for your eyes, many people prefer using sites with light text on a dark backgrounds. Letting users customize their experience is core to the web platform, and if a site can let them choose their color theme without having to write a custom style sheet, it should.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dark Mode isn't for Everybody
&lt;/h2&gt;

&lt;p&gt;Dark mode is certainly popular right now, but there are still a large number of people who prefer to read dark text on a light background — and that's okay! There are many &lt;a href="https://levelup.gitconnected.com/why-dark-mode-causes-more-accessibility-issues-than-it-solves-d2f8359bb46a"&gt;medical reasons&lt;/a&gt; why a person might want to stick with what has been a more traditional color scheme.&lt;/p&gt;

&lt;p&gt;With the tools currently available for the web platform it's certainly possible to let users make choices about how they want to view content.&lt;/p&gt;

</description>
      <category>design</category>
      <category>colors</category>
      <category>a11y</category>
      <category>research</category>
    </item>
    <item>
      <title>Automatic Social Share Images</title>
      <dc:creator>ryanfiller</dc:creator>
      <pubDate>Mon, 30 Nov 2020 14:13:05 +0000</pubDate>
      <link>https://dev.to/ryanfiller/automatic-social-share-images-2644</link>
      <guid>https://dev.to/ryanfiller/automatic-social-share-images-2644</guid>
      <description>&lt;p&gt;Every time I write a new blog post, I share it to Twitter. And, if I'm being honest, these tweets usually look pretty boring. Here's the tweet for last month's blog post.&lt;/p&gt;


&lt;blockquote&gt;
&lt;p&gt;Happy Halloween! Here's a sPoOkY new blog post about.... transforming markdown with &lt;a href="https://twitter.com/unifiedjs?ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;@unifiedjs&lt;/a&gt; tools? I don't know, its not really Halloween themed but hopefully its worth a read anyways! &lt;a href="https://t.co/a3rgZf6MAl" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://t.co/a3rgZf6MAl" rel="noopener noreferrer"&gt;https://t.co/a3rgZf6MAl&lt;/a&gt;&lt;/p&gt;— Ryan Filler (@ryanfiller_) &lt;a href="https://twitter.com/ryanfiller_/status/1322568875502931974?ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;October 31, 2020&lt;/a&gt;
&lt;/blockquote&gt;  

&lt;p&gt;When I built my post template I added the most basic &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta" rel="noopener noreferrer"&gt;&lt;code&gt;&amp;lt;meta&amp;gt;&lt;/code&gt; tags&lt;/a&gt; like &lt;code&gt;author&lt;/code&gt;, &lt;code&gt;description&lt;/code&gt;, and &lt;code&gt;keywords&lt;/code&gt;. Sites like Twitter are smart enough to use those tags and generate a basic preview card, but there are tools to augment the look of this card. The two most popular are &lt;a href="https://developer.twitter.com/en/docs/twitter-for-websites/cards/overview/markup" rel="noopener noreferrer"&gt;Twitter's Card Components&lt;/a&gt; and &lt;a href="https://developers.facebook.com/docs/sharing/webmasters" rel="noopener noreferrer"&gt;Facebook's Open Graph Image&lt;/a&gt;. Hooking into these protocols is as easy as adding some extra &lt;code&gt;&amp;lt;meta&amp;gt;&lt;/code&gt; tags to the page's &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;, but it's important to know the data type each tag is expecting.&lt;/p&gt;
&lt;h2&gt;
  
  
  Which Tags to Use?
&lt;/h2&gt;

&lt;p&gt;Most of the data I needed was already on each page, but to make sure it was seen by each crawler I needed to duplicate it into some platform-specific meta tags. A few have special values that need to be filled in with keywords, such as which card style to use, but most can be reused from regular SEO tags.&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;title&amp;gt;&lt;/span&gt;...&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;'author'&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;'...'&lt;/span&gt; &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;'description'&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;'...'&lt;/span&gt; &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;'keywords'&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;'...'&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 html"&gt;&lt;code&gt;  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;'twitter:card'&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;'summary_large_image'&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;  
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;'twitter:site'&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;'...'&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;'twitter:creator'&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;'...'&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;'twitter:url'&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;'...'&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;'twitter:title'&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;'...'&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;'twitter:description'&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;'...'&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;'twitter:image'&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;'...'&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;'twitter:image:alt'&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;'...'&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 html"&gt;&lt;code&gt;  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;property=&lt;/span&gt;&lt;span class="s"&gt;'og:type'&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;'article'&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;property=&lt;/span&gt;&lt;span class="s"&gt;'og:locale'&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;'en_US'&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;property=&lt;/span&gt;&lt;span class="s"&gt;'og:image:height'&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;'630'&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;property=&lt;/span&gt;&lt;span class="s"&gt;'og:image:width'&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;'1200'&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;property=&lt;/span&gt;&lt;span class="s"&gt;'og:site_name'&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;'...'&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;property=&lt;/span&gt;&lt;span class="s"&gt;'og:title'&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;'...'&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;property=&lt;/span&gt;&lt;span class="s"&gt;'og:description'&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;'...'&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;property=&lt;/span&gt;&lt;span class="s"&gt;'og:url'&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;'...'&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;property=&lt;/span&gt;&lt;span class="s"&gt;'og:image'&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;'...'&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;property=&lt;/span&gt;&lt;span class="s"&gt;'og:og:image:alt'&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;'...'&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;Both &lt;a href="https://developers.facebook.com/tools/debug/" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; and &lt;a href="https://cards-dev.twitter.com/validator" rel="noopener noreferrer"&gt;Open Graph&lt;/a&gt; have pretty good developer tools to test out their crawlers and debug any missing info. They also show a preview of how the final render will look so it’s a good opportunity to make sure the right data made it into the right tag.&lt;/p&gt;

&lt;p&gt;Both services offer multiple card styles — small images, large images, videos, galleries — but I wanted to attach a simple image with my posts. This meant that for Twitter I needed &lt;code&gt;name='twitter:card' content='summary_large_image'&lt;/code&gt; style, and &lt;code&gt;property='og:type' content='article'&lt;/code&gt; for Open Graph. I put together a quick design for an image with baked-in text that I could use whenever I shared any of my posts.&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%2Fwww.ryanfiller.com%2Fimages%2Fpreview-card-skeleton.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%2Fwww.ryanfiller.com%2Fimages%2Fpreview-card-skeleton.png" alt="basic preview card layout"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Dynamically Constructing a Preview Image
&lt;/h2&gt;

&lt;p&gt;Rather than making one-off images in Photoshop for each post like I have done in the past, I decided to create each image dynamically in HTML. To do this, I built a basic page and styled it with CSS to look like the card image I had designed.&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;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;'wrapper'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;'preview'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;main&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;'content'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;{TITLE}&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;ul&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;'tag-list'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;{CATEGORY}&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;{TAG}&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;{TAG}&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;'url'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{URL}&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;'author'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{AUTHOR}&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;'attribution'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;Image Credit:&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;{IMAGE CREDIT}&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/main&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;'{IMAGE SRC}'&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&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;I used &lt;a href="https://svelte.dev/" rel="noopener noreferrer"&gt;Svelte&lt;/a&gt; to build this template out, but all the tools used here are actually native browser APIs. Most of what I did can be accomplished using only HTML, CSS, and JavaScript DOM manipulation.&lt;/p&gt;

&lt;p&gt;This page will live at the &lt;a href="https://dev.to/generate-image"&gt;&lt;code&gt;/generate-image&lt;/code&gt;&lt;/a&gt; route on my website.&lt;/p&gt;

&lt;p&gt;Next, JavaScript needs to fill those tags in with dynamic data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Passing Data with URL Search Parameters
&lt;/h2&gt;

&lt;p&gt;Because I plan on making this page available &lt;em&gt;after&lt;/em&gt; the site has been statically rendered, I need a way to pass around variable data &lt;em&gt;without&lt;/em&gt; using &lt;code&gt;POST&lt;/code&gt; or &lt;code&gt;PUT&lt;/code&gt; requests. The best way to do this is to use the &lt;code&gt;search&lt;/code&gt; portion of a URL. &lt;a href="https://en.wikipedia.org/wiki/URL#Syntax" rel="noopener noreferrer"&gt;Wikipedia has a really good diagram&lt;/a&gt; of the anatomy or an entire URL, but in short &lt;code&gt;search&lt;/code&gt;, or &lt;code&gt;query&lt;/code&gt;, parameters are the section at the end of a URL following the &lt;code&gt;?&lt;/code&gt; character. These parameters are a special part of the URL that will not change which route the browser navigates to, and as many as needed can be chained together using the &lt;code&gt;?key1=value1&amp;amp;key2=value2&lt;/code&gt; syntax.&lt;/p&gt;

&lt;p&gt;One thing that &lt;code&gt;search params&lt;/code&gt; &lt;em&gt;don't&lt;/em&gt; do well is handle special characters. Since the &lt;code&gt;&amp;amp;&lt;/code&gt; character denotes a new piece of data trying to include an ampersand in either the key or value string will be problematic. Luckily, JavaScript provides several useful built-ins for dealing with this scenario.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;encodeURI()&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURI" rel="noopener noreferrer"&gt;&lt;code&gt;encodeURI&lt;/code&gt;&lt;/a&gt; (and it's reverse function &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURI" rel="noopener noreferrer"&gt;&lt;code&gt;decodeURI&lt;/code&gt;&lt;/a&gt;) can be used to transform an entire query string. This means that it will ignore &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURI#Description" rel="noopener noreferrer"&gt;special characters&lt;/a&gt; that are meaningful, such as the &lt;code&gt;?&lt;/code&gt;, &lt;code&gt;&amp;amp;&lt;/code&gt;, and &lt;code&gt;#&lt;/code&gt; so that it does not break the structure of the URL.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  abc 123 #&amp;amp;? ABC {}[]"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  abc%20123%20#&amp;amp;?%20ABC%20%7B%7D%5B%5D%22
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that space characters were replaced with &lt;code&gt;%20&lt;/code&gt; and other symbols like brackets are also encoded but &lt;code&gt;#&amp;amp;?&lt;/code&gt; were left as is. If you know for a fact that your data won't contain any special characters this method is the easiest to use, but I personally found it caused more problems than it solved.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;encodeURIComponent()&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent" rel="noopener noreferrer"&gt;&lt;code&gt;encodeURIComponent&lt;/code&gt;&lt;/a&gt; (and it's reverse function &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent" rel="noopener noreferrer"&gt;&lt;code&gt;decodeURIComponent&lt;/code&gt;&lt;/a&gt;) can be used to encode each individual part of a query rather than the entire query string. This means that they &lt;em&gt;will&lt;/em&gt; encode special characters that are part of the meaningful data for either the key or value of a query.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  abc 123 #&amp;amp;? ABC {}[]"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  abc%20123%20%23%26%3F%20ABC%20%7B%7D%5B%5D%22
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each section of a query can be encoded this way, but the URL will still need to be manually constructed with the &lt;code&gt;&amp;amp;&lt;/code&gt; and &lt;code&gt;=&lt;/code&gt; syntax in order for a browser to understand it correctly.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;URLSearchParams&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;em&gt;most&lt;/em&gt; helpful JavaScript built-in I found is the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams" rel="noopener noreferrer"&gt;&lt;code&gt;URLSearchParams&lt;/code&gt;&lt;/a&gt; interface. Constructing &lt;code&gt;new URLSearchParams()&lt;/code&gt; and passing an entire query string will build a JavaScript class that provides &lt;code&gt;set()&lt;/code&gt;, &lt;code&gt;get()&lt;/code&gt;, and other methods for working holistically with queries.&lt;/p&gt;

&lt;p&gt;Because  most of what I was doing for this project was going back and forth between strings and objects, I wrote two helper methods that used &lt;code&gt;URLSearchParams&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;objectToParams&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;object&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;params&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;URLSearchParams&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;entries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;object&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;entry&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="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;=&lt;/span&gt; &lt;span class="nx"&gt;entry&lt;/span&gt;
    &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&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="p"&gt;})&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&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="nf"&gt;paramsToObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;paramString&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;params&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;URLSearchParams&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;paramString&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;object&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="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="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;object&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;object&lt;/span&gt;
&lt;span class="p"&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 plaintext"&gt;&lt;code&gt;  {
    title: 'post title',
    categories: ['array', 'of', 'categories'].join(','),
    tags: ['array', 'of', 'tags'].join(','),
    imageSrc: 'image source',
    imageCredit: 'attribution',
    url: 'post url'
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  title=post+title&amp;amp;categories=array%2Cof%2Ccategories&amp;amp;tags=array%2Cof%2Ctags&amp;amp;imageSrc=image+source&amp;amp;imageCredit=attribution&amp;amp;url=post+url
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A downside to this tool is that unlike &lt;code&gt;encodeURI&lt;/code&gt; and &lt;code&gt;encodeURIComponent&lt;/code&gt; it is &lt;a href="https://caniuse.com/?search=URLSearchParams" rel="noopener noreferrer"&gt;not supported&lt;/a&gt; in any version of Internet Explorer. &lt;code&gt;URLSearchParams&lt;/code&gt; also doesn't prepend the final string with the &lt;code&gt;?&lt;/code&gt; character, so this will need to be added between the returned string and the main URL manually.&lt;/p&gt;

&lt;h2&gt;
  
  
  Replacing Data on the Page
&lt;/h2&gt;

&lt;p&gt;As I mentioned, my page is built using a &lt;a href="https://svelte.dev/docs#Template_syntax" rel="noopener noreferrer"&gt;Svelte template&lt;/a&gt;, which provides an &lt;a href="https://sapper.svelte.dev/docs#Arguments" rel="noopener noreferrer"&gt;easy way of getting query data&lt;/a&gt; and loading it into a page's HTML. Even with no framework some combination of &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Location/search" rel="noopener noreferrer"&gt;&lt;code&gt;window.location.search&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector" rel="noopener noreferrer"&gt;&lt;code&gt;document.querySelector&lt;/code&gt;&lt;/a&gt;, and &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML" rel="noopener noreferrer"&gt;&lt;code&gt;element.innerHTML&lt;/code&gt;&lt;/a&gt; should work for getting data from a URL and injecting it into the page.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using a Serverless Function to Capture the Page
&lt;/h2&gt;

&lt;p&gt;Now that a page exists and we can pass dynamic data to it, the next step is to create a way to turn the page into an image. To do this, I wrote a &lt;a href="https://www.netlify.com/products/functions/" rel="noopener noreferrer"&gt;Netlify Function&lt;/a&gt; that would use &lt;a href="https://developers.google.com/web/tools/puppeteer" rel="noopener noreferrer"&gt;Puppeteer&lt;/a&gt; to visit the page and take a screenshot of it. In a browser I could visit the URL of the Netlify function and pass it a query to build the screenshot with the &lt;code&gt;objectToParams&lt;/code&gt; function.&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="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;siteUrl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/.netlify/functions/generate-image?&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;imageParams&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Netlify functions work by exporting a &lt;code&gt;handler&lt;/code&gt; method which receives an &lt;a href="https://docs.netlify.com/functions/build-with-javascript/#synchronous-function-format" rel="noopener noreferrer"&gt;&lt;code&gt;event&lt;/code&gt;&lt;/a&gt; argument. &lt;code&gt;event&lt;/code&gt; is an object that contains data about when and how the function was called, including the &lt;code&gt;headers&lt;/code&gt; of the request and an object of &lt;code&gt;queryStringParameters&lt;/code&gt;. The &lt;code&gt;event.headers.host&lt;/code&gt; can be used to make sure the function is always called from the correct URL, even on &lt;a href="https://docs.netlify.com/site-deploys/overview/#deploy-preview-controls" rel="noopener noreferrer"&gt;preview deploys&lt;/a&gt;. We can also use the &lt;code&gt;objectToParams&lt;/code&gt; helper from earlier to pass all the query parameters through to the screenshot template page.&lt;/p&gt;

&lt;p&gt;One thing to note here is that testing this function locally using the &lt;a href="https://cli.netlify.com/" rel="noopener noreferrer"&gt;Netlify CLI&lt;/a&gt;, it will run on &lt;code&gt;http://localhost:8888&lt;/code&gt;. When the function is deployed, either to a preview or production version, it will run on &lt;code&gt;https://&lt;/code&gt;. Puppeteer will require a full URL, including the &lt;code&gt;protocol&lt;/code&gt;, so we can use an &lt;a href="https://docs.netlify.com/configure-builds/environment-variables/" rel="noopener noreferrer"&gt;environment variable&lt;/a&gt; to make sure the function sees either &lt;code&gt;http://&lt;/code&gt; or &lt;code&gt;https://&lt;/code&gt; when needed.&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;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&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="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NODE_ENV&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;development&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;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;`http://&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;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;host&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="s2"&gt;`https://&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;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;host&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;imageParams&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;objectToParams&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;queryStringParameters&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;screenshot&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;takeScreenshot&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;url&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/generate-image?&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;imageParams&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;&lt;code&gt;takeScreenshot()&lt;/code&gt; will accept a &lt;code&gt;url&lt;/code&gt; argument that contains the query parameters needed to construct the image and will open a &lt;a href="https://en.wikipedia.org/wiki/Headless_browser" rel="noopener noreferrer"&gt;headless&lt;/a&gt; version of a browser and return an encoded screenshot. The function is pretty straightforward — it will launch a browser, open a new page in that browser, resize the window, visit the given URL, take a screenshot, then return a &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/Base64" rel="noopener noreferrer"&gt;base64 encoded&lt;/a&gt; version of the image.&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;takeScreenshot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;browser&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;chromium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;puppeteer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&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;page&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;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newPage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setViewport&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;630&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1200&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;buffer&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;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;screenshot&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`data:image/png;base64,&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;base64&lt;/span&gt;&lt;span class="dl"&gt;'&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are a couple nuances though. First, Netlify functions have &lt;a href="https://docs.netlify.com/functions/overview/#default-deployment-options" rel="noopener noreferrer"&gt;limitations&lt;/a&gt;, including a 1024MB memory cap. If the function tries to install the full &lt;a href="https://www.npmjs.com/package/puppeteer-core" rel="noopener noreferrer"&gt;&lt;code&gt;puppeteer-core&lt;/code&gt; package&lt;/a&gt; it will go over this memory limit and crash. Instead, the function needs to rely on a minimal version of &lt;code&gt;puppeteer&lt;/code&gt; in the &lt;a href="https://www.npmjs.com/package/chrome-aws-lambda" rel="noopener noreferrer"&gt;&lt;code&gt;chrome-aws-lambda&lt;/code&gt; package&lt;/a&gt;. The second consideration is that since the function is now relying on a version of Chromium meant to run in a serverless context this function will not work when running it locally. There are several ways around this, but the one I had the most success with was to look at the &lt;code&gt;local&lt;/code&gt; variable and point the &lt;code&gt;browser.executablePath&lt;/code&gt; at the full install of &lt;a href="https://www.google.com/chrome/" rel="noopener noreferrer"&gt;Chrome&lt;/a&gt; on my computer. For this to work you need the full Chrome browser installed, and if you're not using a Mac the path will be different. When the function is &lt;em&gt;not&lt;/em&gt; running locally it will use the default &lt;code&gt;executablePath&lt;/code&gt; from the &lt;code&gt;chrome-aws-lambda&lt;/code&gt; package.&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;browser&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;chromium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;puppeteer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;executablePath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt; 
    &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/Applications/Google Chrome.app/Contents/MacOS/Google Chrome&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;chromium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;executablePath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;chromium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;defaultViewport&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;chromium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;defaultViewport&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headless&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;chromium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headless&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;h2&gt;
  
  
  Uploading the Screenshot to Cloudinary
&lt;/h2&gt;

&lt;p&gt;Now that the function has a way to get a data version of a screenshot, it needs somewhere to put it. Rather than return an image directly to the browser, I decided to store these images on &lt;a href="https://cloudinary.com/" rel="noopener noreferrer"&gt;Cloudinary&lt;/a&gt;. Cloudinary is a great &lt;a href="https://en.wikipedia.org/wiki/Content_delivery_network" rel="noopener noreferrer"&gt;CDN&lt;/a&gt;, has a &lt;a href="https://cloudinary.com/demos" rel="noopener noreferrer"&gt;ton of extra functionality&lt;/a&gt; I could use in the future, and has a &lt;a href="https://cloudinary.com/documentation/javascript_integration" rel="noopener noreferrer"&gt;JavaScript API&lt;/a&gt; I can use in this serverless function.&lt;/p&gt;

&lt;p&gt;The first thing to do was &lt;a href="https://github.com/cloudinary/cloudinary_npm#usage" rel="noopener noreferrer"&gt;configure the &lt;code&gt;cloudinary&lt;/code&gt; NPM package&lt;/a&gt; to work with my Cloudinary account. &lt;code&gt;CLOUDINARY_KEY&lt;/code&gt; and &lt;code&gt;CLOUDINARY_SECRET&lt;/code&gt; definitely should be kept as ENV variables and not shared, but I added my &lt;code&gt;CLOUDINARY_CLOUD&lt;/code&gt; this way too for some extra security.&lt;/p&gt;

&lt;p&gt;It is also important to use the &lt;code&gt;v2&lt;/code&gt; version of the &lt;code&gt;cloudinary&lt;/code&gt; package to get the correct API interface.&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;cloudinary&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;cloudinary&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;v2&lt;/span&gt;

&lt;span class="nx"&gt;cloudinary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;cloud_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CLOUDINARY_CLOUD&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;api_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CLOUDINARY_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;api_secret&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CLOUDINARY_SECRET&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The function needs to be able to know if this image already exists on Cloudinary. I don't want to overwrite existing images every time this function is loaded, and I also don't want to go through the process of starting up the screenshot tool if I won't need it. Since the &lt;code&gt;queryStringParameters&lt;/code&gt; are passed to the function as an object, the &lt;code&gt;title&lt;/code&gt; attribute for each post can be accessed and used as a unique identifier for each image.&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;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&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="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;slugify&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;queryStringParameters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&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;existingImage&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;getImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;title&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;existingImage&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="nx"&gt;existingImage&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;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;`http://&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;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;host&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="s2"&gt;`https://&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;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;host&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;imageParams&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;objectToParams&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;queryStringParameters&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;screenshotBuffer&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;takeScreenshot&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;url&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/generate-image?&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;imageParams&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newImage&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;putImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;screenshotBuffer&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;Since I knew the structure for the final URL for each screenshot, I ran &lt;a href="https://www.npmjs.com/package/node-fetch" rel="noopener noreferrer"&gt;&lt;code&gt;node-fetch&lt;/code&gt;&lt;/a&gt; against that URL. If it returned a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/404" rel="noopener noreferrer"&gt;&lt;code&gt;404&lt;/code&gt; Not Found response&lt;/a&gt; I knew that the image &lt;em&gt;didn't&lt;/em&gt; already exist and it was safe to run the screenshot function and start a new Cloudinary upload. If it did exist, I would return that URL in the &lt;code&gt;handler&lt;/code&gt; method. Cloudinary does provide a version number in their URLs (the number string between &lt;code&gt;/upload/&lt;/code&gt; and the folder name), but this can be omitted from the URL and the image will still be found.&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;getImage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;title&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;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://res.cloudinary.com/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CLOUDINARY_CLOUD&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/image/upload/social-images/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.png`&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&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="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&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;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;url&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="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&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;After the screenshot was generated, I passed it into yet another function to upload it to Cloudinary. &lt;code&gt;cloudinary.uploader.upload&lt;/code&gt; requires two arguments — an encoded image and a configuration object. &lt;code&gt;unique_filename: false&lt;/code&gt; is important because if Cloudinary appends generated numbers to each file to maintain uniqueness the &lt;code&gt;getImage()&lt;/code&gt; function won't be able to properly find existing images with &lt;code&gt;fetch&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;putImage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;buffer&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;cloudinaryOptions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;public_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`social-images/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;title&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="na"&gt;unique_filename&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="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="s2"&gt;`uploading &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; to cloudinary`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;cloudinary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uploader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;upload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cloudinaryOptions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;response&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If successful, &lt;code&gt;cloudinary.uploader.upload&lt;/code&gt; will return the URL to which the image was uploaded. I &lt;em&gt;should&lt;/em&gt; be able to assume the structure of this URL like I did in the &lt;code&gt;getImage()&lt;/code&gt; function, but to be safe I grabbed it from the Cloudinary response and used it inside the final &lt;code&gt;handler&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Forwarding to the Final URL in the Response
&lt;/h2&gt;

&lt;p&gt;The last thing to do is make to sure the final Cloudinary URL makes it to the browser. To do this, I used a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/308" rel="noopener noreferrer"&gt;&lt;code&gt;308&lt;/code&gt; Permanent Redirect&lt;/a&gt; in the response header returned from the function. This means that when a user visits the URL for the Netlify Function the process will run and automatically redirect them to either the existing or newly generated Cloudinary URL. The &lt;code&gt;308&lt;/code&gt; redirect should also help SEO crawlers handle this link correctly.&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;forwardResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;imageUrl&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="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;308&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Permanent Redirect&lt;/span&gt;
    &lt;span class="na"&gt;headers&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;location&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;imageUrl&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because I was uploading each image with the same URL structure this meant that I could dynamically reconstruct it inside the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; for each blog post.&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;socialImageUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://res.cloudinary.com/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CLOUDINARY_CLOUD&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/image/upload/social-images/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;slugify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$$props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;.png`&lt;/span&gt;

&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;twitter:image&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;socialImageUrl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;og:image&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;socialImageUrl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Final Function
&lt;/h2&gt;

&lt;p&gt;The final logic for the function without all of the abstracted helper methods included looked 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="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&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="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;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;queryStringParameters&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="s2"&gt;`no params given`&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;slugify&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;queryStringParameters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&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="s2"&gt;`processing &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;title&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;existingImage&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;getImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;title&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;existingImage&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="s2"&gt;`yay, &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; already existed`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;forwardResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;existingImage&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="s2"&gt;`generating image for &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;title&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;`http://&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;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;host&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="s2"&gt;`https://&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;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;host&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;imageParams&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;objectToParams&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;queryStringParameters&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;screenshot&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;takeScreenshot&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;url&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/generate-image?&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;imageParams&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newImage&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;putImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;screenshot&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="s2"&gt;`done with &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;title&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;return&lt;/span&gt; &lt;span class="nf"&gt;forwardResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newImage&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;To see the full function, with everything included, check it out &lt;a href="https://github.com/ryanfiller/portfolio-svelte/blob/main/functions/generate-image.js" rel="noopener noreferrer"&gt;on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The final image generated with the metadata from this post should look something like this.&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%2Fwww.ryanfiller.com%2Fimages%2Ffinal-card-image.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%2Fwww.ryanfiller.com%2Fimages%2Ffinal-card-image.png" alt="final generated screenshot for this post"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One added benefit is that even though the two protocols used here were created specifically by Twitter and Facebook, many other web-based services now tap into these tags and create custom preview cards as well.&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%2Fwww.ryanfiller.com%2Fimages%2Ffinal-card-image-composite.jpg" 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%2Fwww.ryanfiller.com%2Fimages%2Ffinal-card-image-composite.jpg" alt="final generated cards for this post"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Shoutouts
&lt;/h2&gt;

&lt;p&gt;I looked at a &lt;em&gt;lot&lt;/em&gt; of examples to pull all of this together, so I wanted to thank a few people who shared content examples or were nice enough to talk to me about issues I ran into.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://ireaderinokun.com/" rel="noopener noreferrer"&gt;Ire Aderinokun&lt;/a&gt; for writing an awesome &lt;a href="https://bitsofco.de/" rel="noopener noreferrer"&gt;Bits of Code&lt;/a&gt; article about &lt;a href="https://bitsofco.de/how-to-use-puppeteer-in-a-netlify-aws-lambda-function/" rel="noopener noreferrer"&gt;how to take a serverless screenshot&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://wesbos.com" rel="noopener noreferrer"&gt;Wes Bos&lt;/a&gt; for &lt;a href="https://github.com/wesbos/wesbos/blob/master/functions/ogimage/ogimage.js" rel="noopener noreferrer"&gt;code examples on how to use local Chrome&lt;/a&gt; and talking through image caching with me on Twitter&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.christopherbiscardi.com/" rel="noopener noreferrer"&gt;Chris Biscardi&lt;/a&gt; for this &lt;a href="https://egghead.io/playlists/building-an-opengraph-image-generation-api-with-cloudinary-netlify-functions-and-react-914e" rel="noopener noreferrer"&gt;free Egghead series on generating images, using the Cloudinary API, and using redirects&lt;/a&gt; and giving me advice on how to point to the correct domain for my functions&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.darrik.dev/" rel="noopener noreferrer"&gt;Darrik Moberg&lt;/a&gt; and &lt;a href="https://www.jason.af/" rel="noopener noreferrer"&gt;Jason Lengstorf&lt;/a&gt; for code examples and helping me debug on &lt;a href="http://discord.gg/partycorgi" rel="noopener noreferrer"&gt;Discord&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>seo</category>
      <category>serverless</category>
      <category>media</category>
      <category>code</category>
    </item>
    <item>
      <title>Transforming Markdown with Remark &amp; Rehype</title>
      <dc:creator>ryanfiller</dc:creator>
      <pubDate>Sat, 31 Oct 2020 16:09:34 +0000</pubDate>
      <link>https://dev.to/ryanfiller/transforming-markdown-with-remark-rehype-11pb</link>
      <guid>https://dev.to/ryanfiller/transforming-markdown-with-remark-rehype-11pb</guid>
      <description>&lt;p&gt;My blog, like a lot of &lt;a href="https://jamstack.org/"&gt;JAMstack&lt;/a&gt; content, is written in &lt;a href="https://en.wikipedia.org/wiki/Markdown"&gt;Markdown&lt;/a&gt;. I've &lt;a href="https://dev.to/blog/tips-and-tricks-ive-learned-about-gatsby-with-netlifycms#gatsby-plugin-mdx"&gt;written&lt;/a&gt; about how I've used &lt;a href="https://mdxjs.com/"&gt;&lt;code&gt;MDX&lt;/code&gt;&lt;/a&gt; as a parser to give my Markdown extra power. I've also written a bit about some of the &lt;a href="https://dev.to/blog/things-svelte-and-sapper-cant-do-yet#mdsvex-headings-in-sapper"&gt;shortcomings&lt;/a&gt; I've had trying to replicate the same &lt;code&gt;MDX&lt;/code&gt; functionality in &lt;a href="https://mdsvex.com/"&gt;&lt;code&gt;MDsveX&lt;/code&gt;&lt;/a&gt;. One thing that &lt;code&gt;MDX&lt;/code&gt; and &lt;code&gt;MDsveX&lt;/code&gt; have in common is that they are both built on top of the &lt;a href="https://unifiedjs.com/explore/package/remark/"&gt;&lt;code&gt;remark&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://unifiedjs.com/explore/package/rehype/"&gt;&lt;code&gt;rehype&lt;/code&gt;&lt;/a&gt; packages from the &lt;a href="https://unifiedjs.com/"&gt;unified&lt;/a&gt; ecosystem. Both parsers use these dependencies and they each have the ability to use any plugin that would work with the raw &lt;code&gt;remark&lt;/code&gt; or &lt;code&gt;rehype&lt;/code&gt; processors. To make my workflow less coupled to a specific technology, I decided to move the transformations up a level of abstraction. Instead of using &lt;code&gt;MDX&lt;/code&gt; I wrote a series of plugins to transform my markdown.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do Markdown Parsers Work?
&lt;/h2&gt;

&lt;p&gt;Markdown parsers work by taking a file, running it through a series of transformers, and then producing HTML for the browser. The transformation steps involve turning documents into a common shape that different tools can read and interpret, called an &lt;a href="https://en.wikipedia.org/wiki/Abstract_syntax_tree"&gt;abstract syntax tree&lt;/a&gt;. Put shortly —&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Abstract syntax trees are data structures widely used in compilers to represent the structure of program code. An AST is usually the result of the syntax analysis phase of a compiler. It often serves as an intermediate representation of the program through several stages that the compiler requires, and has a strong impact on the final output of the compiler.&lt;/p&gt;



&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Abstract_syntax_tree#Application_in_compilers"&gt;Wikipedia&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;// Markdown
&lt;span class="gh"&gt;# Luminous beings are we, not this crude matter.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;AST&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="err"&gt;type:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"heading"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;depth:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;children:&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;span class="err"&gt;type:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="err"&gt;value:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Luminous beings are we, not this crude matter."&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;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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;// HTML
&lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Luminous beings are we, not this crude matter.&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It isn't necessary to understand the computer science behind an abstract syntax tree (AST) to work with plugins. All you need to know is that an AST is an intermediary step that a compiler takes between taking in a &lt;code&gt;.md&lt;/code&gt; and outputting an &lt;code&gt;.html&lt;/code&gt; file. To paraphrase heavily from the &lt;a href="https://mdsvex.com/docs#remarkplugins--rehypeplugins"&gt;&lt;code&gt;mdsvex&lt;/code&gt; docs&lt;/a&gt;, the source file is first parsed into a Markdown AST (&lt;a href="https://github.com/syntax-tree/mdast"&gt;MDAST&lt;/a&gt;), where &lt;code&gt;remark&lt;/code&gt; plugins run. Then the data is converted into an HTML AST (&lt;a href="https://github.com/syntax-tree/hast"&gt;HAST&lt;/a&gt;), where &lt;code&gt;rehype&lt;/code&gt; plugins run. Finally the data is converted (stringified) into valid markup for the browser.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is Remark?
&lt;/h3&gt;

&lt;dl&gt;
  &lt;dt&gt;
    &lt;strong&gt;remark&lt;/strong&gt; is a &lt;a href="https://unifiedjs.com/explore/package/unified/" rel="nofollow noopener noreferrer"&gt;&lt;strong&gt;unified&lt;/strong&gt;&lt;/a&gt; processor to parse and serialize Markdown.
  &lt;/dt&gt;
  &lt;dd&gt;
    API by &lt;a href="https://unifiedjs.com/explore/package/unified/" rel="nofollow noopener noreferrer"&gt;&lt;strong&gt;unified&lt;/strong&gt;&lt;/a&gt;
  &lt;/dd&gt;
  &lt;dd&gt;
    Parses Markdown to a syntax tree with &lt;a href="https://unifiedjs.com/explore/package/remark-parse/" rel="nofollow noopener noreferrer"&gt;&lt;code&gt;remark-parse&lt;/code&gt;&lt;/a&gt;
&lt;/dd&gt;
  &lt;dd&gt;
    &lt;a href="https://github.com/syntax-tree/mdast" rel="nofollow noopener noreferrer"&gt;&lt;strong&gt;mdast&lt;/strong&gt;&lt;/a&gt; syntax tree
  &lt;/dd&gt;
  &lt;dd&gt;
    &lt;a href="https://github.com/remarkjs/remark/blob/main/doc/plugins.md" rel="nofollow noopener noreferrer"&gt;Plugins&lt;/a&gt; transform the tree
  &lt;/dd&gt;
  &lt;dd&gt;
    Serializes syntax trees to Markdown with &lt;a href="https://unifiedjs.com/explore/package/remark-stringify/" rel="nofollow noopener noreferrer"&gt;&lt;code&gt;remark-stringify&lt;/code&gt;&lt;/a&gt;
  &lt;/dd&gt;
&lt;/dl&gt;

&lt;h3&gt;
  
  
  What is Rehype?
&lt;/h3&gt;

&lt;dl&gt;
  &lt;dt&gt;
    &lt;strong&gt;rehype&lt;/strong&gt; is a &lt;a href="https://unifiedjs.com/explore/package/unified/" rel="nofollow noopener noreferrer"&gt;&lt;strong&gt;unified&lt;/strong&gt;&lt;/a&gt; processor to parse and serialize HTML
  &lt;/dt&gt;
  &lt;dd&gt;
    API by &lt;a href="https://unifiedjs.com/explore/package/unified/" rel="nofollow noopener noreferrer"&gt;&lt;strong&gt;unified&lt;/strong&gt;&lt;/a&gt;
  &lt;/dd&gt;
  &lt;dd&gt;
    Parses HTML to the tree with &lt;a href="https://unifiedjs.com/explore/package/rehype-parse/" rel="nofollow noopener noreferrer"&gt;&lt;code&gt;rehype-parse&lt;/code&gt;&lt;/a&gt;
  &lt;/dd&gt;
  &lt;dd&gt;
    &lt;a href="https://github.com/syntax-tree/hast" rel="nofollow noopener noreferrer"&gt;&lt;strong&gt;hast&lt;/strong&gt;&lt;/a&gt; syntax tree
  &lt;/dd&gt;
  &lt;dd&gt;
    &lt;a href="https://github.com/rehypejs/rehype/blob/master/doc/plugins.md" rel="nofollow noopener noreferrer"&gt;Plugins&lt;/a&gt; transform the tree
  &lt;/dd&gt;
  &lt;dd&gt;
    Serializes the tree to HTML with &lt;a href="https://unifiedjs.com/explore/package/rehype-stringify/" rel="nofollow noopener noreferrer"&gt;&lt;code&gt;rehype-stringify&lt;/code&gt;&lt;/a&gt;
  &lt;/dd&gt;
&lt;/dl&gt;

&lt;h3&gt;
  
  
  When to use Each?
&lt;/h3&gt;

&lt;p&gt;I couldn’t find a hard and fast rule for when to use &lt;code&gt;remark&lt;/code&gt; and when to use &lt;code&gt;rehype&lt;/code&gt;. There are ways  to get the same end result with either tool. My rule of thumb for  this project had to do with the original format of the content I was  manipulating. If the plugin would run on markdown syntax I used &lt;code&gt;remark&lt;/code&gt;. If the plugin was running on any HTML used directly in the document, I used &lt;code&gt;rehype&lt;/code&gt;. In either scenario the transformation is done by manipulating values in the syntax tree, so the process isn’t too different.&lt;/p&gt;

&lt;h2&gt;
  
  
  Manipulating the AST
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://css-tricks.com/how-to-modify-nodes-in-an-abstract-syntax-tree/"&gt;This CSS-Tricks article&lt;/a&gt; by &lt;a href="https://twitter.com/jlengstorf"&gt;Jason Lengstorf&lt;/a&gt; goes into deep detail about how ASTs work and best practices for editing them. Here are a few key points I wanted to highlight.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ASTs are the best way to make programmatic changes to HTML. HTML is hard (maybe even impossible) to parse with &lt;a href="https://en.wikipedia.org/wiki/Regular_expression"&gt;RegEx&lt;/a&gt;, so trying to change it without using an AST is often error prone.&lt;/li&gt;
&lt;li&gt;Contrary to usual best practices, ASTs should be treated as &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/Mutable"&gt;mutable&lt;/a&gt;. Because AST documents can be very large, making copies of the data and making changes to those copies can be detrimental to performance. Manipulations are best made directly to the original AST.&lt;/li&gt;
&lt;li&gt;AST transformations work &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/Recursion"&gt;recursively&lt;/a&gt;, so if new nodes are added the transformer will find them and try to transform them too. This is important to be aware of to avoid accidental infinitely deep loops.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Basic Plugin Structure
&lt;/h3&gt;

&lt;p&gt;A plugin works by creating a JavaScript function that returns a &lt;code&gt;transformer&lt;/code&gt; method. That transformer will be run on each &lt;code&gt;node&lt;/code&gt; that is found by the &lt;a href="https://www.npmjs.com/package/unist-util-visit"&gt;&lt;code&gt;unist-util-visit&lt;/code&gt; package's&lt;/a&gt; &lt;code&gt;visit&lt;/code&gt; method. The plugin will be called by the &lt;code&gt;unified&lt;/code&gt; process and will be passed the AST tree. There are many ways to directly mutate the tree, but I found the easiest was to use &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign"&gt;&lt;code&gt;Object.assign&lt;/code&gt;&lt;/a&gt; to overwrite the existing tree nodes with new values.&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="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;visit&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;unist-util-visit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;transformer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;visit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;TYPE&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;visitor&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;visitor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;newNode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;do work here&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newNode&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;function&lt;/span&gt; &lt;span class="nx"&gt;plugin&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="nx"&gt;transformer&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="nx"&gt;plugin&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second argument for the &lt;code&gt;visit&lt;/code&gt; method is a node &lt;code&gt;type&lt;/code&gt; that the transformation will be applied to. The &lt;code&gt;unist&lt;/code&gt; ecosystem comes with a predefined list of &lt;a href="https://github.com/syntax-tree/unist#literal"&gt;literal types&lt;/a&gt;, but plugins can define their own custom ones. I found that copy / pasting HTML into this &lt;a href="https://astexplorer.net/"&gt;AST Explorer tool&lt;/a&gt; was a super helpful way to find the exact string that each node type matched against. For HTML elements that don't have an explicit &lt;code&gt;type&lt;/code&gt;, &lt;code&gt;visit&lt;/code&gt; can find &lt;code&gt;raw&lt;/code&gt; nodes and then match them with RegEx. If, like me, you're not that great at RegEx, I found a &lt;a href="https://regex101.com/"&gt;RegeEx testing tool&lt;/a&gt; to be invaluable while I was working.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reworking Attributes on an Existing Element
&lt;/h3&gt;

&lt;p&gt;It is a &lt;a href="https://css-tricks.com/use-target_blank/"&gt;controversial opinion&lt;/a&gt;, but I prefer to use &lt;code&gt;target='_blank'&lt;/code&gt; on links external to my site. I cite a lot of sources via links, and I don't want readers to lose their place on my page after clicking on an external resource. There are two things this plugin needs to do — apply the correct &lt;code&gt;target&lt;/code&gt; attribute, but more importantly it needs to add some other attributes to fix &lt;a href="https://www.jitbit.com/alexblog/256-targetblank---the-most-underestimated-vulnerability-ever/"&gt;a security concern&lt;/a&gt;. One nice thing about adding these with a plugin is that I do not have to write these extra attributes manually on every link. Also if I ever change my opinion on where links should open I can remove them all at once by editing the plugin.&lt;/p&gt;

&lt;p&gt;The original AST tree for a &lt;code&gt;link&lt;/code&gt; node looks roughly like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  type: 'link',
  title: 'string,
  url: 'string',
  children: [
    {
      type: 'text',
      value: 'string',
    }
  ],
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In short, this plugin visits &lt;code&gt;link&lt;/code&gt; nodes and uses RegEx to determine if they linked to external sites, then if it does, assign some extra &lt;a href="https://github.com/syntax-tree/mdast-util-to-hast#hproperties"&gt;&lt;code&gt;hProperties&lt;/code&gt;&lt;/a&gt; to the node. Nodes of different types come with different default &lt;code&gt;data&lt;/code&gt; values (for instance, a &lt;code&gt;link&lt;/code&gt; node has a &lt;code&gt;data.url&lt;/code&gt; value), and &lt;code&gt;hProperties&lt;/code&gt; are sort of a catchall for all other attributes.&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;transformer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;visit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;link&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;visitor&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;visitor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&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;props&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hProperties&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hProperties&lt;/span&gt; &lt;span class="o"&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;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&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;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;siteUrl&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="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;_blank&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
      &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;noopener&lt;/span&gt;&lt;span class="dl"&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;span class="kd"&gt;function&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;transformer&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="nx"&gt;links&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;transformer&lt;/code&gt; function will look at all &lt;code&gt;links&lt;/code&gt;, determine if their &lt;code&gt;data.url&lt;/code&gt; contains the url of the current site, and assign &lt;code&gt;data.hProperties&lt;/code&gt; of &lt;code&gt;target = '_blank'&lt;/code&gt; and  &lt;code&gt;rel = 'noopener'&lt;/code&gt; to links that do not. Again, these values need to mutate the tree directly, so that's why the new values are set on the original &lt;code&gt;node&lt;/code&gt; rather than creating a copy.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;// Markdown
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;an intneral link&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://www.ryanfiller.com/about&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;an external link&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://developer.mozilla.org&lt;/span&gt;&lt;span class="p"&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 html"&gt;&lt;code&gt;// HTML
&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;"https://www.ryanfiller.com/about"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  an intneral link
&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;target=&lt;/span&gt;&lt;span class="s"&gt;"_blank"&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"noopener"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://developer.mozilla.org"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  an external link
&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See the full code for the plugin on &lt;a href="https://github.com/ryanfiller/portfolio-svelte/blob/main/plugins/remark/links.js"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Inserting New Child Elements
&lt;/h3&gt;

&lt;p&gt;This plugin reads any &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Heading_Elements"&gt;heading element&lt;/a&gt; and automatically converts it to a linkable hash. It also creates an &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; tag that a user can click to easily copy the hash out of the url bar.&lt;/p&gt;

&lt;p&gt;The original AST tree for a &lt;code&gt;heading&lt;/code&gt; node looks roughly like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  type: 'heading',
  depth: number,
  children: [
    {
      type: 'text',
      value: 'string'
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This plugin needs to take the original &lt;code&gt;text&lt;/code&gt; children of the element and convert it into a url hash. Then it replaces the original child elements with a new  anchor tag that contains both the original text and the hashed text. To handle some edge cases this plugin uses another plugin,  &lt;a href="https://www.npmjs.com/package/remark-stringify"&gt;remark-stringify&lt;/a&gt;, to convert the entire element into one string. This will catch scenarios where headings contain bold or italic text without accidentally stringifying a &lt;code&gt;&amp;lt;strong&amp;gt;&lt;/code&gt; or &lt;code&gt;&amp;lt;em&amp;gt;&lt;/code&gt; tag.&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;transformer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;visit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;heading&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;visitor&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;visitor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&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;props&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hProperties&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hProperties&lt;/span&gt; &lt;span class="o"&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;slugId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;slugify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;slugId&lt;/span&gt;
    &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;slugId&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;originalChildren&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;link&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;url&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;slugId&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="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;originalChildren&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;function&lt;/span&gt; &lt;span class="nx"&gt;headings&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="nx"&gt;transformer&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="nx"&gt;headings&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In order to wrap the original text element, the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax"&gt;...spread operator&lt;/a&gt; can be used to create a new array filled with the data from the original &lt;code&gt;heading&lt;/code&gt;. Then a new &lt;code&gt;children&lt;/code&gt; array that contains a single &lt;code&gt;link&lt;/code&gt; type node can be assigned to the &lt;code&gt;heading&lt;/code&gt;. The new &lt;code&gt;link&lt;/code&gt; node will have all the elements of the original heading.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;// Markdown
&lt;span class="gu"&gt;### It was a dark and stormy night.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;// HTML
&lt;span class="nt"&gt;&amp;lt;h3&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"it-was-a-dark-and-stormy-night"&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;"#it-was-a-dark-and-stormy-night"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    It was a dark and stormy night.
  &lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See the full code for the plugin on &lt;a href="https://github.com/ryanfiller/portfolio-svelte/blob/main/plugins/remark/headings.js"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wrapping an Element with a New Element
&lt;/h3&gt;

&lt;p&gt;In a &lt;a href="https://dev.to/blog/fighting-with-git-lfs#why-use-lfs"&gt;previous post&lt;/a&gt; I explained how I was using &lt;a href="https://www.netlify.com/products/large-media/"&gt;Netlify LFS&lt;/a&gt; to host and resize images using url parameters. This is something I wanted to automate using &lt;code&gt;remark&lt;/code&gt;. I also wanted to augment the native Markdown image syntax using the &lt;a href="https://www.npmjs.com/package/remark-attr"&gt;&lt;code&gt;remark-attr&lt;/code&gt; plugin&lt;/a&gt; to pass a &lt;code&gt;data-caption&lt;/code&gt; attribute. If this attribute exists I want to take that element and wrap it in a &lt;code&gt;&amp;lt;figure&amp;gt;&lt;/code&gt; tag with the caption inside of a &lt;code&gt;&amp;lt;figcaption&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The original AST tree for a &lt;code&gt;image&lt;/code&gt; node looks roughly like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  type: 'image',
  url: 'string',
  alt: 'string',
  data: {
    hProperties: {
      data-caption: 'string'
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The most important part of this transformation is to mutate the &lt;code&gt;children&lt;/code&gt; array in a way that doesn't cause an infinite loop. Since the &lt;code&gt;visit&lt;/code&gt; function will work recursively, if the number of children changes then the new nodes will also be visited. If a transformer creates a node that can itself be transformed, this will go on forever and crash the build process.&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;transformer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;visit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;image&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;visitor&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;visitor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&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;props&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hProperties&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hProperties&lt;/span&gt; &lt;span class="o"&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;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;alt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;alt&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;caption&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data-caption&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;skipSrcSet&lt;/span&gt; &lt;span class="o"&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.svg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ext&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ext&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;skipSrcSet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`srcset="&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="s2"&gt;?nf_resize=fit&amp;amp;w=500 500w, &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="s2"&gt;?nf_resize=fit&amp;amp;w=800 800w"
        sizes="100vw"
        src="&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="s2"&gt;?nf_resize=fit&amp;amp;w=1000"
      `&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;newNode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&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;caption&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;newNode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;figure&amp;gt;
          &amp;lt;img src=&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="s2"&gt; alt="&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" /&amp;gt;
          &amp;lt;figcaption&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;caption&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/figcaption&amp;gt;
        &amp;lt;/figure&amp;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;newNode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;img src=&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="s2"&gt; alt="&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newNode&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;function&lt;/span&gt; &lt;span class="nx"&gt;images&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="nx"&gt;transformer&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="nx"&gt;images&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since Netlify Large media will only offer transforms for certain types of images I created an array of extensions that I wanted my function to ignore. If the file type wasn't a &lt;code&gt;.gif&lt;/code&gt; or a &lt;code&gt;.svg&lt;/code&gt;, I would apply a series of query parameters to get back resized images. To decide between creating a &lt;code&gt;&amp;lt;figure&amp;gt;&lt;/code&gt; or an &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; an &lt;code&gt;if()&lt;/code&gt; statement checks whether the node contains &lt;code&gt;hProperties['data-caption]&lt;/code&gt;. In either scenario, a new node is created with type &lt;code&gt;html&lt;/code&gt; and a &lt;code&gt;value&lt;/code&gt; is passed with a string literal for what will be rendered in the DOM.&lt;/p&gt;

&lt;p&gt;In order to keep this plugin from infinitely creating and visiting new &lt;code&gt;image&lt;/code&gt; type nodes is to used &lt;code&gt;Object.assign&lt;/code&gt; to overwrite the current node and never create new ones. By doing this we preserve the original index in the AST tree and the transformer will understand that it's already seen this node and not visit it again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;// Markdown
&lt;span class="p"&gt;![&lt;/span&gt;&lt;span class="nv"&gt;alt text&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;/images/picture.jpg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;![&lt;/span&gt;&lt;span class="nv"&gt;alt text&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;/images/picture.jpg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;{data-caption='a caption'}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;// HTML
&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;"alt text"&lt;/span&gt;
  &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/images/picture.jpg?nf_resize=fit&amp;amp;w=1000"&lt;/span&gt;
  &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"/images/picture.jpg?nf_resize=fit&amp;amp;w=500 500w,
    /images/picture.jpg?nf_resize=fit&amp;amp;w=800 800w"&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;figure&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;"alt text"&lt;/span&gt;
    &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/images/picture.jpg?nf_resize=fit&amp;amp;w=1000"&lt;/span&gt;
    &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"/images/picture.jpg?nf_resize=fit&amp;amp;w=500 500w,
      /images/picture.jpg?nf_resize=fit&amp;amp;w=800 800w"&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;figcaption&amp;gt;&lt;/span&gt;
    a caption
  &lt;span class="nt"&gt;&amp;lt;/figcaption&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/figure&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See the full code for the plugin on &lt;a href="https://github.com/ryanfiller/portfolio-svelte/blob/main/plugins/remark/images.js"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Hijacking Existing Syntax to Create New Markup
&lt;/h3&gt;

&lt;p&gt;Markdown supports shorthand for a limited number of elements, and to the best of my knowledge no more are being added. For elements with no shorthand you can always use HTML directly in a &lt;code&gt;.md&lt;/code&gt; file. For some elements this can be very verbose. Wouldn't it be nice to steal some of the concise syntax for, say, an image but use it instead for a video file?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="p"&gt;![&lt;/span&gt;&lt;span class="nv"&gt;a video&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;'./video.mp4&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://res.cloudinary.com/practicaldev/image/fetch/s--PTwVN5Th--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ryanfiller.com/images/video-src-broken-image.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PTwVN5Th--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ryanfiller.com/images/video-src-broken-image.png" alt="a broken img tag with an .mp4 src attribute"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since linking to a video with the image syntax doesn't create a working element, &lt;code&gt;remark&lt;/code&gt; can change the markup to work for video. Be very careful with this idea and make sure you're not overwriting any valid usecases. I &lt;em&gt;think&lt;/em&gt; I'm fine in this scenario since pointing an &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag to a video file will always result in a broken image. Inside the &lt;code&gt;image&lt;/code&gt; transformer, the function can check the file type of the &lt;code&gt;src&lt;/code&gt; attribute and return a new &lt;code&gt;Object.assign&lt;/code&gt; with completely different markup depending on the extension.&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;transformer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;visit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;image&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;visitor&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;visitor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&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;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;alt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;alt&lt;/span&gt;

    &lt;span class="c1"&gt;// escape hatch into video component&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;node&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="nx"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.mp4&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;video&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
        &amp;lt;video title="&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" controls loop autoplay&amp;gt;
          &amp;lt;source src="&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="s2"&gt;" type="video/mp4" muted /&amp;gt;
          Sorry, your browser doesn't support embedded videos.
          &amp;lt;track kind="descriptions" label="&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" /&amp;gt;
        &amp;lt;/video&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="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;video&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// else, original image transformation&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;function&lt;/span&gt; &lt;span class="nx"&gt;images&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="nx"&gt;transformer&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="nx"&gt;images&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;// Markdown
&lt;span class="p"&gt;![&lt;/span&gt;&lt;span class="nv"&gt;alt text&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;/videos/video.mp4&lt;/span&gt;&lt;span class="p"&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 html"&gt;&lt;code&gt;// HTML
&lt;span class="nt"&gt;&amp;lt;video&lt;/span&gt; &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;"alt text"&lt;/span&gt; &lt;span class="na"&gt;controls&lt;/span&gt; &lt;span class="na"&gt;loop&lt;/span&gt; &lt;span class="na"&gt;autoplay&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;src=&lt;/span&gt;&lt;span class="s"&gt;"/videos/video.mp4"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"video/mp4"&lt;/span&gt; &lt;span class="na"&gt;muted&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  Sorry, your browser doesn't support embedded videos.
  &lt;span class="nt"&gt;&amp;lt;track&lt;/span&gt; &lt;span class="na"&gt;kind=&lt;/span&gt;&lt;span class="s"&gt;"descriptions"&lt;/span&gt; &lt;span class="na"&gt;label=&lt;/span&gt;&lt;span class="s"&gt;"alt text"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/video&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See the full code for the plugin on &lt;a href="https://github.com/ryanfiller/portfolio-svelte/blob/main/plugins/remark/video.js"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Transforming HTML
&lt;/h3&gt;

&lt;p&gt;So far all of the examples I’ve covered have taken in markdown content and looked at the MDAST tree to make changes. &lt;code&gt;rehype&lt;/code&gt; plugins will look at HTML content in the HAST tree and work almost exactly the same as &lt;code&gt;remark&lt;/code&gt; MDAST plugins. Nodes can have different types than the MDAST tree, but all of the same rules apply.&lt;/p&gt;

&lt;p&gt;Even though rendering an &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; in Markdown requires using the full HTML syntax, I still want to be as terse as possible and add extra values like &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Performance/Lazy_loading#Images_and_iframes"&gt;&lt;code&gt;loading='lazy'&lt;/code&gt;&lt;/a&gt; programmatically. Also, until the CSS &lt;a href="https://caniuse.com/mdn-css_properties_aspect-ratio"&gt;&lt;code&gt;aspect-ratio&lt;/code&gt; property&lt;/a&gt; officially lands, I still need to wrap &lt;code&gt;&amp;lt;iframes&amp;gt;&lt;/code&gt; in a &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; and use the &lt;a href="https://css-tricks.com/aspect-ratio-boxes/"&gt;padded box trick&lt;/a&gt; to maintain a responsive aspect ratio.&lt;/p&gt;

&lt;p&gt;Like &lt;code&gt;remark&lt;/code&gt;, &lt;code&gt;rehype&lt;/code&gt; only recognizes a handful of default &lt;code&gt;types&lt;/code&gt;. Also like &lt;code&gt;remark&lt;/code&gt;, custom &lt;code&gt;types&lt;/code&gt; can be created, but I found it was still easier to visit the default HTML &lt;code&gt;raw&lt;/code&gt; nodes and use RegEx to filter out the ones I wanted.&lt;/p&gt;

&lt;p&gt;The RegEx matcher I used looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;new RegExp(/&amp;lt;iframe(.*)&amp;lt;\/iframe&amp;gt;/g)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the matching AST node looks roughly like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  type: 'raw',
  value: '&amp;lt;iframe&amp;gt;...&amp;lt;/iframe&amp;gt;'
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A downside to working directly with the &lt;code&gt;raw&lt;/code&gt; node type is there is no concept similar to &lt;code&gt;hProperties&lt;/code&gt;. The only information the node has is its string literal &lt;code&gt;value&lt;/code&gt;, so any HTML attributes will need to be filtered out of that string with RegEx. I made a &lt;a href="https://github.com/ryanfiller/portfolio-svelte/blob/main/src/helpers/index.js#L13"&gt;helper function&lt;/a&gt; that would take a source string and attribute name and fish them out.&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;transformer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tree&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;visit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tree&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;raw&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;visitor&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;visitor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;iframeRegex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;RegExp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&amp;lt;iframe&lt;/span&gt;&lt;span class="se"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;.*&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;&amp;lt;&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;iframe&amp;gt;/g&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;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;node&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="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;iframeRegex&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;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;node&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="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;iframeRegex&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fishAttr&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;title&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;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fishAttr&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src&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;aspectRatio&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fishAttr&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data-aspect-ratio&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;newValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;div class="embed" data-aspect-ratio="&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;aspectRatio&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&amp;gt;
        &amp;lt;iframe src="&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="s2"&gt;" title="&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" loading="lazy"&amp;gt;&amp;lt;/iframe&amp;gt;
      &amp;lt;/div&amp;gt;`&lt;/span&gt;

      &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;node&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="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;iframeRegex&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="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;function&lt;/span&gt; &lt;span class="nx"&gt;embed&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="nx"&gt;transformer&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="nx"&gt;embed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another difference between &lt;code&gt;rehype&lt;/code&gt; and &lt;code&gt;remark&lt;/code&gt; is that only the node's &lt;code&gt;value&lt;/code&gt; property matters, so we can actually use &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace"&gt;&lt;code&gt;String.replace()&lt;/code&gt;&lt;/a&gt; instead of &lt;code&gt;Object.assign&lt;/code&gt;. This does as little modification as possible to the HAST tree and can prevent unintentionally assigning over important values, like whitespace, that the RegEx matcher may have accidentally grabbed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;// Markdown
&amp;lt;iframe
  src='https://ryan-responsive-iframe.netlify.com/'
  title='wide screen'
  data-aspect-ratio="16/9"
&lt;span class="gt"&gt;&amp;gt;&amp;lt;/iframe&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 html"&gt;&lt;code&gt;// HTML
&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;"embed"&lt;/span&gt; &lt;span class="na"&gt;data-aspect-ratio=&lt;/span&gt;&lt;span class="s"&gt;"16/9"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;iframe&lt;/span&gt;
    &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://ryan-responsive-iframe.netlify.com/"&lt;/span&gt;
    &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;"wide screen"&lt;/span&gt;
    &lt;span class="na"&gt;loading=&lt;/span&gt;&lt;span class="s"&gt;"lazy"&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;gt;&amp;lt;/iframe&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;See the full code for the plugin on &lt;a href="https://github.com/ryanfiller/portfolio-svelte/blob/main/plugins/rehype/embed.js"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Doing Work Asynchronously
&lt;/h3&gt;

&lt;p&gt;Since these plugins are just node functions, they have the ability to &lt;a href="https://www.npmjs.com/package/node-fetch"&gt;&lt;code&gt;fetch&lt;/code&gt;&lt;/a&gt; out to other sources and get dynamic data at build time. An issue with this workflow is that the &lt;code&gt;visit&lt;/code&gt; method provided by &lt;code&gt;unist-util-visit&lt;/code&gt; is not an &lt;code&gt;async&lt;/code&gt; function and cannot be used with the &lt;code&gt;await&lt;/code&gt; keyword.&lt;/p&gt;

&lt;p&gt;For this particular plugin, I wanted to take the &lt;a href="https://publish.twitter.com/?query=https%3A%2F%2Ftwitter.com%2Fryanfiller_%2Fstatus%2F1259280535421140998&amp;amp;widget=Tweet"&gt;Twitter provided HTML embed code&lt;/a&gt;, strip off the &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag that would load the JavaScript from Twitter, and instead use &lt;code&gt;rehype&lt;/code&gt; to format the HTML in a way I could style myself. I used the same strategy as matching &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; elements, but looked for &lt;code&gt;&amp;lt;blockquote&amp;gt;&lt;/code&gt;s that had a class of &lt;code&gt;"twitter-tweet"&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;new RegExp(/&amp;lt;blockquote class="twitter-tweet"&amp;gt;(.*)&amp;lt;\/blockquote&amp;gt;/)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the matching AST node looks roughly like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  type: 'raw',
  value: '&amp;lt;blockquote class="twitter-tweet"&amp;gt;...&amp;lt;/iframe&amp;gt;'
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To build my own Twitter component, I needed the url of a user's avatar from the Twitter website. If &lt;code&gt;fetch&lt;/code&gt; visits Twitter's regular homepage and cannot load client side JavaScript Libraries it will triggers the "you need to load React" warning screen. The only way (that I could think of, anyways) to get a parsable page that contains an avatar url is to &lt;code&gt;fetch&lt;/code&gt; against &lt;code&gt;mobile.twitter.com&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--v1SmrSl3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ryanfiller.com/images/mobile-vs-desktop-twitter.png%3Fnf_resize%3Dfit%26w%3D800" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--v1SmrSl3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ryanfiller.com/images/mobile-vs-desktop-twitter.png%3Fnf_resize%3Dfit%26w%3D800" alt="screenshots of mobile and desktop twitter pages"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Transforming the HTML from the Twitter blockquote works almost exactly the same as the &lt;code&gt;embed&lt;/code&gt; plugin. I used my &lt;code&gt;fishAttr&lt;/code&gt; function to get values from the original markup and then plug them into a string literal template. The difference is that instead of using &lt;code&gt;String.replace()&lt;/code&gt; to create new markup, I created a &lt;code&gt;render()&lt;/code&gt; function that could be called inside of a &lt;code&gt;Promise&lt;/code&gt; and passed the returned url for the avatar.&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;twitter&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="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tree&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;createTweet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;node&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;tweetRegex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;RegExp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&amp;lt;blockquote class="twitter-tweet"&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;.*&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;&amp;lt;&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;blockquote&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;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;node&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="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tweetRegex&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;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;node&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="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tweetRegex&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;render&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;avatarUrl&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`
        &amp;lt;div class="twitter-tweet"&amp;gt;
          // new markup here
        &amp;lt;/div&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="nx"&gt;visit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tree&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;raw&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;createTweet&lt;/span&gt;&lt;span class="p"&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="nx"&gt;twitter&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As I previously mentioned, &lt;code&gt;visit&lt;/code&gt; can’t be used directly with &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt;. Instead, the &lt;code&gt;createTweet&lt;/code&gt; method passed to &lt;code&gt;visit&lt;/code&gt; needs to &lt;code&gt;await&lt;/code&gt; the result of the &lt;code&gt;fetch&lt;/code&gt; &lt;code&gt;Promise&lt;/code&gt;. This can be done by storing that &lt;code&gt;Promise&lt;/code&gt; inside of a variable and pushing it to an array of pending &lt;code&gt;Promise&lt;/code&gt;s. Then, using &lt;code&gt;Promise.all&lt;/code&gt;, we can make sure everything pushed to that array is done before the final return from the plugin happens.&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;twitter&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="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tree&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;promises&lt;/span&gt; &lt;span class="o"&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;createTweet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;node&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;tweetRegex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;RegExp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&amp;lt;blockquote class="twitter-tweet"&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;.*&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;&amp;lt;&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;blockquote&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;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;node&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="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tweetRegex&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;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;node&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="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tweetRegex&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&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;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&amp;amp;mdash&lt;/span&gt;&lt;span class="se"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;.*&lt;/span&gt;&lt;span class="se"&gt;)\)&lt;/span&gt;&lt;span class="sr"&gt;/&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\((&lt;/span&gt;&lt;span class="sr"&gt;.*&lt;/span&gt;&lt;span class="se"&gt;)\)&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;1&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;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&amp;amp;mdash; &lt;/span&gt;&lt;span class="se"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;.*&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt; &lt;/span&gt;&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;1&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;render&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;avatarUrl&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;promise&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`https://mobile.twitter.com/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;handle&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;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;html&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;imageUrlRegex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;RegExp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&amp;lt;img alt="&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;" src="(.*)" \/&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;imageUrlMatch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;imageUrlRegex&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;imageUrlMatch&lt;/span&gt;&lt;span class="p"&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="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;avatarUrl&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;newNodeValue&lt;/span&gt; &lt;span class="o"&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;avatarUrl&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;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;node&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="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tweetRegex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newNodeValue&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;

      &lt;span class="nx"&gt;promises&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;promise&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;visit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tree&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;raw&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;createTweet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;await&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;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;promises&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="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;twitter&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A nice thing about &lt;code&gt;rehype&lt;/code&gt; plugins is that if they return an error, they do not transform the node and move on. If, for whatever reason, the plugin gets into the &lt;code&gt;.catch()&lt;/code&gt; block of the &lt;code&gt;fetch&lt;/code&gt; &lt;code&gt;Promise&lt;/code&gt;, the original blockquote will still exist as it originally was in the AST tree.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;// Markdown
&lt;span class="nt"&gt;&amp;lt;blockquote&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"twitter-tweet"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt; &lt;span class="na"&gt;dir=&lt;/span&gt;&lt;span class="s"&gt;"ltr"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    I need to make a tweet embed component for my blog.
  &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="ni"&gt;&amp;amp;mdash;&lt;/span&gt; Ryan Filler (@ryanfiller_)
  &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;"https://twitter.com/ryanfiller_/status/1259280535421140998?ref_src=twsrc%5Etfw"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    May 10, 2020
  &lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/blockquote&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 html"&gt;&lt;code&gt;// HTML
&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;"twitter-tweet"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&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;"https://twitter.com/ryanfiller_"&lt;/span&gt; &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;"RyanFiller"&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;"Ryan Filler"&lt;/span&gt;
        &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://pbs.twimg.com/profile_images/1038060989147766784/8P25vCc6_normal.jpg"&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &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;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://twitter.com/ryanfiller_"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        Ryan Filler
      &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;"https://twitter.com/ryanfiller_"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        @ryanfiller_
      &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;span class="nt"&gt;&amp;lt;a&lt;/span&gt;
      &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://twitter.com/ryanfiller_/status/1259280535421140998?ref_src=twsrc%5Etfw"&lt;/span&gt;
      &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;"View on Twitter"&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;/svg&amp;gt;&lt;/span&gt;
    &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;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    I need to make a tweet embed component for my blog.
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      May 10, 2020
    &lt;span class="nt"&gt;&amp;lt;/span&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;"/uses/#embedded-tweets"&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;/svg&amp;gt;&lt;/span&gt;
    &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;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See the full code for the plugin on &lt;a href="https://github.com/ryanfiller/portfolio-svelte/blob/main/plugins/rehype/twitter.js"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Huge thank you to &lt;a href="https://twitter.com/chrisbiscardi"&gt;Chris Biscardi&lt;/a&gt; for being available in the &lt;a href="https://www.partycorgi.com/"&gt;Party Corgi Discord group&lt;/a&gt; and pointing me to this &lt;code&gt;async&lt;/code&gt; example, again by &lt;a href="https://twitter.com/jlengstorf"&gt;Jason Lengstorf&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using the Plugins
&lt;/h2&gt;

&lt;p&gt;I've found two situations to use these plugins on my own site — to pass to a build tool to create pages, or to call from the &lt;code&gt;unified&lt;/code&gt; method directly for RSS.&lt;/p&gt;

&lt;p&gt;For compiling pages, an array of plugins can be passed as part of the configuration object to both &lt;a href="https://mdxjs.com/advanced/plugins"&gt;MDX&lt;/a&gt; (and its &lt;a href="https://www.gatsbyjs.com/plugins/gatsby-plugin-mdx/#remark-plugins"&gt;Gatsby implementation&lt;/a&gt;) and &lt;a href="https://mdsvex.com/docs#remarkplugins--rehypeplugins"&gt;MDsveX&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="c1"&gt;// gatsby-config.js&lt;/span&gt;
&lt;span class="nx"&gt;plugins&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="na"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gatsby-plugin-mdx&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;extensions&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;.md&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;remarkPlugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`remark-attr`&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nx"&gt;require&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;__dirname&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/plugins/remark/blockquote`&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nx"&gt;require&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;__dirname&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/plugins/remark/headings`&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nx"&gt;require&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;__dirname&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/plugins/remark/images`&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nx"&gt;require&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;__dirname&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/plugins/remark/links`&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;rehypePlugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nx"&gt;require&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;__dirname&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/plugins/rehype/embed`&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nx"&gt;require&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;__dirname&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/plugins/rehype/twitter`&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// rollup.config.js&lt;/span&gt;
&lt;span class="nx"&gt;svelte&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;mdsvex&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;extension&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.md&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;remarkPlugins&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;attr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;every&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
      &lt;span class="nx"&gt;blockquote&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;headings&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;images&lt;/span&gt;&lt;span class="p"&gt;,&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="na"&gt;rehypePlugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="nx"&gt;embed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;twitter&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;The entire purpose of this refactor was to make my content more  portable. The best part of this is that these plugins can be run outside of a framework. Like any other &lt;code&gt;remark&lt;/code&gt; or &lt;code&gt;rehype&lt;/code&gt; plugins, they can also be called as chained methods from the parent &lt;code&gt;unified&lt;/code&gt; function. I use this in a &lt;a href="https://github.com/ryanfiller/portfolio-svelte/blob/main/src/helpers/get-pages.js"&gt;helper method&lt;/a&gt; to create &lt;code&gt;json&lt;/code&gt; and &lt;code&gt;rss&lt;/code&gt; endpoints of my content.&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;unified&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;remarkParse&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;// use remark plugins here&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;remarkStringify&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;remarkToRehype&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;// use rehype plugins here&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rehypeStringify&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;process&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="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Plugins will need to run at certain points during the transformation life cycle, and going from a markdown file to a final HTML document requires a particular set of &lt;code&gt;parse&lt;/code&gt; and &lt;code&gt;stringify&lt;/code&gt; functions depending on the data.&lt;/p&gt;

&lt;p&gt;For now, these plugins only live in a &lt;a href="https://github.com/ryanfiller/portfolio-svelte/tree/main/plugins"&gt;directory&lt;/a&gt; in my project. If your project could benefit from similar transformations, feel free to copy and paste them into your own project or reach out &lt;a href="https://twitter.com/ryanfiller_"&gt;on Twitter&lt;/a&gt; and let me know — I can look into abstracting these and open sourcing them on NPM.&lt;/p&gt;

</description>
      <category>markdown</category>
      <category>blogging</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Svelte, Sanity, and Severless Functions</title>
      <dc:creator>ryanfiller</dc:creator>
      <pubDate>Sun, 27 Sep 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/ryanfiller/svelte-sanity-and-severless-functions-h93</link>
      <guid>https://dev.to/ryanfiller/svelte-sanity-and-severless-functions-h93</guid>
      <description>&lt;p&gt;Last month I wrote about creating an &lt;a href="https://dev.to/blog/building-a-component-in-three-frameworks"&gt;NPM package with Svelte&lt;/a&gt;. This month I built a basic &lt;a href="https://en.wikipedia.org/wiki/Create,_read,_update_and_delete" rel="noopener noreferrer"&gt;CRUD site&lt;/a&gt; that would let other people use the &lt;a href="https://www.npmjs.com/package/color-contrast-table" rel="noopener noreferrer"&gt;&lt;code&gt;color-contrast-table&lt;/code&gt; package&lt;/a&gt; tool I made.&lt;/p&gt;

&lt;p&gt;The sitemap for the project was pretty basic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HOMEPAGE
 - A list of all the site's users
 - New Users can be created here.
  └─ /OWNER 
   - A user's list of palettes that they own.
   - New Palettes can be created here.
    └─ /PALETTE
     - A specific color palette that contains an array of color data.
     - New Colors can be created here.
     - A user can go into edit mode to live edit and save existing colors.
     - A user can view and edit JSON for the color data.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the final product, check out the code on &lt;a href="https://github.com/ryanfiller/color-contrast-site" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; or the &lt;a href="//colors.ryanfiller.com/"&gt;live site&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Tech Stack
&lt;/h2&gt;

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

&lt;p&gt;Because I wrote the same NPM package in three frameworks, I had three options to choose from for this project. I've mentioned in several previous posts that I'm interested in learning more about &lt;a href="https://svelte.dev/" rel="noopener noreferrer"&gt;Svelte&lt;/a&gt;. Svelte is also a great fit for this project becase, as their homepage says Svelte is -&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Truly reactive. No more complex state management libraries — Svelte brings reactivity to JavaScript itself.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Built-in, easy reactivity is ideal since the main feature of this site are users changing color values and viewing information in real time.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
    &lt;br&gt;
    Sorry, your browser doesn't support embedded videos.&lt;br&gt;
  a user using the edit feature on the site&lt;/p&gt;
&lt;h3&gt;
  
  
  Routify
&lt;/h3&gt;

&lt;p&gt;Because I already used &lt;a href="https://sapper.svelte.dev/" rel="noopener noreferrer"&gt;Sapper&lt;/a&gt; on a few other projects, I started this one out using it too. However, I quickly ran into a big issue.&lt;/p&gt;

&lt;p&gt;Sapper works best in two scenarios:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;as a node.js app running on a server and dynamically generating pages&lt;/li&gt;
&lt;li&gt;as a statically served site with a predetermined URL structure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Serving the site statically meant that I would need to trigger a build and a re-deploy any time a user added any new data. While it is possible to deploy a Sapper project in Node-mode to &lt;a href="https://vercel.com/" rel="noopener noreferrer"&gt;Vercel&lt;/a&gt;, I wanted to host this site on Netlify to avoid trying too many new things at once. Maybe next time I'll give Vercel a shot.&lt;/p&gt;

&lt;p&gt;Luckily, &lt;a href="https://routify.dev/" rel="noopener noreferrer"&gt;Routify&lt;/a&gt; exists as an entirely client-side routing solution for Svelte apps. This means that the site could still deploy statically to Netlify, and instead of running a Vercel server to create dynamic pages, I could do all the work in a user's browser. As a bonus, Routify has a very similar API to Sapper so it wasn't a huge lift to refactor.&lt;/p&gt;

&lt;p&gt;Routify has three main concepts.&lt;/p&gt;
&lt;h4&gt;
  
  
  File Structure
&lt;/h4&gt;

&lt;p&gt;Routify and Sapper both use &lt;a href="https://routify.dev/guide/introduction/structure" rel="noopener noreferrer"&gt;file system naming conventions&lt;/a&gt; to organize the site's final page heirarchy. The file structure within the &lt;code&gt;/pages&lt;/code&gt; directory roughly translates to how the site will be built.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;/pages/index.svelte/&lt;/code&gt; becomes the homepage and &lt;code&gt;/pages/page-name.svelte/&lt;/code&gt; would become &lt;code&gt;/page-name&lt;/code&gt;. Routify can also translate index files from directories into routes so &lt;code&gt;/pages/nested/index.svelte/&lt;/code&gt; would live at &lt;code&gt;/nested&lt;/code&gt; on the live site. Files, or folders, can be named with square brackets surrounding them to pass dynamic parameters — &lt;code&gt;/pages/nested/[name].svelte/&lt;/code&gt; would catch any direct subpage of the &lt;code&gt;/nested&lt;/code&gt; url, and would pass the &lt;code&gt;name&lt;/code&gt; value into that page as a parameter.&lt;/p&gt;

&lt;p&gt;My site structure was pretty shallow and my &lt;code&gt;/pages&lt;/code&gt; directory looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/pages
 ├─ index.svelte
 ├─ [owner]
 │ ├─ index.svelte
 │ └─ [palette].svelte
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;code&gt;&amp;lt;Router&amp;gt;&lt;/code&gt; Component
&lt;/h4&gt;

&lt;p&gt;Routify interacts with the browser's &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/URL" rel="noopener noreferrer"&gt;&lt;code&gt;window.url&lt;/code&gt; API&lt;/a&gt; by wrapping an entire app in the &lt;a href="https://routify.dev/guide/installation/install-to-existing-project" rel="noopener noreferrer"&gt;&lt;code&gt;&amp;lt;Router&amp;gt;&lt;/code&gt; component&lt;/a&gt;. The &lt;code&gt;dev&lt;/code&gt;, &lt;code&gt;serve&lt;/code&gt;, and &lt;code&gt;build&lt;/code&gt; commands in the &lt;code&gt;package.json&lt;/code&gt; file will need to be changed to use the &lt;code&gt;routify&lt;/code&gt; command instead of &lt;code&gt;rollup&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// app.svelte

&amp;lt;script&amp;gt;
  import { Router } from '@sveltech/routify'
  import { routes } from '@sveltech/routify/tmp/routes'
&amp;lt;/script&amp;gt;

&amp;lt;Router routes={routes} /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This looks sort of magical as the &lt;code&gt;routes&lt;/code&gt; object doesn't need to be manually defined anywhere. Instead it's imported from a file generated by Routify based on the file structure I outline above. This handles pushing the correct url to the browser when a user clicks a link, as well as catching a nested URL that a user might visit directly and loading the correct internal page.&lt;/p&gt;

&lt;h4&gt;
  
  
  Passing Url Parameters
&lt;/h4&gt;

&lt;p&gt;Routify will link between pages with regular &lt;code&gt;&amp;lt;a href='...'&amp;gt;&lt;/code&gt; tags, but to actually pass dynamic data into the special &lt;code&gt;[name].svelte&lt;/code&gt; file you need to use the &lt;a href="https://routify.dev/guide/navigation" rel="noopener noreferrer"&gt;&lt;code&gt;$url&lt;/code&gt; helper&lt;/a&gt;. The &lt;code&gt;$url()&lt;/code&gt; function accepts a URL string, using colons to denote variables, and also a &lt;a href="https://routify.dev/docs/helpers#url" rel="noopener noreferrer"&gt;'params' object&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For my site, each &lt;code&gt;owner&lt;/code&gt; contains a &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;slug&lt;/code&gt; property.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  name: 'Ryan Filler',
  slug: 'ryan-filler'
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In order to create a link that would read correctly and take the user to the correct page, the anchor tag looks like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;a href={$url('/:owner', {owner: owner.slug})}&amp;gt;
  {owner.name}
&amp;lt;/a&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  sanity.io
&lt;/h3&gt;

&lt;p&gt;Now that the site could dynamically access data, that data needed somewhere to live.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.sanity.io/" rel="noopener noreferrer"&gt;Sanity&lt;/a&gt; describes itself as a "fast, flexible platform for delivering structured content," which sort of means that it functions as a database as a service. Sanity works as a hosted store for data that can be easily retrieved via their &lt;a href="https://www.sanity.io/docs/http-api" rel="noopener noreferrer"&gt;HTTP API&lt;/a&gt; and their &lt;a href="https://www.sanity.io/docs/groq" rel="noopener noreferrer"&gt;GROQ&lt;/a&gt; language. GROQ, which is short for _Graph Oriented Query Language_, is very similar in syntax to &lt;a href="https://graphql.org/" rel="noopener noreferrer"&gt;GraphQL&lt;/a&gt; but without the need to write your own database resolvers. Sanity lets you post to a url, or use one of their &lt;a href="https://www.sanity.io/docs/js-client" rel="noopener noreferrer"&gt;language client interfaces&lt;/a&gt; to fetch and post data.&lt;/p&gt;

&lt;p&gt;I started my project with the &lt;a href="https://www.sanity.io/create?template=sanity-io%2Fsanity-template-sapper-blog" rel="noopener noreferrer"&gt;official Sapper Starter&lt;/a&gt;, but I wouldn't actually recommend doing this. This starter comes with a Sapper site (that I ended up replacing with Routify), and a way to locally run the backend &lt;a href="https://www.sanity.io/docs/sanity-studio" rel="noopener noreferrer"&gt;Sanity Studio&lt;/a&gt;. If you're using Sanity more as a CMS this probably makes sense, but since I really just needed a data store I found it much easier to skip the local development and work on my database directly in the hosted version on &lt;a href="https://sanity.studio" rel="noopener noreferrer"&gt;sanity.studio&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The "structured" aspect of Sanity's approach to data comes from the &lt;code&gt;schema.js&lt;/code&gt; file. A schema is a description of each data &lt;code&gt;type&lt;/code&gt; and what &lt;code&gt;fields&lt;/code&gt;, or accessible values, will be collected on each &lt;code&gt;type&lt;/code&gt;. Each &lt;code&gt;field&lt;/code&gt; is given a &lt;a href="https://www.sanity.io/docs/schema-types" rel="noopener noreferrer"&gt;"schema type"&lt;/a&gt; which defines what someone querying the data can expect to get back.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;schema.js&lt;/code&gt; file isn't &lt;em&gt;technically&lt;/em&gt; required since the Sanity API will accept any data and store it, but filling out this file helps make sure any posted data conforms to expectations.&lt;/p&gt;

&lt;p&gt;Since each page would need to be backed by a corresponding data type the schema for my site was roughly the same as the site map. The homepage would be a collection of &lt;code&gt;owners&lt;/code&gt;, each of which is defined in the schema like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  title: 'Owner',
  name: 'owner',
  type: 'document',
  fields: [
    {
      title: 'Name',
      name: 'name',
      type: 'string',
    },
    {
      title: 'Slug',
      name: 'slug',
      type: 'string'
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What makes Sanity and other services like it so awesome is the ability to define relationships between data without having to deeply understand the complexities of &lt;a href="https://en.wikipedia.org/wiki/Cardinality_(data_modeling)" rel="noopener noreferrer"&gt;data model relationships&lt;/a&gt;. I used to work at an agency that used a custom PHP CMS, and having to decide between &lt;code&gt;belongs-to&lt;/code&gt;, &lt;code&gt;belongs-to-many&lt;/code&gt;, &lt;code&gt;has-one&lt;/code&gt;, or &lt;code&gt;has-many&lt;/code&gt; was hands down the hardest part of working on any site.&lt;/p&gt;

&lt;p&gt;Since an Owner would be creating Palettes, I used the &lt;a href="https://www.sanity.io/docs/reference-type" rel="noopener noreferrer"&gt;reference type&lt;/a&gt; in my Palette schema to create this association relationship.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  title: 'Color Palette',
  name: 'palette',
  type: 'document',
  fields: [
    {
      title: 'Title',
      name: 'title',
      type: 'string'
    },
    {
      title: 'Slug',
      name: 'slug',
      type: 'string'
    },
    {
      title: 'Owner',
      name: 'owner',
      type: 'reference',
      to: [{type: 'owner'}]
    },
    {
      title: 'Colors',
      name: 'colors',
      type: 'array',
      of: [{type: 'color'}]
    },   
  ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each Palette would hold a list of Colors so I used the &lt;code&gt;of&lt;/code&gt; key to specify which other data type each element of the array would need to conform to.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  title: 'Color',
  name: 'color',
  type: 'object',
  fields: [
    {name: 'name', type: 'string', title: 'Name'},
    {name: 'value', type: 'string', title: 'Value'}
  ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Getting Data
&lt;/h4&gt;

&lt;p&gt;In order to make the next steps easier, I went ahead and seeded some data via the Studio hosted on sanity.io.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "name": "Ryan",
  "slug": "ryan",
  "_id": "b524755a-25b6-4619-b92d-fb23779ea709",
  "palettes": [
    {
      "title": "CMYK",
      "slug": "cmyk",
      "_id": "a8659824-ebcd-4afe-a099-fffee9018024",
      "owner":{
        "_ref": "b524755a-25b6-4619-b92d-fb23779ea709",
      },
      "colors":[
        {
          "name": "cyan",
          "value": "#00FFFF"
        },
        {
          "name": "magenta",
          "value": "#FF00FF"
        },
        {
          "name": "yellow",
          "value": "#FFFF00"
        },
        {
          "name": "black",
          "value": "#000000"
        }
      ]
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Assuming we have data in the Sanity backend, the next step is to get it to the frontend. The easiest way to do this is to use the &lt;a href="https://www.sanity.io/docs/js-client" rel="noopener noreferrer"&gt;JavaScript &lt;code&gt;@sanity/client&lt;/code&gt; package&lt;/a&gt;. This package provides an interface that can be called in other JavaScript files that, among other things, makes it much easier to work with GROQ queries. It needs a little bit of configuration to hook it up to your specific Sanity data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// sanityClient.js

import sanityClient from '@sanity/client'

const client = sanityClient({
  projectId: process.env.SANITY_ID,
  dataset: 'production'
  useCdn: false
})

export default client
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sanity says that the &lt;code&gt;projectID&lt;/code&gt; &lt;em&gt;isn't&lt;/em&gt; sensitive data and is fine to expose over network calls, but I went ahead and put it in an &lt;code&gt;ENV&lt;/code&gt; variable using the &lt;a href="https://www.npmjs.com/package/dotenv" rel="noopener noreferrer"&gt;&lt;code&gt;dotenv&lt;/code&gt;&lt;/a&gt; package just to be safe (more on this in the Sending Data section). Also, because of the live editing aspect of this project, the &lt;code&gt;useCdn: false&lt;/code&gt; option is actually super important in this scenario to make sure that users aren't seeing cached data after making updates to their colors.&lt;/p&gt;

&lt;h5&gt;
  
  
  Getting &lt;em&gt;All&lt;/em&gt; Data
&lt;/h5&gt;

&lt;p&gt;Once the Sanity client is configured it can be imported and used in any component. To retrieve data, the &lt;code&gt;client.fetch()&lt;/code&gt; method must be passed a GROQ query as the first argument. It can also accept an optional second arguement, a &lt;code&gt;params&lt;/code&gt; object containing dynamic variables.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// index.svelte

&amp;lt;script&amp;gt;
  import client from '../sanityClient.js'

  const query = '*[_type == "owner"]'

  const getData = async () =&amp;gt; {
    return client.fetch(query)
    .then(response =&amp;gt; {
      ...
    })
    .catch(err =&amp;gt; this.error(500, err))
  }
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This paragraph from &lt;a href="https://www.sanity.io/docs/groq-syntax" rel="noopener noreferrer"&gt;the GROQ documentation&lt;/a&gt; explains how a simple query works.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;*&lt;/code&gt; returns all documents in the dataset that the current user has permissions to read. The documents are passed to a filter, which retains documents for which the expression evaluates to &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Since this query fetches the data for the homepage, which will show a list of all Owners, the query will use the &lt;code&gt;*&lt;/code&gt; selector to return all documents, then filter out ones that do not pass the &lt;code&gt;[_type == "owner"]&lt;/code&gt; check and return only Owners.&lt;/p&gt;

&lt;p&gt;Because the &lt;code&gt;client.fetch()&lt;/code&gt; returns a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise" rel="noopener noreferrer"&gt;&lt;code&gt;Promise&lt;/code&gt;&lt;/a&gt;, Svelte's &lt;a href="https://svelte.dev/docs#await" rel="noopener noreferrer"&gt;&lt;code&gt;await&lt;/code&gt; blocks&lt;/a&gt; make it super easy to handle the different loading states in the UI.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// index.svelte

&amp;lt;script&amp;gt;
  const getData = async () =&amp;gt; {...}
&amp;lt;/script&amp;gt;

{#await getData()}
  &amp;lt;Loading /&amp;gt;
{:then}
  ...
{:catch error}
  &amp;lt;Error /&amp;gt;
{/await}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  Getting &lt;em&gt;Some&lt;/em&gt; Data
&lt;/h5&gt;

&lt;p&gt;Once someone is on a specific Owner's page it makes sense to only load the data relevant to that Owner. To do this, we can use the &lt;code&gt;client.fetch()&lt;/code&gt; &lt;code&gt;params&lt;/code&gt; argument to pass variables to the GROQ query. Because part of the Routify link data includes the &lt;code&gt;owner.slug&lt;/code&gt; data, that can be used to look up a Sanity owner with matching &lt;code&gt;slug&lt;/code&gt; value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// [owner]/index.svelte

&amp;lt;script&amp;gt;
  import { params } from '@sveltech/routify'
  import client from '../../sanityClient'

  const { owner } = $params

  const query = `
    *[_type == 'owner' &amp;amp;&amp;amp; slug == $owner] {
      ...
    }
  `

  const queryArgs = { 
    owner: owner
  }

  const getData = async () =&amp;gt; {
        return client.fetch(query, queryArgs)
        .then(response =&amp;gt; {
       ...
    })
    .catch(err =&amp;gt; this.error(500, err))
  }
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;$params&lt;/code&gt; (not to be confused with the Sanity &lt;code&gt;params&lt;/code&gt; argument) is a Svelte store imported from the &lt;code&gt;@sveltech/routify&lt;/code&gt; package. It allows information to be pulled from the &lt;code&gt;&amp;lt;Router /&amp;gt;&lt;/code&gt; and used inside other components.&lt;/p&gt;

&lt;p&gt;Inside the GROQ query, the &lt;a href="https://www.sanity.io/docs/groq-syntax#identifiers-d3bdd1c14bbe" rel="noopener noreferrer"&gt;&lt;code&gt;$&lt;/code&gt; identifier&lt;/a&gt; can be used to interpolate a dynamic value. I did some abstracting in the block above to make the component a little easier to read, but the end &lt;code&gt;client.fetch()&lt;/code&gt; call will look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;client.fetch(`
  *[_type == 'owner' &amp;amp;&amp;amp; slug == $owner] {
    // projection here
  }
`, {owner: $params.owner})
.then(response =&amp;gt; {
    ...
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once we've looked up the &lt;code&gt;owner&lt;/code&gt; that matches the current page, we can use Sanity's &lt;a href="https://www.sanity.io/docs/how-queries-work#projections-727ecb6f5e15" rel="noopener noreferrer"&gt;&lt;code&gt;projections&lt;/code&gt;&lt;/a&gt; on that filter and get more data from it. The query can be expanded to look up a specific &lt;code&gt;owner&lt;/code&gt;, get their &lt;code&gt;_id&lt;/code&gt; attribute, then use that &lt;code&gt;_id&lt;/code&gt; to look up all documents of the &lt;code&gt;palette&lt;/code&gt; type that have a &lt;code&gt;_ref&lt;/code&gt; value matching the Owner's &lt;code&gt;_id&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const query = `
  *[_type == 'owner' &amp;amp;&amp;amp; slug == $owner] {
    name,
    slug,
    _id,
    'palettes': *[_type == 'palette' &amp;amp;&amp;amp; owner._ref == ^._id] {
      title,
      slug
    }
  }
`
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Projections are similar to &lt;a href="https://graphql.org/learn/queries/#fields" rel="noopener noreferrer"&gt;GraphQL &lt;code&gt;fields&lt;/code&gt;&lt;/a&gt; and can be used to shape the data that will come back in the HTTP response. The query above would return JSON data in the shape built out in the Projection. In a Projection, you are able to ask for any &lt;code&gt;field&lt;/code&gt; that exists on a &lt;code&gt;type&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "result": [
    {
      "name": "Ryan",
      "slug": "ryan",
      "_id": "b524755a-25b6-4619-b92d-fb23779ea709",
      "palettes": [
        {
          "slug": "website-2020",
          "title": "website 2020"
        },
        { ... },
        { ... },
        { ... },
        { ... }
      ]
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Filters and projections can be nested as deep as necessary (though keep in mind this might slow a query down), and as many &lt;code&gt;params&lt;/code&gt; as needed can be passed. That means that on the page for a specific color palette, the query looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const { owner, palette } = $params

const query = `
  *[_type == 'owner' &amp;amp;&amp;amp; slug == $owner] {
    name,
    slug,
    _id,
    'palettes': *[_type == 'palette' &amp;amp;&amp;amp; owner._ref == ^._id &amp;amp;&amp;amp; slug == $palette] {
      title,
      slug,
      _id,
      colors
    }
  }
`

const queryArgs = { owner, palette }

const getData = async () =&amp;gt; {
  return client.fetch(query, queryArgs)
  .then(response =&amp;gt; {
    ...
  })
  .catch(err =&amp;gt; this.error(500, err))
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;... and would return data like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "result": [
    {
      "name": "Ryan",
      "slug": "ryan",
      "_id": "b524755a-25b6-4619-b92d-fb23779ea709",
      "palettes": [
        {
          "title": "website 2020",
          "slug": "website-2020",
          "_id": "NOuiz2BuYnd9zvcp3ofESs",
          "colors": [
            {
              "name": "primary",
              "value": "#0f3287"
            },
            {
              "name": "dark",
              "value": "#151d43"
            },
            {
              "name": "light",
              "value": "#eee9d5"
            },
            {
              "name": "highlight",
              "value": "#da4181"
            },
            {
              "name": "active",
              "value": "#df5909"
            },
            {
              "name": "disabled",
              "value": "#6577a6"
            }
          ]
        }
      ]
    }
  ] 
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The GROQ language can be a little confusing at first, and making a malformed query doesn't always return a super helpful error message. One helpful tool that Sanity provides is the &lt;a href="https://www.sanity.io/docs/the-vision-plugin" rel="noopener noreferrer"&gt;Vision Plugin&lt;/a&gt;. This plugin can be added to the project's Studio, either locally or hosted on &lt;a href="https://sanity.studio" rel="noopener noreferrer"&gt;sanity.studio&lt;/a&gt;, and used to try out queries and see their potential response data.&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%2Fwww.ryanfiller.com%2Fimages%2Fuploads%2Fsanity-vision-plugin.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%2Fwww.ryanfiller.com%2Fimages%2Fuploads%2Fsanity-vision-plugin.png" alt="sanity vision plugin showing query and response for an owner's color palettes"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Moving data around with Stores
&lt;/h4&gt;

&lt;p&gt;Cool, so, once we have data, we need to move it around to the correct places. To do this I relied heavily on Svelte's &lt;a href="https://svelte.dev/docs#svelte_store" rel="noopener noreferrer"&gt;&lt;code&gt;stores&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;On each page, there are two main data concerns — the Sanity data that needs to be shown, and the actions that a user can take to interact with that data. In a &lt;code&gt;stores.js&lt;/code&gt; file, I set up two &lt;a href="https://svelte.dev/docs#writable" rel="noopener noreferrer"&gt;&lt;code&gt;writable&lt;/code&gt;s&lt;/a&gt; that could hold this data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// stores.js

import { writable } from 'svelte/store'

export const data = writable({
  loading: false,
  error: null,
  response: [],
  owners: [],
  owner: null,
  palettes: [],
  palette: null,
  colors: []
})

export const actions = writable({
  buttons: [],
  current: null,
  error: false
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To keep page data in sync, every time I made a call to the Sanity &lt;code&gt;client&lt;/code&gt; I then took the response from the &lt;code&gt;fetch&lt;/code&gt; call and set the page's &lt;code&gt;$data&lt;/code&gt; store accordingly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// index.svelte

&amp;lt;script&amp;gt;
  import client from '../sanityClient.js'
  import { data } from '../stores.js'

  const query = '*[_type == "owner"]'

  const getData = async () =&amp;gt; {
    return client.fetch(query)
    .then(response =&amp;gt; data.set({
      ...data,
      owners: response
    })
    ).catch(err =&amp;gt; this.error(500, err))
  }
&amp;lt;/script&amp;gt;

{#await getData()}
  &amp;lt;Loading /&amp;gt;
{:then}
  {#if $data.owners.length}
    {#each $data.owners as owner}
      &amp;lt;a href={$url('/:owner', {owner: owner.slug})}&amp;gt;
        {owner.name}
      &amp;lt;/a&amp;gt;
    {/each}
  {:else}
    uh oh, there are no users.
  {/if}
{:catch error}
  &amp;lt;Error /&amp;gt;
{/await}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;$data&lt;/code&gt; holds multiple types of page information, so I used the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax" rel="noopener noreferrer"&gt;...spread&lt;/a&gt; to copy the existing data and &lt;code&gt;set&lt;/code&gt; a new value that only overwrites new data for the current page. On pages that had complicated data, it makes sense to use &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment" rel="noopener noreferrer"&gt;destructuring assignment&lt;/a&gt; to grab data off of the &lt;code&gt;response&lt;/code&gt; object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// [owner]/palette.svelte

const getData = async () =&amp;gt; {
  return client.fetch(query, queryArgs)
  .then(response =&amp;gt; {
    const {
      name,
      slug,
      _id: ownerId,
      palettes
    } = response[0]

    const {
      title,
      slug,
      _id: paletteId,
      colors
    } = response[0]

    return data.set({
      ...data,
      owner: { 
        name: name,
        slug: slug,
        id: ownerId
      },
      palette: {
        title: title,
        slug: slug,
        id: paletteId
      },
      colors: colors.map(color =&amp;gt; ({
        name: color.name,
        value: color.value
      }))
    })
  }
  ).catch(err =&amp;gt; this.error(500, err))
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To load the specific actions for each page, I called the &lt;code&gt;writeable.set()&lt;/code&gt; method any time a new layout was visited. On the &lt;code&gt;/owner/palette&lt;/code&gt; route, a user needs to be able to add a new color, edit existing colors, or edit the JSON code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// [owner]/palette.svelte

&amp;lt;script&amp;gt;
  import { actions } from '../../stores.js'

  actions.set({
    buttons: [
      {
        text: 'add a color',
        title: 'addColor', 
        icon: 'add',
        action: () =&amp;gt; $actions.current = 'addColor'
      },
      {
        text: 'edit colors',
        title: 'editColors', 
        icon: 'edit',
        action: () =&amp;gt; $actions.current = 'editColors'
      },
      {
        text: 'see JSON',
        title: 'seeCode', 
        icon: 'code',
        action: () =&amp;gt; $actions.current = 'seeCode'
      }
    ]
  })
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I mapped through these objects and used each to construct a button. Each configuration object could set things like a title and icon, and set the &lt;code&gt;$actions.current&lt;/code&gt; value to render a different component to allow the user to edit data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// actions-buttons.svelte

&amp;lt;script&amp;gt;
  import { actions } from '../stores.js'

  const icons = { ... } // a list of SVGs
&amp;lt;/script&amp;gt;

{#each $actions.buttons as button}
  &amp;lt;button
    title={button.disabled ? `uh oh, there's a data error` : title}
    on:click={button.action}
    class:active={button.active}
    disabled={button.disabled}
  &amp;gt;
    &amp;lt;svelte:component this={icons[button.icon]} /&amp;gt;
  &amp;lt;/button&amp;gt;
{/each}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The actions component was a &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; tag that contained a giant &lt;code&gt;if...else&lt;/code&gt; switch to conditionally show inputs that would post data back to the Sanity client.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// actions-area.svelte

&amp;lt;script&amp;gt;
  import { actions } from '../stores.js'

  const createNewColor = () =&amp;gt; { ... }
&amp;lt;/script&amp;gt;

&amp;lt;form id='action'&amp;gt;
  ...
  {if $actions.current === 'addColor'}
    &amp;lt;input
      type='text'
      id='new-color-name'
      name='new-color-name'
      bind:value={$data.color.name}
    /&amp;gt;
    &amp;lt;input
      type='color'
      id='new-color-value'
      name='new-color-value'
      bind:value={$data.color.value}
    /&amp;gt;
    &amp;lt;button on:click={createNewColor}&amp;gt;save color&amp;lt;/button&amp;gt;
  {:else if $actions.current === ...}
&amp;lt;/form&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the correct &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; elements are rendered, the Svelte &lt;a href="https://svelte.dev/docs#bind_element_property" rel="noopener noreferrer"&gt;&lt;code&gt;bind:&lt;/code&gt; directive&lt;/a&gt; is used to update the &lt;code&gt;$data&lt;/code&gt; store every time a user makes a change.&lt;/p&gt;

&lt;h3&gt;
  
  
  Netlify Functions
&lt;/h3&gt;

&lt;p&gt;Now that we have data and users can create or edit data, we need a way to send that new data back to Sanity. The &lt;a href="https://www.sanity.io/docs/js-client" rel="noopener noreferrer"&gt;Sanity &lt;code&gt;client&lt;/code&gt;&lt;/a&gt; can help here, with the &lt;code&gt;.create&lt;/code&gt; and &lt;code&gt;.mutate&lt;/code&gt; methods, but sending data requires a little more care than getting data.&lt;/p&gt;

&lt;p&gt;Technically, anyone is able to hit the Sanity url endpoint and query data. There are ways to lock this down, but in my app I'm not too worried about it. However, I don't want just anybody to be able to &lt;em&gt;write&lt;/em&gt; to that endpoint, which means I need a &lt;code&gt;sanityClient&lt;/code&gt; configured with an &lt;a href="https://www.sanity.io/docs/http-auth" rel="noopener noreferrer"&gt;authentication token&lt;/a&gt;. Tokens can be generated from the Sanity dashboard on &lt;a href="//manage.sanity.io"&gt;manage.sanity.io&lt;/a&gt;, under Settings &amp;gt; API &amp;gt; CORS Origins.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const client = sanityClient({
  projectId: process.env.SANITY_ID,
  dataset: 'production',
  token: process.env.SANITY_TOKEN
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'm choosing to host this site statically on Netlify, so I can't include that token in the project. Firstly, it would be exposed in the project's git repository, and secondly it would be sent openly "over the wire" in each HTTP &lt;code&gt;POST&lt;/code&gt; request.&lt;/p&gt;

&lt;p&gt;This is where &lt;a href="https://www.netlify.com/products/functions/" rel="noopener noreferrer"&gt;Netlify's serverless functions&lt;/a&gt; come in.&lt;/p&gt;

&lt;p&gt;"Serverless functions" are not actually functions that don't run on a server, they just don't run on &lt;em&gt;your&lt;/em&gt; server. A more accurate name for them is "cloud functions" since they run on other servers that exist only to perform one small action at a time.&lt;/p&gt;

&lt;p&gt;Netlify Functions are pretty straightforward to set up. There are steps in the documentation on how to set them up via the &lt;a href="https://docs.netlify.com/configure-builds/get-started/#basic-build-settings" rel="noopener noreferrer"&gt;online interface&lt;/a&gt;, or how to set them up directly in the &lt;a href="https://docs.netlify.com/configure-builds/file-based-configuration/#sample-file" rel="noopener noreferrer"&gt;&lt;code&gt;netlify.toml&lt;/code&gt; file&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I set up two small functions, one to &lt;code&gt;create&lt;/code&gt; and one to &lt;code&gt;mutate&lt;/code&gt;, and they look almost exactly the same. The contents of each file in the &lt;code&gt;/api&lt;/code&gt; folder will be packaged and deployed to a cloud server somewhere, so a new &lt;code&gt;sanityClient&lt;/code&gt; needed to be created instantiated of each.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// api/create.js

require('dotenv').config()
const sanityClient = require('@sanity/client')
const client = sanityClient({
  projectId: process.env.SANITY_ID,
  dataset: 'production',
  token: process.env.SANITY_TOKEN
})

exports.handler = async function(event, _context, callback) {
  const payload = JSON.parse(event.body)
  const result = await client.create(payload, { returnDocuments: true })
  callback(null, {
    statusCode: 200,
    body: JSON.stringify(result)
  })
}

// api/mutate.js

require('dotenv').config()
const sanityClient = require('@sanity/client')
const client = sanityClient({
  projectId: process.env.SANITY_ID,
  dataset: 'production',
  token: process.env.SANITY_TOKEN
})

exports.handler = async function(event, _context, callback) {
  const payload = JSON.parse(event.body)
  const result = await client.mutate(payload, { returnDocuments: true })
  callback(null, {
    statusCode: 200,
    body: JSON.stringify(result)
  })
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;returnDocuments: true&lt;/code&gt; option in the configuration object is important because it tells Sanity to return a representation of the document after the update request has succeeded. This means that the app can listen for the &lt;code&gt;fetch.response&lt;/code&gt; and update accordingly with the latest data.&lt;/p&gt;

&lt;p&gt;To avoid exposing my token credentials in these functions, I used the &lt;a href="https://www.npmjs.com/package/dotenv" rel="noopener noreferrer"&gt;&lt;code&gt;dotenv&lt;/code&gt;&lt;/a&gt; package again. After this package is installed, its &lt;code&gt;config()&lt;/code&gt; method needs to be called as soon as possible in the project, so I put it at the top of my &lt;code&gt;rollup.config.js&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// rollup.config.js

import dotenv from 'dotenv'
dotenv.config()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;process.env&lt;/code&gt; values can be defined in a &lt;code&gt;.env&lt;/code&gt; file locally (make sure to add this to your &lt;code&gt;.gitignore&lt;/code&gt; and commit it!), and in the Settings &amp;gt; Deploys &amp;gt; Environment section of the Netlify dashboard. These values don't have to match in both places, but they do both need to be valid tokens.&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%2Fwww.ryanfiller.com%2Fimages%2Fuploads%2Fnetlify-environment-dashboard.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%2Fwww.ryanfiller.com%2Fimages%2Fuploads%2Fnetlify-environment-dashboard.png" alt="Netlify dashboard, Settings Deploys &amp;gt; Environment"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Sending Data
&lt;/h4&gt;

&lt;p&gt;Once the Netlify Functions have been deployed, they live at a &lt;a href="https://docs.netlify.com/functions/build-with-javascript/#format" rel="noopener noreferrer"&gt;url corresponding to the name of the &lt;code&gt;.js&lt;/code&gt; file&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To send data to these endpoints, I intercepted the &lt;code&gt;POST&lt;/code&gt; action of the &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; element in my &lt;code&gt;actions-area.svelte&lt;/code&gt; component. Since the &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; values are bound to the &lt;code&gt;$data&lt;/code&gt; store, I could get values directly from the &lt;code&gt;$data&lt;/code&gt; object and &lt;code&gt;fetch&lt;/code&gt; against my Netlify function url.&lt;/p&gt;

&lt;p&gt;Adding new data means posting a new object to the &lt;code&gt;/functions/create&lt;/code&gt; endpoint. This object defines the &lt;code&gt;_type&lt;/code&gt; and information of the new document to be created.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// actions-area.svelte

const createNewOwner = () =&amp;gt; {
  fetch(`/.netlify/functions/create`, {
    method: 'POST', 
    credentials: 'same-origin',
    headers: { 'Content-Type': 'application/json' },
    body: {
      _type: '$data.owner',
      name: $data.owner,
      slug: slugify($data.owner)
    }
  }).then(response =&amp;gt; response.json())
  .then(response =&amp;gt; {
    $data.owners = [...$data.owners, response]
  })
  .catch(error =&amp;gt; console.log('error', error))
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In order to make sure that a user is seeing the most up to date representation of the data, every time a &lt;code&gt;fetch&lt;/code&gt; call is made the &lt;code&gt;$data&lt;/code&gt; store is updated with the &lt;code&gt;response&lt;/code&gt; object from the Netlify function.&lt;/p&gt;

&lt;p&gt;Adding to the &lt;code&gt;colors&lt;/code&gt; array, since it involves editing existing data rather than creating new data, is slightly different. To do this I need to hit my &lt;code&gt;/functions/mutate&lt;/code&gt; endpoint and give Sanity a &lt;a href="https://www.sanity.io/docs/http-patches" rel="noopener noreferrer"&gt;&lt;code&gt;patch&lt;/code&gt;&lt;/a&gt; to make.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// actions-area.svelte

const createNewColor = () =&amp;gt; {
  fetch(`/.netlify/functions/mutate`, {
    method: 'POST', 
    credentials: 'same-origin',
    headers: { 'Content-Type': 'application/json' },
    body: [
      {
        patch: {
          id: $data.palette.id,
          insert: {
            after: 'colors[-1]',
            items: [{ name: $data.color.name, value: $data.color.value }]
          }
        }
      }
    ]
  }).then(response =&amp;gt; response.json())
  .then(response =&amp;gt; {
    $data.colors = response[0].colors)
  })
  .catch(error =&amp;gt; console.log('error', error))
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because I need to add to append to the current array of colors the &lt;code&gt;/functions/mutate&lt;/code&gt; endpoint takes &lt;a href="https://www.sanity.io/docs/http-patches" rel="noopener noreferrer"&gt;an array of mutations&lt;/a&gt; to be performed on the document with the corresponding &lt;code&gt;id&lt;/code&gt; value. Because of the &lt;code&gt;returnDocuments: true&lt;/code&gt; option sent along with the request, I can then take the entire new array that contains both my old and new colors and set it to the &lt;code&gt;$data&lt;/code&gt; store.&lt;/p&gt;

&lt;h2&gt;
  
  
  Still TODO
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Tests
&lt;/h3&gt;

&lt;p&gt;This application could probably use some tests, and this would be a good opportunity to explore &lt;a href="https://docs.cypress.io/guides/guides/network-requests.html" rel="noopener noreferrer"&gt;mocking network activity using Cypress&lt;/a&gt;. I might get around to this in the future, but since this was just a fun side project there's also a chance I won't make time to thoroughly test it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Authentication
&lt;/h3&gt;

&lt;p&gt;Right now, this app works on the honor system — anyone can edit any colors. I hope they won't, but if this becomes a problem I could add authentication that would limit people to only making edits to things they were an Owner of. This might be a good learning experience for the fun of it, too. I could maybe implement &lt;a href="https://docs.netlify.com/visitor-access/identity/#enable-identity-in-the-ui" rel="noopener noreferrer"&gt;Netlify Auth&lt;/a&gt;, which I've worked a little with in the past, or take a look at something completely new like &lt;a href="https://auth0.com/" rel="noopener noreferrer"&gt;Auth0&lt;/a&gt;. I've heard good things about Auth0 and how it can be used as a serverless function.&lt;/p&gt;

</description>
      <category>svelte</category>
      <category>routify</category>
      <category>sanity</category>
      <category>serverless</category>
    </item>
    <item>
      <title>Building a Component in Three Frameworks</title>
      <dc:creator>ryanfiller</dc:creator>
      <pubDate>Mon, 31 Aug 2020 12:56:51 +0000</pubDate>
      <link>https://dev.to/ryanfiller/comparing-and-contrasting-creating-a-basic-widget-in-react-svelte-and-vue-hgh</link>
      <guid>https://dev.to/ryanfiller/comparing-and-contrasting-creating-a-basic-widget-in-react-svelte-and-vue-hgh</guid>
      <description>&lt;p&gt;I recently decided to break up my &lt;code&gt;color-contrast-chart&lt;/code&gt; NPM package to be less coupled to a specific framework. I split off the core functionality into &lt;a href="https://www.npmjs.com/package/color-contrast-table" rel="noopener noreferrer"&gt;one package&lt;/a&gt; and the React rendering bits into &lt;a href="https://www.npmjs.com/package/color-contrast-table-react" rel="noopener noreferrer"&gt;another&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I did this because I knew I would be building a &lt;a href="https://www.npmjs.com/package/color-contrast-table-svelte" rel="noopener noreferrer"&gt;Svelte version&lt;/a&gt; of the package to use on my new site whenever I get around to refactoring it with Sapper. Since I was already building two versions I figured I might as well make a third and try out &lt;a href="https://www.npmjs.com/package/color-contrast-table-vue" rel="noopener noreferrer"&gt;Vue&lt;/a&gt; for the first time. (Sorry &lt;a href="https://angular.io/" rel="noopener noreferrer"&gt;Angular&lt;/a&gt; friends, maybe next time.)&lt;/p&gt;

&lt;p&gt;This post is far from an exhaustive tutorial about how to use each framework, but more of a look into how each handles common frontend concerns at a high level. I've been working with React for almost four years, looking into Svelte on and off for about a year, and this will be my first time digging into Vue. I feel pretty confident in the React version, having refactored it several times already, but there's probably room for improvement in my Svelte and Vue implementations. If you have any constructive criticism, I'd absolutely love to hear it either on &lt;a href="https://github.com/ryanfiller/" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; or &lt;a href="https://twitter.com/ryanfiller_" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Specifications
&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%2Fwww.ryanfiller.com%2Fimages%2Fuploads%2Fcolor-chart.jpg" 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%2Fwww.ryanfiller.com%2Fimages%2Fuploads%2Fcolor-chart.jpg" alt="example color chart"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In order to make sure I was building the same features into each package, I came up with a short list of specifications. Each component would: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;accept a color array to be transformed by the &lt;code&gt;color-contrast-table&lt;/code&gt; core package&lt;/li&gt;
&lt;li&gt;be able to toggle a set of default styles on but expose a set of usable class names for custom styles&lt;/li&gt;
&lt;li&gt;have a pair of props that would be able to toggle the name and value being edited in real time and stored in local state&lt;/li&gt;
&lt;li&gt;be able to pass in custom functions for when &lt;code&gt;names&lt;/code&gt; and &lt;code&gt;values&lt;/code&gt; change as well as a function for when inputs &lt;code&gt;blur&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;I made a table of props and proptypes that each version of the component would need to conform to.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;name&lt;/th&gt;
&lt;th&gt;type&lt;/th&gt;
&lt;th&gt;notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;colors&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;array&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;see color-contrast-table
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;useStyles&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;boolean&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;turn on default styles for chart&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;editNames&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;boolean&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;activate input for color names, does not need to be passed if &lt;code&gt;onNamesChange&lt;/code&gt; is provided&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;editValues&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;boolean&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;activate input for color values, does not need to be passed if &lt;code&gt;onValuesChange&lt;/code&gt; is provided&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;onNamesChange&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;function&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;optional callback to be fired when color name inputs change, will be passed &lt;code&gt;(colorIndex, event.target.value)&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;onValueChange&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;function&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;optional callback to be fired when color value inputs change, will be passed &lt;code&gt;(colorIndex, event.target.value)&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;onInputBlur&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;function&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;optional callback to be fired on blur of any input, will be passed &lt;code&gt;(event)&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Scaffolding a New Project
&lt;/h2&gt;

&lt;p&gt;Creating a new project in a modern framework often require setting up complex environments and build setups. Luckily, all three frameworks offer a pretty straightforward ways to get a new project started.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;React offers a first party CLI application tool, &lt;a href="https://reactjs.org/docs/create-a-new-react-app.html#create-react-app" rel="noopener noreferrer"&gt;&lt;code&gt;create-react-app&lt;/code&gt;&lt;/a&gt;. It's definitely a fast and easy way to get a new project going, and comes with &lt;a href="https://jestjs.io/" rel="noopener noreferrer"&gt;Jest tests&lt;/a&gt; and &lt;a href="https://webpack.js.org/" rel="noopener noreferrer"&gt;Webpack&lt;/a&gt; already configured.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you have the CLI installed, you can run&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  create-react-app new-project
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's actually a really helpful section in the &lt;a href="https://reactjs.org/docs/create-a-new-react-app.html" rel="noopener noreferrer"&gt;React documentation&lt;/a&gt; suggesting some alternate tools for different scenarios if a full-blown Create React App would be too much overhead.&lt;/p&gt;

&lt;p&gt;If &lt;a href="https://www.typescriptlang.org/" rel="noopener noreferrer"&gt;TypeScript&lt;/a&gt; is your thing, I've heard good things about &lt;a href="https://www.npmjs.com/package/tsdx" rel="noopener noreferrer"&gt;&lt;code&gt;TSDX&lt;/code&gt;&lt;/a&gt; but have never used it myself.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Starting a new Svelte project could not be easier. There's a code snippet to copy and paste from &lt;a href="https://svelte.dev/" rel="noopener noreferrer"&gt;The Svelte homepage&lt;/a&gt; that runs a &lt;code&gt;degit&lt;/code&gt; command to create a Svelte app. It even uses &lt;a href="https://www.npmjs.com/package/npx" rel="noopener noreferrer"&gt;&lt;code&gt;npx&lt;/code&gt;&lt;/a&gt; so it doesn't required that you have &lt;code&gt;degit&lt;/code&gt; installed.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  npx degit sveltejs/template new-project
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While &lt;a href="https://github.com/Rich-Harris/degit" rel="noopener noreferrer"&gt;&lt;code&gt;degit&lt;/code&gt;&lt;/a&gt; is yet another tool built by Svelte's creator &lt;a href="https://twitter.com/Rich_Harris" rel="noopener noreferrer"&gt;Rich Harris&lt;/a&gt;, it isn't really a first party Svelte CLI tool. I think, though, that this speaks to how straightforward Svelte is compared to other frameworks. It can install from a simple repo with a &lt;code&gt;rollup.config.js&lt;/code&gt; (another Rich Harris tool) and not have to deal with a complex Webpack configuration. This is because all of Svelte's work is done at compile time, so there's no need to transform or polyfill anything for run time in the browser.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Vue offers an absolutely amazing &lt;a href="https://cli.vuejs.org/guide/" rel="noopener noreferrer"&gt;first party CLI&lt;/a&gt;. To start a new project, run
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  vue create new-project
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Honestly, this tool was magical, especially the &lt;a href="https://cli.vuejs.org/guide/prototyping.html" rel="noopener noreferrer"&gt;&lt;code&gt;vue serve&lt;/code&gt;&lt;/a&gt; command. It was crazy cool to be able to create any &lt;code&gt;.vue&lt;/code&gt; file, then run &lt;code&gt;vue serve&lt;/code&gt; and be able to view just that file being built and served in the browser.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dev Tools
&lt;/h2&gt;

&lt;p&gt;There isn't a ton to say about this, other than that all three frameworks have browser extension dev tools. I would highly recommend downloading them if you're doing any work with a particular framework. They help peer into component structure and data in more specific ways that a browser's normal inspect tools. This can save a lot of time when trying to diagnose the cause of an issue, plus it gives you some extra power to poke around in other people's websites and see how they're using a framework.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://addons.mozilla.org/en-US/firefox/addon/react-devtools/" rel="noopener noreferrer"&gt;Firefox&lt;/a&gt; | &lt;a href="https://chrome.google.com/webstore/detail/react-developer-tools" rel="noopener noreferrer"&gt;Chrome&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://addons.mozilla.org/en-US/firefox/addon/svelte-devtools/" rel="noopener noreferrer"&gt;Firefox&lt;/a&gt; | &lt;a href="https://chrome.google.com/webstore/detail/svelte-devtools/" rel="noopener noreferrer"&gt;Chrome&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://addons.mozilla.org/en-US/firefox/addon/vue-js-devtools/" rel="noopener noreferrer"&gt;Firefox&lt;/a&gt; | &lt;a href="https://chrome.google.com/webstore/detail/vuejs-devtools/" rel="noopener noreferrer"&gt;Chrome&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Components &amp;amp; Template Engines
&lt;/h2&gt;

&lt;p&gt;Components are small pieces of reusable code that combine to build up complex applications, which is called "component composition." Often they accept data from their parent components and display or transform it in some way. Most modern JavaScript frameworks refer to passed data as "props," short for properties.&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%2Fwww.ryanfiller.com%2Fimages%2Fuploads%2Fcolor-block.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%2Fwww.ryanfiller.com%2Fimages%2Fuploads%2Fcolor-block.png" alt="a color block component"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The most simple component in my color-chart is the &lt;code&gt;&amp;lt;Color&amp;gt;&lt;/code&gt; component. This component needs to look at a &lt;code&gt;color&lt;/code&gt; object which will contain a &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;value&lt;/code&gt;, &lt;code&gt;score&lt;/code&gt;, and contrast &lt;code&gt;ratio&lt;/code&gt;. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;There are two ways to create components in React: class components and function components. For the sake of simplicity in this post I'll be referring to function components. The React team is adamant that &lt;a href="https://reactjs.org/docs/hooks-intro.html#gradual-adoption-strategy" rel="noopener noreferrer"&gt;class components are not being deprecated&lt;/a&gt; and are still a totally valid way to approach React.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A React component is literally just a function that is exportd from a &lt;code&gt;.js&lt;/code&gt; or &lt;code&gt;.jsx&lt;/code&gt; file. Attributes that are passed to a component can be accessed with the &lt;code&gt;props&lt;/code&gt; argument of a function component.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;React&lt;/code&gt; package itself will need to be imported first thing in any React component file.&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;// color.jsx&lt;/span&gt;

  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&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;react&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;Color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&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="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    }
  }

  export default Color
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's not required to name this function, but doing so will make debugging a lot easier than &lt;code&gt;export default () =&amp;gt; {...}&lt;/code&gt; will.&lt;/p&gt;

&lt;p&gt;While technically not a requirement, the large majority of React projects use &lt;a href="https://reactjs.org/docs/introducing-jsx.html" rel="noopener noreferrer"&gt;JSX&lt;/a&gt; as the template tool. JSX is not only for React, but I've never seen one used without the other.&lt;/p&gt;

&lt;p&gt;JSX works &lt;em&gt;mostly&lt;/em&gt; like HTML, but with ability to interpolate Javascript by wrapping it in curly brackets (&lt;code&gt;{ }&lt;/code&gt;). This syntax can run expressions or print values, such as those from the &lt;code&gt;props&lt;/code&gt; object . String attributes can be passed using quotation marks, but anything else will need to use the &lt;code&gt;prop={value}&lt;/code&gt; bracket syntax.&lt;/p&gt;

&lt;p&gt;This particular component is self-contained, but if it needed to wrap and render another component it could use React's special &lt;a href="https://reactjs.org/docs/composition-vs-inheritance.html" rel="noopener noreferrer"&gt;&lt;code&gt;props.children&lt;/code&gt;&lt;/a&gt; value. This value will be automatically added to any component that contains children and doesn't need to be explicity passed anywhere.&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;// label.jsx&lt;/span&gt;

  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&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;react&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;Label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;span&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;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    }
  }

  export default Label
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A component can be imported into another component's file with an &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import" rel="noopener noreferrer"&gt;import statement&lt;/a&gt; and then used like any other element.&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;// color.jsx&lt;/span&gt;

  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&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;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Label&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;./label.jsx&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;Color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Label&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Label&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="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    }
  }

  export default Color
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;A Svelte component is a special type of file with the &lt;code&gt;.svelte&lt;/code&gt; extension that lets the Svelte compiler know it needs to be treated in a certain way. Behind the scenes, Svelte components are actually JavaScript classes, but writing them is a lot like writing &lt;a href="https://svelte.dev/docs#Component_format" rel="noopener noreferrer"&gt;regular HTML&lt;/a&gt;. JavaScript is contained within &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tags, CSS within &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; tags, and everything else on the page will be rendered as HTML. A Svelte component can consist of one or more of these three sections.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight svelte"&gt;&lt;code&gt;  // color.svelte

  &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/span&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;A few things are immediately different from React. The component is automatically named and exported based on the name of the file. Rather than be passed an a &lt;code&gt;props&lt;/code&gt; object, values must be created with the &lt;a href="https://svelte.dev/docs#1_export_creates_a_component_prop" rel="noopener noreferrer"&gt;&lt;code&gt;export let&lt;/code&gt; syntax&lt;/a&gt;. Sort of a running theme for Svelte, the syntax seems weird at first but makes sense the more you think about it. Think of it as declaring a reassignable variable, and exporting it so that it can be overwritten by other components.&lt;/p&gt;

&lt;p&gt;Similarly to JSX, Svelte uses curly brackets to escape HTML and interpolate JavaScript values declared in the &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag. Unlike JSX, anything that is correct in HTML is also valid in a Svelte template. Svelte describes itself as as "superset" of HTML, so "any valid HTML is valid Svelte."&lt;/p&gt;

&lt;p&gt;Component composition works slightly different as well. Rather than passing children via props, Svelte uses a special tag called a &lt;a href="https://svelte.dev/docs#slot" rel="noopener noreferrer"&gt;&lt;code&gt;&amp;lt;slot/&amp;gt;&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;slot&amp;gt;&lt;/code&gt; elements are functionally very different than JSX's &lt;code&gt;props.children&lt;/code&gt;, but at a basic level they should work mostly the same.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight svelte"&gt;&lt;code&gt;  // label.svelte

  &lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;slot&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Components can be imported within the &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; section of other components and used as custom HTML tags.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight svelte"&gt;&lt;code&gt;  // color.svelte

  &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Label&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;./label.svelte&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Label&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/Label&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;ul&gt;
&lt;li&gt;Vue, like React, has multiple ways to structure a component file.  I don't have enough experience to speak to the pros and cons of each, but I chose to write all my components using the &lt;a href="https://vuejs.org/v2/guide/single-file-components.html" rel="noopener noreferrer"&gt;single file component&lt;/a&gt; approach.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Apparently you &lt;em&gt;can&lt;/em&gt; &lt;a href="https://vuejs.org/v2/guide/render-function.html" rel="noopener noreferrer"&gt;use JSX with Vue&lt;/a&gt;, but none of the Vue developers I know have ever mentioned doing this. By default, Vue comes with its &lt;a href="https://vuejs.org/v2/guide/syntax.html" rel="noopener noreferrer"&gt;own template syntax&lt;/a&gt; that is similar to Svelte's.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;  // color.vue

  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;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="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;Color&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;color&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;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;
        &lt;span class="si"&gt;{{&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="si"&gt;}}&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&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;Just like Svelte, JavaScript in a Vue component lives inside a &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag at the top of the file. Unlike Svelte, this code is &lt;em&gt;required&lt;/em&gt; for a Vue component, and must contain a structured &lt;code&gt;default export&lt;/code&gt; object. Vue components instantiate JavaScript classes, and Vue forces you to be declarative when defining a component's dependcies. This makes sense if you think about writing the &lt;code&gt;constructor()&lt;/code&gt; function of a regular class.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;name&lt;/code&gt; key corresponds to how the component will be referenced in other files. All the props need to be explicitly added to the &lt;code&gt;props&lt;/code&gt; object. Once properties are assigned to the Vue component class, they can be used in the HTML by using the &lt;code&gt;this.&lt;/code&gt; syntax. &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this" rel="noopener noreferrer"&gt;&lt;code&gt;this&lt;/code&gt; in JavaScript&lt;/a&gt; can be kind of a hard thing to understand, but for this scenario think about it as "this component."&lt;/p&gt;

&lt;p&gt;Vue also needs to return a &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; tag in each component to register what HTML will be rendered. Interpolatin JavaScript in HTML uses &lt;a href="https://vuejs.org/v2/guide/syntax.html#Text" rel="noopener noreferrer"&gt;"Mustache" syntax&lt;/a&gt;, or double curly brackets (&lt;code&gt;{{ }}&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;For composing components, Vue also uses the &lt;a href="https://vuejs.org/v2/guide/components-slots.html" rel="noopener noreferrer"&gt;&lt;code&gt;&amp;lt;slot&amp;gt;&lt;/code&gt; tag&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;  // label.vue

  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;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="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;Label&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;slot&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&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;Like &lt;code&gt;props&lt;/code&gt;, any imported components will need to be registered inside the &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag. After that, they can be used inside the &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; as custom elements.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;  // color.vue

  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Label&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;./label.vue&lt;/span&gt;&lt;span class="dl"&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="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;Color&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;color&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;Label&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;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;Label&amp;gt;&lt;/span&gt;
        &lt;span class="si"&gt;{{&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="si"&gt;}}&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/Label&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Passing Props and Checking Types
&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%2Fwww.ryanfiller.com%2Fimages%2Fuploads%2Fcolor-block-2.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%2Fwww.ryanfiller.com%2Fimages%2Fuploads%2Fcolor-block-2.png" alt="a color block component with a background"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;props&lt;/code&gt;, like I briefly mentioned before, are how modern JavaScript frameworks pass data around. For the most part props "flow down," meaning that they are passed from parent to child and not in the other direction.&lt;/p&gt;

&lt;p&gt;In the color chart, each &lt;code&gt;&amp;lt;Color&amp;gt;&lt;/code&gt; component needs to get a certain set of color data from its parent &lt;code&gt;&amp;lt;Row&amp;gt;&lt;/code&gt; component. That data looks 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="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&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;purple&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// the color's name&lt;/span&gt;
    &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#800080&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="c1"&gt;// the color's hex value,&lt;/span&gt;
    &lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AAA&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// whether or not the color passes contrast with the row&lt;/span&gt;
    &lt;span class="nx"&gt;ratio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1.5&lt;/span&gt; &lt;span class="c1"&gt;// the actual contrast with the current row&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are three main ways data can be passed: as individual attributes; bundled up as an object containing multiple attributes that can be unpacked later; or using some variation of the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax" rel="noopener noreferrer"&gt;spread funtion&lt;/a&gt; to unpack the data &lt;em&gt;before&lt;/em&gt; it makes it into the component.&lt;/p&gt;

&lt;p&gt;The good news is that all three frameworks also have tools to add &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures" rel="noopener noreferrer"&gt;types&lt;/a&gt; to help manage a component's expectations around props data.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In React, &lt;code&gt;props&lt;/code&gt; are &lt;a href="https://reactjs.org/docs/components-and-props.html#props-are-read-only" rel="noopener noreferrer"&gt;immutable data passed into child components&lt;/a&gt;. This means that unless you go out of your way to allow changes, what you pass into a component will be exactly what the component will always render. Props are passed using regular HTML attribute syntax for strings, and the curly brackets syntax for any other type of value.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;  &lt;span class="c1"&gt;// row.jsx&lt;/span&gt;

  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&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;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Color&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;./color.jsx&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;Row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;tr&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="nc"&gt;Color&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;score&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;ratio&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ratio&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;tr&lt;/span&gt;&lt;span class="p"&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="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;Row&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To ensure that the &lt;code&gt;&amp;lt;Color&amp;gt;&lt;/code&gt; component knows what type to expect for each prop, React requires the &lt;a href="https://reactjs.org/docs/typechecking-with-proptypes.html" rel="noopener noreferrer"&gt;PropTypes&lt;/a&gt; addon. This used to be part of the core React package, but was split into its own dependency since using it is optional. It now needs to be separately imported into any file it is used in.&lt;/p&gt;

&lt;p&gt;A component's &lt;code&gt;.propTypes&lt;/code&gt; are assigned in the same file, after the main body of the component.&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;// color.jsx&lt;/span&gt;

  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&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;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;PropTypes&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;prop-types&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;Color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`Ratio &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ratio&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="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&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="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    }
  }

  Color.propTypes = &lt;span class="si"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PropTypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;string&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="nx"&gt;PropTypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PropTypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;ratio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PropTypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;
  &lt;span class="si"&gt;}&lt;/span&gt;

  export default Color
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To make this more concise, the entire &lt;code&gt;color&lt;/code&gt; object can be passed from &lt;code&gt;&amp;lt;Row&amp;gt;&lt;/code&gt; to &lt;code&gt;&amp;lt;Color&amp;gt;&lt;/code&gt;, then its attributes can be accessed inside the &lt;code&gt;&amp;lt;Color&amp;gt;&lt;/code&gt; component.&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;// row.jsx&lt;/span&gt;

  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&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;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Color&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;./color.jsx&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;Row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;tr&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="nc"&gt;Color&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&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;tr&lt;/span&gt;&lt;span class="p"&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="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;Row&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means an update in the Color's PropTypes. It's possible to deep check the shape of an object, but for the sake of simplicity let's just check that it &lt;em&gt;is&lt;/em&gt; an object.&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;// color.jsx&lt;/span&gt;

  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&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;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;PropTypes&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;prop-types&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;Color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`Ratio &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ratio&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="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&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="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    }
  }

  Color.propTypes = &lt;span class="si"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PropTypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;object&lt;/span&gt;
  &lt;span class="si"&gt;}&lt;/span&gt;

  export default Color
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The last way to pass props is to take advantage of the fact that they themselves are &lt;code&gt;object&lt;/code&gt; type and use the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax" rel="noopener noreferrer"&gt;...spread&lt;/a&gt; operator. Instead of passing the bundled object as one value, the spread operator will take each attribute and pass it individually. This is a particularly helpful tool when a parent is passed props that in turn need to all be passed through to a child. Rather than be set to the value of an attribute, this syntax is applied directly to the component.&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;// row.jsx&lt;/span&gt;

  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&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;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Color&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;./color.jsx&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;Row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;tr&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="nc"&gt;Color&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&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;tr&lt;/span&gt;&lt;span class="p"&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="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;Row&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because each attribute was passed unbundled from the single &lt;code&gt;color&lt;/code&gt; object, the type of each individual prop can be checked.&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;// color.jsx&lt;/span&gt;

  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&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;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;PropTypes&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;prop-types&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;Color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`Ratio &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ratio&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="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&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="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    }
  }

  Color.propTypes = &lt;span class="si"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PropTypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;string&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="nx"&gt;PropTypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PropTypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;ratio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PropTypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;
  &lt;span class="si"&gt;}&lt;/span&gt;

  export default Color
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://svelte.dev/docs#Attributes_and_props" rel="noopener noreferrer"&gt;Svelte &lt;code&gt;props&lt;/code&gt;&lt;/a&gt; work similar to React &lt;code&gt;props&lt;/code&gt;. They have nearly the same syntax, with quotation marks for strings and curly brackets for other values, but can also accept any keywords that work in regular HTML.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight svelte"&gt;&lt;code&gt;    // row.svelte

    &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
      &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt;

      &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Color&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;./color.svelte&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;Color&lt;/span&gt; 
        &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;score=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;ratio=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ratio&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unlike React, Svelte comes built-in with an easy-to-opt-into typing system. When defining a &lt;code&gt;prop&lt;/code&gt; in a child component using the &lt;code&gt;export let&lt;/code&gt; syntax, &lt;a href="https://svelte.dev/docs#1_export_creates_a_component_prop" rel="noopener noreferrer"&gt;a default value can be set&lt;/a&gt;. Svelte will then throw a warning when a new value is assigned that does not match that original type. To opt out of this either don't set a default or explicitly assign it &lt;code&gt;undefined&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight svelte"&gt;&lt;code&gt;  // color.svelte

  &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;
    &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;
    &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;
    &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;ratio&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`Ratio ${ratio}`&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/span&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;Svelte can also pass an entire object as one attribute. One cool thing Svelte offers is the ability to use a shorthand to pass a prop if its name and value are the same. Instead of setting an attribute equal to a value, the value can be wrapped with curly bracket and added directly on an element.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight svelte"&gt;&lt;code&gt;    // row.svelte

    &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
      &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

      &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Color&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;./color.svelte&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;
      // long form
      &lt;span class="nt"&gt;&amp;lt;Color&lt;/span&gt; &lt;span class="na"&gt;color=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
      // shorthand
      &lt;span class="nt"&gt;&amp;lt;Color&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;color&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; /&amp;gt;
    &lt;span class="nt"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These values can then be accessed off of the object in the child component.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight svelte"&gt;&lt;code&gt;  // color.svelte
  &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt; &lt;span class="o"&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;div&lt;/span&gt; &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`Ratio ${color.ratio}`&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/span&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;It may seem like all individually defined and exported props are not automatically grouped into a single &lt;code&gt;props&lt;/code&gt; object, but they actually are. This comes with the caveat that this syntax is best used sparingly.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;$$props references all props that are passed to a component – including ones that are not declared with export. It is useful in rare cases, but not generally recommended, as it is difficult for Svelte to optimise.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The same spread operator syntax can be used to pass all props into a child.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;```svelte
// row.svelte

&amp;lt;script&amp;gt;
  export let color = {}

  import Color from './color.svelte'
&amp;lt;/script&amp;gt;

&amp;lt;tr&amp;gt;
  &amp;lt;Color {...$$props} /&amp;gt;
&amp;lt;/tr&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


  In the child component the exported `props` will need to correspond to whatever was also passed to the parent. In this example, it would be a `color` object.



  ```svelte
  // color.svelte

  &amp;lt;script&amp;gt;
    export let color = {}
  &amp;lt;/script&amp;gt;

  &amp;lt;div title={`Ratio ${color.ratio}`}&amp;gt;
    &amp;lt;span&amp;gt;
      {color.score}
    &amp;lt;/span&amp;gt;
  &amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Passing props in Vue works mostly like React and Svelte, but with a few syntactical differences. Firstly, prop attributes have to be &lt;a href="https://vuejs.org/v2/guide/components-props.html#Prop-Casing-camelCase-vs-kebab-case" rel="noopener noreferrer"&gt;"kebab-case"&lt;/a&gt;. Vue props will also always use the double quotation (&lt;code&gt;" "&lt;/code&gt;) syntax to declare &lt;strong&gt;all&lt;/strong&gt; types of props, not just strings. Vue also makes a distinction between &lt;a href="https://vuejs.org/v2/guide/components-props.html#Passing-Static-or-Dynamic-Props" rel="noopener noreferrer"&gt;static props and dynamic props&lt;/a&gt;, the latter of which are prepended with &lt;code&gt;v-bind:&lt;/code&gt;. Since the color chart will sometimes update when a user interacts with it, this is the syntax to use in this situation.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;  // row.vue

  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Color&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;./color.vue&lt;/span&gt;&lt;span class="dl"&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="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;Row&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;Color&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;color&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;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;Color&lt;/span&gt; 
        &lt;span class="na"&gt;v-bind:name=&lt;/span&gt;&lt;span class="s"&gt;"this.color.name"&lt;/span&gt;
        &lt;span class="na"&gt;v-bind:value=&lt;/span&gt;&lt;span class="s"&gt;"this.color.value"&lt;/span&gt;
        &lt;span class="na"&gt;v-bind:score=&lt;/span&gt;&lt;span class="s"&gt;"this.color.score"&lt;/span&gt;
        &lt;span class="na"&gt;v-bind:ratio=&lt;/span&gt;&lt;span class="s"&gt;"this.color.ratio"&lt;/span&gt;
      &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&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;Vue, like Svelte, comes with a &lt;a href="https://vuejs.org/v2/guide/components-props.html#Prop-Validation" rel="noopener noreferrer"&gt;typing system&lt;/a&gt; that only requires a small amount of additional syntax. When registering &lt;code&gt;props&lt;/code&gt; to a component, each prop can be assigned a value to define a type associated with each key.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;  // color.vue

  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;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="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;Color&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;props&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="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;score&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;ratio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Number&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;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;"`Ratio $&lt;/span&gt;{this.ratio}`"&amp;gt;
        &lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;
          &lt;span class="si"&gt;{{&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="si"&gt;}}&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&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;Just like the other frameworks, objects can be passed down as props as well. Like Svelte, Vue offers a &lt;a href="https://vuejs.org/v2/guide/syntax.html#v-bind-Shorthand" rel="noopener noreferrer"&gt;shorthand&lt;/a&gt; for writing out &lt;code&gt;v-bind:&lt;/code&gt;. &lt;code&gt;v-bind&lt;/code&gt; can be left off and dynamic props can be prepended with just &lt;code&gt;:&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;  // row.vue
  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Color&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;./color.vue&lt;/span&gt;&lt;span class="dl"&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="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;Row&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;Color&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Object&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;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;
      // long form
      &lt;span class="nt"&gt;&amp;lt;Color&lt;/span&gt; &lt;span class="na"&gt;v-bind:color=&lt;/span&gt;&lt;span class="s"&gt;"this.color"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
      // shorthand
      &lt;span class="nt"&gt;&amp;lt;Color&lt;/span&gt; &lt;span class="na"&gt;:color=&lt;/span&gt;&lt;span class="s"&gt;"this.color"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&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;As you would probably expect, those values can be accessed via the &lt;code&gt;color&lt;/code&gt; object prop.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;  // color.vue

  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;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="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;Color&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Object&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;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;"`Ratio $&lt;/span&gt;{this.color.ratio}`"&amp;gt;
        &lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;
          &lt;span class="si"&gt;{{&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="si"&gt;}}&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&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;Similar to Svelte, Vue also secretly bundles all of a components registered prop values into an object called &lt;a href="https://vuejs.org/v2/api/#vm-props" rel="noopener noreferrer"&gt;&lt;code&gt;$props&lt;/code&gt;&lt;/a&gt;, which can be referenced elsewhere in the component as &lt;code&gt;this.$props&lt;/code&gt;. Unlike Svelte and React, however, there isn't really a way to spread this value into a child component and it is made more for referencing passed in values elsewhere in the &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag.&lt;/p&gt;

&lt;h2&gt;
  
  
  Inline Javascript and Styles
&lt;/h2&gt;

&lt;p&gt;Sometimes, in order to make components more dynamic, values need to be set locally to each instance of a component. This is often useful to do within the render body of the component if the output will end up directly in the DOM. These DOM side effects often involve manipulating classes for CSS, or even adding inline CSS directly onto elements.&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%2Fwww.ryanfiller.com%2Fimages%2Fuploads%2Fcolor-row.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%2Fwww.ryanfiller.com%2Fimages%2Fuploads%2Fcolor-row.png" alt="block components with different color text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the color chart, the easiest way to style each block was to add an inline &lt;code&gt;background-color&lt;/code&gt; to each &lt;code&gt;&amp;lt;Color&amp;gt;&lt;/code&gt; component. Also, each block runs a small JavaScript function to determine whether to use black or white text.&lt;/p&gt;

&lt;p&gt;Using a conditional CSS class is how I was able to scope the global style sheet for the entire chart. A user can turn styles off by not passing the &lt;code&gt;useStyles&lt;/code&gt; top level prop.&lt;/p&gt;

&lt;p&gt;I'll be using &lt;a href="http://getbem.com/" rel="noopener noreferrer"&gt;Block Element Modifier&lt;/a&gt; style classes for this section, so if these look odd read up on what all the dashes and underscores mean.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;React does its best to be unopinionated when it comes to styles. JSX uses the &lt;a href="https://reactjs.org/docs/faq-styling.html#how-do-i-add-css-classes-to-components" rel="noopener noreferrer"&gt;&lt;code&gt;className&lt;/code&gt; attribute&lt;/a&gt; as an analog to the regular HTML &lt;code&gt;class&lt;/code&gt;. Any string, or expression that evaluates to a string, can be used and will end up in the DOM and can be hooked up to an external style sheet.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;  &lt;span class="c1"&gt;// color.jsx&lt;/span&gt;

  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&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;react&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;Color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;td&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;'color-contrast-table__color'&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="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'color-contrast-table__color-block'&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;span&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;'color-contrast-table__color-score'&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&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;/&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&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="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;Color&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;React also allows for inline styles, but the syntax is &lt;a href="https://reactjs.org/docs/dom-elements.html#style" rel="noopener noreferrer"&gt;different&lt;/a&gt; than regular HTML. The &lt;code&gt;style&lt;/code&gt; attribute accepts an object, and as such the keys need to be camelCase strings rathan than normal CSS properties that contain hyphens. JavaScript values can be used directly in this object.&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;// color.jsx&lt;/span&gt;

  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&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;react&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;Color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;td&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="na"&gt;style&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="na"&gt;backgroundColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&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="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;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&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;/&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&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="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;Color&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The last built-in way to work with styles in React is similar to inline styles, but is useful for styles that don't need to be attached to a specific DOM element. React's (hilariously named) &lt;a href="https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml" rel="noopener noreferrer"&gt;&lt;code&gt;dangerouslySetInnerHTML&lt;/code&gt;&lt;/a&gt; attribute can be combined with a &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; tag to create an on-page style sheet.&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;// styles.jsx&lt;/span&gt;

  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&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;react&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;Styles&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="k"&gt;return &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;style&lt;/span&gt; &lt;span class="na"&gt;dangerouslySetInnerHTML&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="na"&gt;__html&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
        .color-contrast-table
          ...
        }
      `&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;)&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="nx"&gt;Styles&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since JSX is "just javascript," running inline method doesn't require any special work. A function can be imported from another file, and then used anywhere in the body of the component or its returned JSX.&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;// color.jsx&lt;/span&gt;

  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&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;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;getLabelColor&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;./get-label-color.js&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;Color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;td&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="na"&gt;style&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="na"&gt;backgroundColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&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="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;getLabelColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&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="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;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&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;/&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&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="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;Color&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;More complex functions can even be defined as nested functions within a component before the return of the component's JSX. To hook up the &lt;code&gt;useStyle&lt;/code&gt; prop I used this strategy to conditionally add a top level &lt;code&gt;className&lt;/code&gt; and then scoped my &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; tag accordingly.&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;// table.jsx&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&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;function&lt;/span&gt; &lt;span class="nf"&gt;getClassName&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;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;useStyles&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;use-styles color-contrast-table&lt;/span&gt;&lt;span class="dl"&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="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;color-contrast-table&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;return &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;table&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="nf"&gt;getClassName&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;tbody&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;tbody&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;table&lt;/span&gt;&lt;span class="p"&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="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;Table&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Svelte, striving to be as close to HTML as possible, lets you use regular HTML class attribute syntax.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight svelte"&gt;&lt;code&gt;  // color.svelte
  &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;td&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;'color-contrast-table__color'&lt;/span&gt;&lt;span class="nt"&gt;&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;'color-contrast-table__color-block'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;'color-contrast-table__color-score'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since "valid HTML is valid Svelte," a regular &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/style" rel="noopener noreferrer"&gt;&lt;code&gt;style&lt;/code&gt; attribute&lt;/a&gt; can be used to create inline styles. This isn't very well documented, but Svelte can also interpolate JavaScript values within this attribute with the curly bracket syntax.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight svelte"&gt;&lt;code&gt;  // color.svelte

  &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;'background-color: {color.value};'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;
        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Svelte also comes with some pretty powerful built-in style tools. In addition to being able to define a &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag at the top of a &lt;code&gt;.svelte&lt;/code&gt; file, you can define a &lt;a href="https://svelte.dev/docs#style" rel="noopener noreferrer"&gt;&lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; tag&lt;/a&gt; as well. The Svelte compiler will generate unique classes that will encapsulate styles to only effect elements within this component file. This means that styles won't naturally cascade down to child components, unless the style rule is wrapped with the &lt;code&gt;:global()&lt;/code&gt; modifier.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight svelte"&gt;&lt;code&gt;  // styles.svelte

  &lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
    &lt;span class="nd"&gt;:global&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;.color-contrast-table&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="err"&gt;...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because the &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; and HTML sections of a component are optional, this creates a global &lt;code&gt;&amp;lt;Style /&amp;gt;&lt;/code&gt; component I can include near the top level of my color chart.&lt;/p&gt;

&lt;p&gt;Since the &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; section of a component will run any valid JavaScript, component functions can be imported and run here. Variables can also be created and used in the HTML body with the same bracket syntax as &lt;code&gt;props&lt;/code&gt; values.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight svelte"&gt;&lt;code&gt;  // color.svelte

  &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt;

    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;getLabelColor&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;./get-label-color.js&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;textColor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getLabelColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;color&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="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;'
      background-color: {color.value};
      color: {textColor};
    '&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;
        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just like React, methods can be called inline using curly brackets. I used the same scoping method combined with a &lt;code&gt;:global()&lt;/code&gt; modifier to toggle the styles for the chart.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight svelte"&gt;&lt;code&gt;  // table.svelte

  &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;colors&lt;/span&gt;
    &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;useStyles&lt;/span&gt;

    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getClassName&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;useStyles&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;use-styles color-contrast-table&lt;/span&gt;&lt;span class="dl"&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="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;color-contrast-table&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="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;table&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;useStyles&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;tbody&amp;gt;&lt;/span&gt;
      ...
    &lt;span class="nt"&gt;&amp;lt;/tbody&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/table&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One extra thing to note here when applying this logic specifically to classes is that Svelte also comes with a &lt;a href="https://svelte.dev/docs#class_name" rel="noopener noreferrer"&gt;&lt;code&gt;class:&lt;/code&gt; directive&lt;/a&gt; that makes conditionally rendering classes easier. Whatever directly follows the &lt;code&gt;:&lt;/code&gt; will be added to the element's class list if the prop is truthy.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight svelte"&gt;&lt;code&gt;  // table.svelte

  &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;colors&lt;/span&gt;
    &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;useStyles&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;table&lt;/span&gt;
    &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;'color-contrast-table'&lt;/span&gt;
    &lt;span class="na"&gt;class:use-styles=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;useStyles&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;tbody&amp;gt;&lt;/span&gt;
      ...
    &lt;span class="nt"&gt;&amp;lt;/tbody&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/table&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Vue, just like Svelte, uses the same class attribute syntax as regular HTML.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;  // color.vue

  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;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="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;Color&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;color&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;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;td&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;'color-contrast-table__color'&lt;/span&gt;&lt;span class="nt"&gt;&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;'color-contrast-table__color-block'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;'color-contrast-table__color-score'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="si"&gt;{{&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="si"&gt;}}&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&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;Vue's &lt;a href="https://vuejs.org/v2/guide/class-and-style.html#Binding-Inline-Styles" rel="noopener noreferrer"&gt;inline style attribute&lt;/a&gt;, however, is a little closer to React's. Vue uses the &lt;code&gt;v-bind:style&lt;/code&gt; syntax and needs to be passed an object. It can use regular, hyphenated CSS properties as keys as long as they're surrounded with single quotation marks (&lt;code&gt;' '&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Any attributes that need access to interpolated values needs to use a Vue &lt;a href="https://vuejs.org/v2/guide/custom-directive.html" rel="noopener noreferrer"&gt;directives&lt;/a&gt;. A directive is a special kdinf of attribute, usually prefixed with "v-" that lets Vue know it needs to interpolate that attribute in a special way.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;  // color.vue

  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;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="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;Color&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;color&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;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;v-bind:style=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;{ 'background-color': color.value }"&amp;gt;
        &lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;
          &lt;span class="si"&gt;{{&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="si"&gt;}}&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&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;One awesome benefit of using a Vue &lt;a href="https://vuejs.org/v2/guide/single-file-components.html" rel="noopener noreferrer"&gt;single file component&lt;/a&gt; is the ability to add a &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; tag to a component in addition to a &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag. This works a lot like the Svelte &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; tag, but is &lt;a href="https://vue-loader.vuejs.org/guide/scoped-css.html" rel="noopener noreferrer"&gt;more configurable&lt;/a&gt;. By default the styles will work like a global CSS stylesheet, but the tag itself can accept a &lt;code&gt;scoped&lt;/code&gt; attribute. This will add generated classes and encapsulate the styles similar to Svelte.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;  // styles.vue

  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;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="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;Styles&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="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;style&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nc"&gt;.color-contrast-table&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="err"&gt;...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;style&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&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;One thing worth mentioning here is that since a Vue component must contain a &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; tag and that tag must contain an element, you will have to put some random DOM element here. If that bothers you (it bothered me), there is an NPM package called &lt;a href="https://www.npmjs.com/package/vue-fragment" rel="noopener noreferrer"&gt;&lt;code&gt;vue-fragment&lt;/code&gt;&lt;/a&gt; that will allow you to return a ghost element that won't actually render anything.&lt;/p&gt;

&lt;p&gt;When it comes to using methods inside of components, Vue requires a bit more structure than the other two frameworks. &lt;/p&gt;

&lt;p&gt;To define a value in the &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; section and use that value in the component body, it must &lt;a href="https://vuejs.org/v2/guide/instance.html#Data-and-Methods" rel="noopener noreferrer"&gt;be registered&lt;/a&gt; like other imports. In a single file component, &lt;code&gt;data&lt;/code&gt; needs to be a &lt;a href="https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function" rel="noopener noreferrer"&gt;function that returns an object of data values&lt;/a&gt;. If that &lt;code&gt;data&lt;/code&gt; value is going to be defined as the result of an expression, that function needs to also be registered to the &lt;code&gt;methods&lt;/code&gt; key of the Vue object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;  // color.vue

  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;getLabelColor&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;./get-label-color.js&lt;/span&gt;&lt;span class="dl"&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="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;Color&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;color&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;getLabelColor&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;data&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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;textColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getLabelColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&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="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;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt;
        &lt;span class="na"&gt;v-bind:style=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;{ 
          'background-color': color.value,
          'color': this.textColor
        }"
      &amp;gt;
        &lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;
          &lt;span class="si"&gt;{{&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="si"&gt;}}&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&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;Methods can also be defined as values inside the &lt;code&gt;methods&lt;/code&gt; object, and can be run directly within the &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; section.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;  // table.vue

  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;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="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;Table&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;useStyles&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;getClassName&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;useStyles&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;use-styles color-contrast-table&lt;/span&gt;&lt;span class="dl"&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="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;color-contrast-table&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;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;table&lt;/span&gt; &lt;span class="na"&gt;v-bind:class=&lt;/span&gt;&lt;span class="s"&gt;"getClassName()"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;tbody&amp;gt;&lt;/span&gt;
        ...
      &lt;span class="nt"&gt;&amp;lt;/tbody&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/table&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&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;For methods that determine which classes are added to an element, Vue allows for &lt;a href="https://vuejs.org/v2/guide/class-and-style.html#Binding-HTML-Classes" rel="noopener noreferrer"&gt;binding a directive to the &lt;code&gt;class&lt;/code&gt; attribute&lt;/a&gt;. You can pass an object to a bound class attribute, and if the value returns truthy the key will be added to the element's class list.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;  // table.vue

  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;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="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;Table&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;useStyles&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;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;table&lt;/span&gt; &lt;span class="na"&gt;v-bind:class=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;{ 'use-styles': useStyles }"&amp;gt;
      &lt;span class="nt"&gt;&amp;lt;tbody&amp;gt;&lt;/span&gt;
        ...
      &lt;span class="nt"&gt;&amp;lt;/tbody&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/table&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Loops and Conditional Rendering
&lt;/h2&gt;

&lt;p&gt;Moving up one level from the &lt;code&gt;&amp;lt;Color&amp;gt;&lt;/code&gt; component in the color charts is the Row component. Each row contains two things: a collection of generated &lt;code&gt;&amp;lt;Color&amp;gt;&lt;/code&gt; components, and a &lt;code&gt;&amp;lt;Header&amp;gt;&lt;/code&gt; element that needs to know whether to show regular text or editable inputs.&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%2Fwww.ryanfiller.com%2Fimages%2Fuploads%2Fcolor-row-2.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%2Fwww.ryanfiller.com%2Fimages%2Fuploads%2Fcolor-row-2.png" alt="a row of blocks generated from an array"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each Row will get &lt;code&gt;props&lt;/code&gt; data that looks 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="nl"&gt;color&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;purple&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// the name of this color&lt;/span&gt;
      &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#800080&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// the value of this color&lt;/span&gt;
      &lt;span class="na"&gt;combinationScores&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[...],&lt;/span&gt; &lt;span class="c1"&gt;// a list of data about the other colors&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nx"&gt;editNames&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="c1"&gt;// whether the name should show text or an input&lt;/span&gt;
    &lt;span class="nx"&gt;editValues&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="c1"&gt;// whether the value should show text or an input&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The biggest difference between JSX and any other templating system I've used is highlighted in the first paragraph of the JSX documentation.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;JSX may remind you of a template language, but it comes with the full power of JavaScript.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This means that rather than learning JSX specific ways to programmatically create markup, React relies on directly using JavaScript methods. To render a list of items, an array of data can be looped through with a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map" rel="noopener noreferrer"&gt;&lt;code&gt;.map&lt;/code&gt;&lt;/a&gt; or a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach" rel="noopener noreferrer"&gt;&lt;code&gt;.forEach&lt;/code&gt;&lt;/a&gt; &lt;code&gt;Array&lt;/code&gt; method.&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;// row.jsx&lt;/span&gt;

  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&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;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Color&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;./color.jsx&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Header&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;./header.jsx&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;Row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;tr&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="nc"&gt;Header&lt;/span&gt; &lt;span class="na"&gt;editNames&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;editNames&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;editValues&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;editValues&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;

        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;combinationScores&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;color&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;=&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Color&lt;/span&gt;
              &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
              &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;color&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;)&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;lt;/&lt;/span&gt;&lt;span class="nt"&gt;tr&lt;/span&gt;&lt;span class="p"&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="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;Row&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One particular quirk to note here is that when rendering elements any list of elements React requires each to have a unqiue &lt;code&gt;key&lt;/code&gt; attribute. This can be anything — like the elements &lt;code&gt;name&lt;/code&gt; value — but its often easiest to just use the array index. This helps React keep track of items during re-renders.&lt;/p&gt;

&lt;p&gt;Another gotcha of JSX is that despite being "just JavaScript," &lt;code&gt;if ()&lt;/code&gt; statements don't work inside the return of a render method. Because of this JSX code is more likely to use either the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_AND" rel="noopener noreferrer"&gt;"short circuit"&lt;/a&gt; or &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_Operator" rel="noopener noreferrer"&gt;ternary&lt;/a&gt; operators.&lt;/p&gt;

&lt;p&gt;The "short circuit" is more of a design pattern and less of an actual operator, but it can be read as "if the data exists, show the HTML; if not, stop and show nothing."&lt;/p&gt;

&lt;p&gt;If the row component needed to &lt;em&gt;only&lt;/em&gt; show &lt;code&gt;&amp;lt;Color&amp;gt;&lt;/code&gt; components for passing color combinations, it might look like 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="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fail&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Color&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;color&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;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The ternary operator is used when one element is needed in one scenario and another in a different scenario, such as in the Header of each row.&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;// header.jsx&lt;/span&gt;

  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&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;react&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;Header&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;th&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;editNames&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;input&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;'text'&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&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;:&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;editValues&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;input&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;'color'&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&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;:&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;th&lt;/span&gt;&lt;span class="p"&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="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;Header&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A ternary, especially when intermixed with markup, can look confusing at first, but it functions essentially the same as an &lt;code&gt;if...else&lt;/code&gt; statement. If the expression before the &lt;code&gt;?&lt;/code&gt; is truthy, the first condition before the &lt;code&gt;:&lt;/code&gt; will execute. If not, default to the second condition after the &lt;code&gt;:&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Svelte comes with its own &lt;a href="https://svelte.dev/docs#Template_syntax" rel="noopener noreferrer"&gt;built in template syntax&lt;/a&gt;. To run logic you'll need to hook into their specific syntax, using &lt;code&gt;#&lt;/code&gt; to start an expression, &lt;code&gt;:&lt;/code&gt; to continue the previous expression, and &lt;code&gt;/&lt;/code&gt; to end.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To render a list of elements from an &lt;code&gt;array&lt;/code&gt; of data, Svelte has several way to run an &lt;a href="https://svelte.dev/docs#each" rel="noopener noreferrer"&gt;each loop&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight svelte"&gt;&lt;code&gt;    // row.svelte

    &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
      &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt;
      &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;editNames&lt;/span&gt;
      &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;editValues&lt;/span&gt;

      &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Color&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;./color.svelte&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
      &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Header&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;./header.svelte&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;Header&lt;/span&gt; &lt;span class="na"&gt;editNames=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;editNames&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;editValues=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;editValues&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="k"&gt;#each&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;combinationScores&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;Color&lt;/span&gt; &lt;span class="na"&gt;color=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="k"&gt;/each&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A nice thing about Svelte templates over JSX is that they are a little smarter, so there's no need to manually add a key to elements (although you can if you ever need to).&lt;/p&gt;

&lt;p&gt;Svelte also provides blocks for &lt;a href="https://svelte.dev/docs#if" rel="noopener noreferrer"&gt;&lt;code&gt;if&lt;/code&gt; type statements&lt;/a&gt; so there's no need to use something like the short circuit or the ternary.&lt;/p&gt;

&lt;p&gt;An &lt;code&gt;if&lt;/code&gt; block wraps some HTML that will only be returned if the condition returns true. To only show blocks with a passing score:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight svelte"&gt;&lt;code&gt;  &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="k"&gt;#if&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fail&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Color&lt;/span&gt; &lt;span class="na"&gt;color=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="k"&gt;/if&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An &lt;code&gt;if&lt;/code&gt; statement can also have an &lt;code&gt;:else&lt;/code&gt; clause, providing a default that will be rendered if the conditional is evaluated as false.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight svelte"&gt;&lt;code&gt;  // header.svelte

  &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;editNames&lt;/span&gt;
    &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;editValues&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;th&amp;gt;&lt;/span&gt;
    &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="k"&gt;#if&lt;/span&gt; &lt;span class="nx"&gt;editNames&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;'text'&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
    &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="k"&gt;/if&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;

    &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="k"&gt;#if&lt;/span&gt; &lt;span class="nx"&gt;editValues&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;'color'&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
    &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="k"&gt;/if&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Vue templates also come with a built in logic system, but rather than using custom tags it uses &lt;a href="https://vuejs.org/v2/guide/syntax.html#Directives" rel="noopener noreferrer"&gt;directives&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Vue's template directive attributes are interesting because they can be applied directly onto a tag, or can be applied to a wrapping &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; element that will function as a "rootless" element during render.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;  // row.vue

  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Header&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;./header.vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Color&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;./color.vue&lt;/span&gt;&lt;span class="dl"&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="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;Row&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;Color&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;color&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;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;Header&lt;/span&gt; &lt;span class="na"&gt;:editNames=&lt;/span&gt;&lt;span class="s"&gt;"editNames"&lt;/span&gt; &lt;span class="na"&gt;:editValues=&lt;/span&gt;&lt;span class="s"&gt;"editValues"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

      // directive directly on element
      &lt;span class="nt"&gt;&amp;lt;Color&lt;/span&gt;
        &lt;span class="na"&gt;v-for=&lt;/span&gt;&lt;span class="s"&gt;"(color, index) in color.combinationScores"&lt;/span&gt;
        &lt;span class="na"&gt;:key=&lt;/span&gt;&lt;span class="s"&gt;"index"&lt;/span&gt;
        &lt;span class="na"&gt;:color=&lt;/span&gt;&lt;span class="s"&gt;"color"&lt;/span&gt;
      &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

      // with wrapping element
      &lt;span class="nt"&gt;&amp;lt;template&lt;/span&gt; &lt;span class="na"&gt;v-for=&lt;/span&gt;&lt;span class="s"&gt;"(color, index) in color.combinationScores"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;Color&lt;/span&gt;
          &lt;span class="na"&gt;:key=&lt;/span&gt;&lt;span class="s"&gt;"index"&lt;/span&gt;
          &lt;span class="na"&gt;:color=&lt;/span&gt;&lt;span class="s"&gt;"color"&lt;/span&gt;
        &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Vue also has both &lt;code&gt;v-if&lt;/code&gt; and &lt;code&gt;v-else&lt;/code&gt; directives that work how you would expect. Just like with &lt;code&gt;v-for&lt;/code&gt; these can be applied on a wrapping &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; or directly to an element.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  // header.vue 
  &amp;lt;script&amp;gt;
    export default {
      name: 'Header',
      props: {
        editNames,
        editValues
      },
    }
  &amp;lt;/script&amp;gt;

  &amp;lt;template&amp;gt;
    // directive directly on element
    &amp;lt;th&amp;gt;
      &amp;lt;input v-if="editNames" type='text' v-bind:value="color.name" /&amp;gt;
      &amp;lt;span v-else&amp;gt;{{ color.name }}&amp;lt;/span&amp;gt;

      &amp;lt;input v-if="editValues" type='color' v-bind:value="color.value" /&amp;gt;
      &amp;lt;span v-else&amp;gt;{{ color.value }}&amp;lt;/span&amp;gt;
    &amp;lt;/th&amp;gt;

    // with wrapping element
    &amp;lt;th&amp;gt;
      &amp;lt;template v-if="editNames"&amp;gt;
        &amp;lt;input type='text' v-bind:value="color.name" /&amp;gt;
      &amp;lt;/template&amp;gt;
      &amp;lt;template v-else&amp;gt;
        &amp;lt;span &amp;gt;{{ color.name }}&amp;lt;/span&amp;gt;
      &amp;lt;/template&amp;gt;

      &amp;lt;template v-if="editValues"&amp;gt;
        &amp;lt;input type='color' v-bind:value="color.value" /&amp;gt;
      &amp;lt;/template&amp;gt;
      &amp;lt;template v-else&amp;gt;
        &amp;lt;span &amp;gt;{{ color.value }}&amp;lt;/span&amp;gt;
      &amp;lt;/template&amp;gt;
    &amp;lt;/th&amp;gt;
  &amp;lt;/template&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Vue also comes with one extra operator that the other two frameworks don't include — &lt;a href="https://vuejs.org/v2/guide/conditional.html#v-show" rel="noopener noreferrer"&gt;&lt;code&gt;v-show&lt;/code&gt;&lt;/a&gt;. &lt;code&gt;v-show&lt;/code&gt; works visually just like &lt;code&gt;v-if&lt;/code&gt;, but instead of not rendering an element it still renders the markup, but uses CSS to hide it from the DOM.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;  // directive directly on element
  &lt;span class="nt"&gt;&amp;lt;Color&lt;/span&gt; &lt;span class="na"&gt;v-show=&lt;/span&gt;&lt;span class="s"&gt;"color.score !== 'fail'"&lt;/span&gt; &lt;span class="na"&gt;color=&lt;/span&gt;&lt;span class="s"&gt;{color}&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

  // with wrapping element
  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt; &lt;span class="na"&gt;color.score&lt;/span&gt; &lt;span class="err"&gt;!==&lt;/span&gt; &lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="na"&gt;fail&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Color&lt;/span&gt; &lt;span class="na"&gt;color=&lt;/span&gt;{color} /&amp;gt;
  &lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&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;This could be accomplished in React or Svelte as well, but it's nice of Vue to provide such an easy syntax for it.&lt;/p&gt;

&lt;h2&gt;
  
  
  State and Events
&lt;/h2&gt;

&lt;p&gt;If props are one half of modern frameworks, state is the other. Props are data passed into components, state represents values internal to the component at render time. State is often updated in response to actions that a user takes. State can even be derived from an initial set of &lt;code&gt;props&lt;/code&gt; passed into a component as a starting state.&lt;/p&gt;

&lt;p&gt;Each component can hold it's own state, and pass it down into child components via props, but often the top level component for a system will maintain all the state and disperse it down the tree. All three frameworks contain mechanisms to re-render a component if its &lt;code&gt;props&lt;/code&gt; or &lt;code&gt;state&lt;/code&gt; change. If a user takes an action, the top level component will be notified to update its state, and then let the children know they needs to re-render with new data.&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%2Fwww.ryanfiller.com%2Fimages%2Fuploads%2Fedit-color.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%2Fwww.ryanfiller.com%2Fimages%2Fuploads%2Fedit-color.png" alt="editting a color value"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Custom events tied to different inputs and user actions are used to update state.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;React has a very complicated state system for class components that involves &lt;a href="https://reactjs.org/docs/state-and-lifecycle.html#adding-local-state-to-a-class" rel="noopener noreferrer"&gt;binding values and updater functions to a component's &lt;code&gt;this&lt;/code&gt;&lt;/a&gt;. Since I've only been talking about function components it makes more sense to look at the &lt;a href="https://reactjs.org/docs/hooks-state.html" rel="noopener noreferrer"&gt;&lt;code&gt;useState&lt;/code&gt; hook&lt;/a&gt; and not the &lt;code&gt;setState&lt;/code&gt; function. Hooks can be a confusing concept at first, especially if you're not already familiar with all of the &lt;a href="https://reactjs.org/docs/state-and-lifecycle.html#adding-lifecycle-methods-to-a-class" rel="noopener noreferrer"&gt;class lifecycle methods&lt;/a&gt;, but try to think of them as importable functions that add ability for components to do more complex actions than just render JSX.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Any of the &lt;a href="https://reactjs.org/docs/hooks-overview.html" rel="noopener noreferrer"&gt;built-in React hooks&lt;/a&gt; can be imported as a named import from the &lt;code&gt;'react'&lt;/code&gt; package, or they can be accessed directly off the &lt;code&gt;React&lt;/code&gt; default import, like &lt;code&gt;React.useState()&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;The &lt;code&gt;useState&lt;/code&gt; hook is a function that can accept an argument to use as its initial state. It will return two values: the state value and a function to update that state value. These two values are usually written with the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment" rel="noopener noreferrer"&gt;array destructure assignment&lt;/a&gt; syntax, and will mostly follow the &lt;code&gt;valueName&lt;/code&gt;, &lt;code&gt;setValueName&lt;/code&gt; naming convention.&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;// chart.js&lt;/span&gt;

  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&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="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;generateChart&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;color-contrast-table&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;Chart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&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;colors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setColors&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="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;colors&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;colorChart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generateChart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;colors&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;&lt;/span&gt;&lt;span class="nc"&gt;Table&lt;/span&gt; &lt;span class="na"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;colorChart&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;)&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="nx"&gt;Chart&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Calling &lt;code&gt;setColor(newColors)&lt;/code&gt; would cause the value of &lt;code&gt;colors&lt;/code&gt; to change and cause a re-render of the &lt;code&gt;&amp;lt;Chart&amp;gt;&lt;/code&gt; component as well as any children that depend on the &lt;code&gt;colors&lt;/code&gt; prop. The &lt;code&gt;setColors&lt;/code&gt; function can even be passed down as a prop itself and called within a child component. State setting functions in class components would need to have their &lt;code&gt;this&lt;/code&gt; value bound in the class constructor, but because arrow function components &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions#No_separate_this" rel="noopener noreferrer"&gt;don't reassign &lt;code&gt;this&lt;/code&gt;&lt;/a&gt; a state updater can be passed and called wherever without worrying about binding.&lt;/p&gt;

&lt;p&gt;Because this function needs to be called on the change event of an input, which will only return one value, the function needs to do a little more work to get only that new value into the existing &lt;code&gt;colors&lt;/code&gt; object. Once that is worked out, the new &lt;code&gt;colors&lt;/code&gt; object can be set using the &lt;code&gt;setColor&lt;/code&gt; updater.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;```react
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;// chart.js&lt;/p&gt;

&lt;p&gt;import React, { useState } from 'react'&lt;br&gt;
  import generateChart from 'color-contrast-table'&lt;/p&gt;

&lt;p&gt;const Chart = (props) =&amp;gt; {&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const [colors, setColors] = useState(props.colors)

const onNamesChange = (index, value) =&amp;gt; {
  const newColors = [...colors]
  newColors[index].name = value
  setColors(newColors)
}

const onValuesChange = (index, value) =&amp;gt; {
  const newColors = [...colors]
  newColors[index].value = value
  setColors(newColors)
}

const colorChart = generateChart(colors)

return (
  &amp;lt;Table
    colors={colorChart}
    onNamesChange={onNamesChange}
    onValuesChange={onValuesChange}
  /&amp;gt;
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;}&lt;/p&gt;

&lt;p&gt;export default Chart&lt;/p&gt;

&lt;p&gt;// header.jsx&lt;/p&gt;

&lt;p&gt;import React from 'react'&lt;/p&gt;

&lt;p&gt;const Header = (props) =&amp;gt; {&lt;br&gt;
    return (&lt;br&gt;
      &lt;/p&gt;
&lt;th&gt;
&lt;br&gt;
        
          type='text'&lt;br&gt;
          value={props.color.name}&lt;br&gt;
          onChange={event =&amp;gt; props.onNamesChange(props.index, event.target.value)}&lt;br&gt;
        /&amp;gt;&lt;br&gt;
        
          type='color'&lt;br&gt;
          value={props.color.value}&lt;br&gt;
          onChange={event =&amp;gt; props.onValuesChange(props.index, event.target.value)}&lt;br&gt;
        /&amp;gt;&lt;br&gt;
      &lt;/th&gt;
&lt;br&gt;
    )&lt;br&gt;
  }

&lt;p&gt;export default Header&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


  The most important concept when dealing with state in React is that state objects are [immutable](https://developer.mozilla.org/en-US/docs/Glossary/Immutable) and should always be set using one of React's updaters and never reassigned directly.



  ```react
  // don't do this!

  const [colors] = useState(props.colors)

  const onNamesChange = (index, value) =&amp;gt; {
    colors[index].name = value
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Svelte is very interesting when it comes to stateful logic. One of the main tenets that differentiates Svelte from other frontend systems is that &lt;a href="https://svelte.dev/docs#2_Assignments_are_reactive" rel="noopener noreferrer"&gt;all assignments are reactive&lt;/a&gt;. Svelte "hijacks" the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment" rel="noopener noreferrer"&gt;assignment operator (=)&lt;/a&gt; and uses it to trigger state changes and re-renders within Svelte apps.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Svelte also comes with a &lt;a href="https://svelte.dev/docs#3_$_marks_a_statement_as_reactive" rel="noopener noreferrer"&gt;built-in method&lt;/a&gt; to make anything reactive by prefacing it with &lt;code&gt;$:&lt;/code&gt;. This is another one of those "looks weird but is actually valid JavaScript" things from Svelte, this time taking advantage of a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/label" rel="noopener noreferrer"&gt;JavaScript &lt;code&gt;label&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;All of that to say, simply by declaring a prop, a Svelte app is already stateful. State, whether just a reactive prop or a labeled reactive statement, can be passed down to child components like any other type of data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight svelte"&gt;&lt;code&gt;  // table.svelte 

  &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;colors&lt;/span&gt;

    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;generateChart&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;color-contrast-table&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Table&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;./table.svelte&lt;/span&gt;&lt;span class="dl"&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;colors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generateChart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;colors&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;Table&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;colors&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Updating stateful values in Svlete can be handled in nearly the same way as in React, where an updater function is passed down into a child, and the return will set a new value in the parent. However, Svelte also offers something very different called a &lt;a href="https://svelte.dev/docs#svelte_store" rel="noopener noreferrer"&gt;&lt;code&gt;store&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;store&lt;/code&gt; is a place to put data outside of the normal component hierarchy. They provide methods to &lt;code&gt;subscribe&lt;/code&gt;, &lt;code&gt;update&lt;/code&gt; and &lt;code&gt;set&lt;/code&gt; their data. Svelte stores come in several varieties, but the one that makes the most sense to use here is a &lt;a href="https://svelte.dev/docs#writable" rel="noopener noreferrer"&gt;&lt;code&gt;writable&lt;/code&gt;&lt;/a&gt;, meaning that we can both read and write data to 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="c1"&gt;// stores.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;writable&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/store&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;colorArray&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;writable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, in the &lt;code&gt;&amp;lt;Chart&amp;gt;&lt;/code&gt; component, the &lt;code&gt;colorArray&lt;/code&gt; store can be imported, &lt;code&gt;set&lt;/code&gt; with &lt;code&gt;props&lt;/code&gt; data, subscribed to, and passed down.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight svelte"&gt;&lt;code&gt;  // chart.svelte

  &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;colors&lt;/span&gt;

    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;generateChart&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;color-contrast-table&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Table&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;./table.svelte&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;colorArray&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;./stores.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="nx"&gt;colorArray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;colorArray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;colorArray&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;colors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;colorArray&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;colors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generateChart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;colors&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;Table&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;colors&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This doesn't change much in the &lt;code&gt;&amp;lt;Chart&amp;gt;&lt;/code&gt; component itself, but what it does allow for is direct access to update the &lt;code&gt;colorArray&lt;/code&gt; store without having to pass functions. Instead, the &lt;code&gt;&amp;lt;Header&amp;gt;&lt;/code&gt; component can access the store directly, call its &lt;code&gt;update&lt;/code&gt; method, and the rest of the components will be made aware of this change via the &lt;code&gt;subscribe&lt;/code&gt; method in &lt;code&gt;&amp;lt;Chart&amp;gt;&lt;/code&gt;. Store methods can be composed in other functions that will sort out updating a single object property, then can be bound to input events using Svelte's &lt;a href="https://svelte.dev/docs#Element_directives" rel="noopener noreferrer"&gt;element directives&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight svelte"&gt;&lt;code&gt;  // header.svelte

  &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt;
    &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;index&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;colorArray&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;./stores.js&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;onNamesChange&lt;/span&gt; &lt;span class="o"&gt;=&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="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;colorArray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;colorArray&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;newColors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;colorArray&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="nx"&gt;newColors&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="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;newColors&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;onValuesChange&lt;/span&gt; &lt;span class="o"&gt;=&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="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;colorArray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;colorArray&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;newColors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;colorArray&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="nx"&gt;newColors&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="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;newColors&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;th&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt;
      &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;'text'&lt;/span&gt;
      &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="na"&gt;on:input=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;onNamesChange&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="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;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt;
      &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;'color'&lt;/span&gt;
      &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="na"&gt;on:input=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;onValuesChange&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="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;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Vue has two state-like concepts: &lt;a href="https://vuejs.org/v2/guide/computed.html" rel="noopener noreferrer"&gt;computed and watched properties&lt;/a&gt;. A &lt;a href="https://vuejs.org/v2/guide/computed.html#Computed-Properties" rel="noopener noreferrer"&gt;&lt;code&gt;computed&lt;/code&gt; property&lt;/a&gt; is one that is calculated based on some other data in the app and will remain cached after updating. A &lt;a href="https://vuejs.org/v2/guide/computed.html#Watchers" rel="noopener noreferrer"&gt;&lt;code&gt;watch&lt;/code&gt; property&lt;/a&gt; is like a &lt;code&gt;computed&lt;/code&gt; property that requires a more custom implementation for how the data changing should be handled. The Vue docs recommend using &lt;code&gt;computed&lt;/code&gt; over &lt;code&gt;watch&lt;/code&gt; for most scenarios that don't involve asynchronous data, so that seemed like the right option for me.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;  // chart.vue

  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;generateChart&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;color-contrast-table&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Table&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;./table.vue&lt;/span&gt;&lt;span class="dl"&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="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;Chart&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;Table&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;colors&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;generateChart&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="nf"&gt;data&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="na"&gt;colorArray&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;colors&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;colorChart&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;function &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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generateChart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;colorArray&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="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Fragment&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;Styles/&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;Table&lt;/span&gt; &lt;span class="na"&gt;v-bind:colors=&lt;/span&gt;&lt;span class="s"&gt;"colorChart"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt; 
    &lt;span class="nt"&gt;&amp;lt;/Fragment&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&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;It feels slightly extraneous to have to assign a &lt;code&gt;data.colorArray&lt;/code&gt; value and pass it through to &lt;code&gt;computed.colorChart&lt;/code&gt;, but since Vue &lt;code&gt;props&lt;/code&gt; are also immutable this is necessary. &lt;code&gt;props.colors&lt;/code&gt; can't be changed, but &lt;code&gt;data.colorArray&lt;/code&gt; can, and whenever it does &lt;code&gt;computed.colorChart&lt;/code&gt; will also change and cause a re-render.&lt;/p&gt;

&lt;p&gt;Vue uses an event-driven system to update state. Rather than having a function be passed down and bound to the change event of an input, an event is emitted by the input and then "caught" by a parent component. Custom events are defined by using the &lt;code&gt;v-on:&lt;/code&gt; syntax, and are then passed an &lt;code&gt;$emit()&lt;/code&gt; function. The first argument of &lt;code&gt;$emit()&lt;/code&gt; is the name of the event to be emitted and the rest will be passed into the function when the event is caught.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;  // header.vue

  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;getLabelColor&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;./get-label-color.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;EditName&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;./edit-name.vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;EditValue&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;./edit-value.vue&lt;/span&gt;&lt;span class="dl"&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="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;Header&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;color&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="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;getLabelColor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nf"&gt;namechange&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="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;$emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;namechange&lt;/span&gt;&lt;span class="dl"&gt;'&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="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="nf"&gt;valuechange&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="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;$emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;valuechange&lt;/span&gt;&lt;span class="dl"&gt;'&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="nx"&gt;value&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="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;th&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt;
        &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;'text'&lt;/span&gt;
        &lt;span class="na"&gt;:value=&lt;/span&gt;&lt;span class="s"&gt;"value"&lt;/span&gt;
        &lt;span class="na"&gt;v-on:input=&lt;/span&gt;&lt;span class="s"&gt;"$emit('namechange', index, $event.target.value)"&lt;/span&gt;
      &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

      &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt;
        &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;'color'&lt;/span&gt;
        &lt;span class="na"&gt;:value=&lt;/span&gt;&lt;span class="s"&gt;"value"&lt;/span&gt;
        &lt;span class="na"&gt;v-on:input=&lt;/span&gt;&lt;span class="s"&gt;"$emit('valuechange', index, $event.target.value)"&lt;/span&gt;
      &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&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;These events can be caught and passed upwards so that a deeply nested child can update a value higher than its direct parent. For less typing, the &lt;code&gt;@&lt;/code&gt; symbol can be used as a shorthand for &lt;code&gt;v-on&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;  // header.vue

  methods: {
    namechange(index, value) { this.$emit('namechange', index, value) },
    valuechange(index, value) { this.$emit('valuechange', index, value) },
  }
  ...
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; 
    &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;'text'&lt;/span&gt;
    &lt;span class="na"&gt;:value=&lt;/span&gt;&lt;span class="s"&gt;"color.name"&lt;/span&gt;
    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;input=&lt;/span&gt;&lt;span class="s"&gt;"$emit('namechange', index, $event.target.value)"&lt;/span&gt;
  &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; 
    &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;'color'&lt;/span&gt;
    &lt;span class="na"&gt;:value=&lt;/span&gt;&lt;span class="s"&gt;"color.value"&lt;/span&gt;
    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;input=&lt;/span&gt;&lt;span class="s"&gt;"$emit('valuechange', index, $event.target.value)"&lt;/span&gt;
  &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

  // other components in between

  methods: {
    namechange(index, value) { this.$emit('namechange', index, value) },
    valuechange(index, value) { this.$emit('valuechange', index, value) },
  }
  ...
  &lt;span class="nt"&gt;&amp;lt;Header&lt;/span&gt;
    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;namechange=&lt;/span&gt;&lt;span class="s"&gt;"namechange"&lt;/span&gt;
    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;valuechange=&lt;/span&gt;&lt;span class="s"&gt;"valuechange"&lt;/span&gt;
  &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

  //chart .vue

  methods: {
    namechange: function (index, value) {
      ...
    }
    valuechange: function (index, value) {
      ...
    }
    ...
    &lt;span class="nt"&gt;&amp;lt;Table&lt;/span&gt;
      &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;namechange=&lt;/span&gt;&lt;span class="s"&gt;"namechange"&lt;/span&gt;
      &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;valuechange=&lt;/span&gt;&lt;span class="s"&gt;"valuechange"&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;This might &lt;em&gt;look&lt;/em&gt; like props being passed down, but this data flow is actually starting in the nested component and working its way upward. Once this function has been passed upwards to the same component in which the original computed values live, a method with the same name as the emitted event can be run to assign new data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;  // chart.vue

  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;generateChart&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;color-contrast-table&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Table&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;./table.vue&lt;/span&gt;&lt;span class="dl"&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="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;Chart&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;Table&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;colors&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="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;generateChart&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nf"&gt;namechangefunction &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="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;colorArray&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="nx"&gt;name&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="nf"&gt;valuechangefunction &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="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;colorArray&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="nx"&gt;value&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="p"&gt;},&lt;/span&gt;
      &lt;span class="nf"&gt;data&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="na"&gt;colorArray&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;colors&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;colorChart&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;function &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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generateChart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;colorArray&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="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Fragment&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;Styles/&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;Table&lt;/span&gt;
        &lt;span class="na"&gt;:colors=&lt;/span&gt;&lt;span class="s"&gt;"colorChart"&lt;/span&gt;
        &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;namechange=&lt;/span&gt;&lt;span class="s"&gt;"namechange"&lt;/span&gt;
        &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;valuechange=&lt;/span&gt;&lt;span class="s"&gt;"valuechange"&lt;/span&gt;
      &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt; 
    &lt;span class="nt"&gt;&amp;lt;/Fragment&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&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;Like I mentioned before, we can't mutate &lt;code&gt;props&lt;/code&gt; values, so instead these functions needs to change &lt;code&gt;computed.colorArray&lt;/code&gt;. One nice side effect of this requirement is that unlike in React and Svelte where we had to spread an existing value into a new object to update one value, Vue can directly change one piece of the &lt;code&gt;computed.colorArray&lt;/code&gt; object.&lt;/p&gt;

&lt;p&gt;Front end state management is a LOT more complicated than what I briefly touched on here. Often since state deals with user input and data transformation there are a million different ways to handle this depending on how to optimize for the specific situation. &lt;/p&gt;

&lt;p&gt;React can use the &lt;a href="https://reactjs.org/docs/context.html" rel="noopener noreferrer"&gt;context API&lt;/a&gt; to circumvent passing props a lot like how Svelte uses stores. Svelte can use &lt;a href="https://svelte.dev/docs#on_component_event" rel="noopener noreferrer"&gt;custom event directives&lt;/a&gt; to emit and listen for custom events similar to how Vue works. All three frameworks can pass down a function in one way or another that can be used to update a parent's state.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance
&lt;/h2&gt;

&lt;p&gt;Just for fun, here's the final bundle size of each package running within three small example projects I put together.&lt;/p&gt;

&lt;ul&gt;
&lt;li&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%2Fwww.ryanfiller.com%2Fimages%2Fuploads%2Freact-bundle.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%2Fwww.ryanfiller.com%2Fimages%2Fuploads%2Freact-bundle.png" alt="434kb react js bundle"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&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%2Fwww.ryanfiller.com%2Fimages%2Fuploads%2Fsvelte-bundle.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%2Fwww.ryanfiller.com%2Fimages%2Fuploads%2Fsvelte-bundle.png" alt="104kb svelte js bundle"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&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%2Fwww.ryanfiller.com%2Fimages%2Fuploads%2Fvue-bundle.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%2Fwww.ryanfiller.com%2Fimages%2Fuploads%2Fvue-bundle.png" alt="267kb vue js bundle"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Quirks and Final Thoughts
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Of the these three frameworks, I've been working with React the longest and have built the most complex apps with it. My last few day jobs have involved primarily using React. That said, while I have the most knowledge about its intricacies, I probably also have the most experience with running up against its rough edges. There's a lot I like about React &lt;em&gt;now&lt;/em&gt;, but I remember JSX being a lot to get used to. It can sometimes be hard to search out help as a beginner, especially now that there's probably as much information on the web about function components and Hooks as there are about class components and Lifecycle Methods. As a beginner its not always apparent when to use which.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also, for whatever reason, at some point React's dev tools became two views — Components and Profiler. To be honest, I still don't know what the Profiler view does and much preferred the old consolidated view. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I've only built a handful of small projects with Svelte, but its been very enjoyable to use. It definitely has some syntax oddities, but I've found that they're often justifiable when explained in the right way. A lot of things make sense eventually but are used in ways that I've never &lt;em&gt;quite&lt;/em&gt; seen JavaScript written before. This is out of the scope of what I wrote about in this post, but Svelte not relying on a virtual DOM is something that greatly interests me. This makes me want to explore Svlete more in the future.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As far as the Svelte community, I'm not a fan of Discord being the official source of help. I've come across many GitHub issues through web searches, been redirected to the Discord group, then been unable to use Discord's search to find any relevant information. Asking for help in Discord is hit or miss. It really depends who else is online at the time and if they have the answer to the question you're asking. Plus, I don't always have time to wait around for an answer, and since Discord lacks threads I've definitely missed responses that have come in after I've closed the Discord app.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Vue is super interesting, and as a direct alternative to React there's a lot about it that appealed to me. At first it felt like registering props, components, methods, data, etc... in each file felt like a lot of work, but the more I worked with Vue the more I appreciated its strong guidance system. For this being my first Vue project, the compiler threw a LOT of helpful console warnings and errors that really led to an overall smooth developer experience.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That said, I found Vue's versions can be kind of confusing. I can imagine this is how a lot of people feel not really being familiar with class vs function components in React, but I got pretty mixed up several times trying to use version 3 solutions to version 2 problems, or trying to do something in a single file component that was meant for a regular Vue instance.&lt;/p&gt;

&lt;p&gt;This is just a personal opinion, but I think its super weird to use double quotes for interpolation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  // React
  &amp;lt;div attribute={value}&amp;gt;text&amp;lt;/div&amp;gt;

  // Svelte
  &amp;lt;div attribute={value}&amp;gt;text&amp;lt;/div&amp;gt;

  // Vue
  &amp;lt;div v-attribute="value"&amp;gt;text&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I have to say that most of my compile errors were around trying to use curly brackets when Vue wanted double quotes. Quotation marks pretty heavily signify a &lt;code&gt;string&lt;/code&gt; type in my mental model at this point.&lt;/p&gt;

&lt;p&gt;I hope this brief look into React, Svelte, and Vue helped someone make up their mind about which of the frameworks they'd like to get started with. All three have strengths and weaknesses, things I like and things I don't. It's hard to pick one I would definitely say is "the best," and that's probably a some-what subjective answer anyways. The best way to find out for yourself is to just start building.&lt;/p&gt;

&lt;p&gt;If any of the examples from the post need a little bit more context to make sense, check out the full mono-repo for all three versions &lt;a href="https://github.com/ryanfiller/color-contrast-table" rel="noopener noreferrer"&gt;on GitHub&lt;/a&gt;. And if I got something wrong, either in the explanation or the packages themselves, I'm definitely accepting feedback and pull requests!&lt;/p&gt;

&lt;p&gt;Good luck building!&lt;/p&gt;

</description>
      <category>react</category>
      <category>svelte</category>
      <category>vue</category>
    </item>
    <item>
      <title>Things Svelte and Sapper Can't Do (Yet)</title>
      <dc:creator>ryanfiller</dc:creator>
      <pubDate>Sun, 26 Jul 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/ryanfiller/things-svelte-and-sapper-can-t-do-yet-31hl</link>
      <guid>https://dev.to/ryanfiller/things-svelte-and-sapper-can-t-do-yet-31hl</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/blog/a-deep-dive-into-sapper"&gt;Last month&lt;/a&gt; I wrote about testing Svelte and Sapper to learn about their intricacies and limitations before trying to change my blog framework. I decided I'd explored enough to attempt a refactor and I found a few features that couldn't easily be replicated or done at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  A .svelte file cannot export multiple components
&lt;/h2&gt;

&lt;p&gt;The first problem I uncovered is a limitation within the Svelte framework. I found a workaround, but it was much, much more verbose.&lt;/p&gt;

&lt;p&gt;On my Gatsby blog I use &lt;a href="https://mdxjs.com/"&gt;MDX&lt;/a&gt; to replace markdown elements with React components. Sapper has an equivalent tool, &lt;a href="https://mdsvex.com/"&gt;MDsveX&lt;/a&gt;. The configuration for both is similar — they need to import components and create an object that maps them to HTML elements.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// react
import { H1, H2, H3, H4, H5, H6 } from "../markdown/headings/index.js"

const components = {
  h1: H1,
  h2: H2,
  h3: H3,
  h4: H4,
  h5: H5,
  h6: H6
}

&amp;lt;MDXProvider components={components}&amp;gt;...&amp;lt;/MDXProvider&amp;gt;

// svelte
&amp;lt;script context="module"&amp;gt;
  import { h1, h2, h3, h4, h5, h6 } from './headings/index.js'
  export { h1, h2, h3, h4, h5, h6 }
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My goal is to replace each heading tag with a page link component. The component is build by taking the text content of the title and transforming it into a hash link. The hash is added as an &lt;code&gt;id&lt;/code&gt; to the heading and then wrapped in an &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; tag that links back to the hash. This basically replicates the functionality of &lt;a href="https://github.com/remarkjs/remark-autolink-headings"&gt;remark's autolink headers&lt;/a&gt; plugin.&lt;/p&gt;

&lt;h3&gt;
  
  
  MDX Headings in Gatsby
&lt;/h3&gt;

&lt;p&gt;The React way to build this component pretty straightforward. Because I still need to generate 6 different headings I made a generic &lt;code&gt;&amp;lt;Heading /&amp;gt;&lt;/code&gt; component that could accept a prop telling it which &lt;code&gt;h&lt;/code&gt; tag to render. I'll explain more about the &lt;code&gt;slugify&lt;/code&gt; function further down.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const Heading = (props) =&amp;gt; {

  import { slugify } from 'helpers'

  const hashUrl = slugify(props.children)
  const Level = `h${props.h}`

  return (
    &amp;lt;Level 
      id={hashUrl}
      className='heading'
    &amp;gt;
      &amp;lt;a href={`#${hashUrl}`}&amp;gt;
        {props.children}
      &amp;lt;/a&amp;gt;
    &amp;lt;/Level&amp;gt;
  )
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, in an index file, I was able to use the &lt;code&gt;&amp;lt;Heading /&amp;gt;&lt;/code&gt; component to compose six new components, one for each of the HTML headings.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import Heading from './heading'

const H1 = props =&amp;gt; &amp;lt;Heading h="1"&amp;gt;{props.children}&amp;lt;/Heading&amp;gt;
const H2 = props =&amp;gt; &amp;lt;Heading h="2"&amp;gt;{props.children}&amp;lt;/Heading&amp;gt;
const H3 = props =&amp;gt; &amp;lt;Heading h="3"&amp;gt;{props.children}&amp;lt;/Heading&amp;gt;
const H4 = props =&amp;gt; &amp;lt;Heading h="4"&amp;gt;{props.children}&amp;lt;/Heading&amp;gt;
const H5 = props =&amp;gt; &amp;lt;Heading h="5"&amp;gt;{props.children}&amp;lt;/Heading&amp;gt;
const H6 = props =&amp;gt; &amp;lt;Heading h="6"&amp;gt;{props.children}&amp;lt;/Heading&amp;gt;

export { H1, H2, H3, H4, H5, H6 }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My directory structure looked like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;└─ headings/
  ├─ heading.js // the generic component
  └─ index.js // export of composed h1-h6
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I want to emphasize that React is creating and exporting six unique components in a single &lt;code&gt;index.js&lt;/code&gt; file.&lt;/p&gt;

&lt;h3&gt;
  
  
  MDsveX Headings in Sapper
&lt;/h3&gt;

&lt;p&gt;There are a few big differences with building this component in Svelte. Ignore the &lt;code&gt;getText&lt;/code&gt; function for now, I'll elaborate on that later.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;script&amp;gt;
  export let h
  import { slugify } from 'helpers'

  let id
  let hashLink
  const getText = node =&amp;gt; {
    id = slugify(node.text)
    hashLink = `#${id}`
  }
&amp;lt;/script&amp;gt;

{#if h === '1'}
    &amp;lt;h1 id={id}&amp;gt;&amp;lt;a use:getText href={hashLink}&amp;gt;&amp;lt;slot/&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/h1&amp;gt;
{:else if h === '2'}
    &amp;lt;h2 id={id}&amp;gt;&amp;lt;a use:getText href={hashLink}&amp;gt;&amp;lt;slot/&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/h2&amp;gt;
{:else if h === '3'}
    &amp;lt;h3 id={id}&amp;gt;&amp;lt;a use:getText href={hashLink}&amp;gt;&amp;lt;slot/&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/h3&amp;gt;
{:else if h === '4'}
    &amp;lt;h4 id={id}&amp;gt;&amp;lt;a use:getText href={hashLink}&amp;gt;&amp;lt;slot/&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/h4&amp;gt;
{:else if h === '5'}
    &amp;lt;h5 id={id}&amp;gt;&amp;lt;a use:getText href={hashLink}&amp;gt;&amp;lt;slot/&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/h5&amp;gt;
{:else if h === '6'}
    &amp;lt;h6 id={id}&amp;gt;&amp;lt;a use:getText href={hashLink}&amp;gt;&amp;lt;slot/&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/h6&amp;gt;
{/if}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first is that Svetle doesn't seem to have the ability to use strings to dynamically choose html tags. Maybe this is possible with &lt;a href="https://dev.tosvelte:component"&gt;svelte:component&lt;/a&gt;, but I could only get this to work with other custom components and not html tags. The good news is that the Svelte community is aware of this, and at the time of writing this post there is an &lt;a href="https://github.com/sveltejs/svelte/issues/2324"&gt;open proposal&lt;/a&gt; to implement a fix. For now I went with one of the easier fixes suggested in the GitHub issue and added a series of &lt;code&gt;if else&lt;/code&gt; statements.&lt;/p&gt;

&lt;p&gt;The second issue lies in trying to replicate the &lt;code&gt;index.js&lt;/code&gt; file that exports six heading components.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import h1 from './h1.svelte'
import h2 from './h2.svelte'
import h3 from './h3.svelte'
import h4 from './h4.svelte'
import h5 from './h5.svelte'
import h6 from './h6.svelte'

export { h1, h2, h3, h4, h5, h6 }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Svelte components can be imported and exported using ES6 syntax, but only one component can be created per &lt;code&gt;.svelte&lt;/code&gt; file. Rather than naming a component and exporting it manually, Svelte will use the file name to automatically determine a component's named and how it is exported.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;script&amp;gt;
  import Heading from './heading.svelte'
&amp;lt;/script&amp;gt;

&amp;lt;Heading h='1'&amp;gt;&amp;lt;slot/&amp;gt;&amp;lt;/Heading&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An index file that exports six headings is still possible, but the directory structure is a lot messier than with React.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;└─ headings/
  ├─ h1.svelte // composed component
  ├─ h2.svelte
  ├─ h3.svelte
  ├─ h4.svelte
  ├─ h5.svelte
  ├─ h6.svelte
  ├─ heading.svelte // the generic component
  └─ index.js // export of imported h1-h6
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Sapper's router doesn't work with hash links
&lt;/h2&gt;

&lt;p&gt;As with React, there is no officially supported Svelte router, only community favorites that most projects use. As far as I know Sapper doesn't use any one specific router package. Instead, it has a series of helper functions that handle internal navigation.&lt;/p&gt;

&lt;p&gt;Sapper's routing strategy, however, does currently contain a pretty big bug. Clicking on &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; tags that point to hash links will trigger a navigation to a new page rather than scroll to an id on the same page. A link on the &lt;code&gt;www.site.com/example&lt;/code&gt; page that looks like &lt;code&gt;&amp;lt;a href='#test'&amp;gt;test&amp;lt;/a&amp;gt;&lt;/code&gt; &lt;em&gt;should&lt;/em&gt; scroll the page to an element with &lt;code&gt;id='test'&lt;/code&gt; and change the url to &lt;code&gt;www.site.com/example#test&lt;/code&gt;. This link will mistakenly navigate the app to &lt;code&gt;www.site.com/#test&lt;/code&gt;, and likely end up rendering a 404 page.&lt;/p&gt;

&lt;h3&gt;
  
  
  This is a known bug
&lt;/h3&gt;

&lt;p&gt;The Sapper team is aware of this bug with &lt;a href="https://github.com/sveltejs/sapper/issues/331"&gt;one&lt;/a&gt; or &lt;a href="https://github.com/sveltejs/sapper/issues/904"&gt;two&lt;/a&gt; issues already filed about it. I looked into the issue but couldn't figure out a fix since both the &lt;a href="https://github.com/sveltejs/sapper/blob/ca3645768597035b08e9d0e025cabd062180184a/runtime/src/app/start/index.ts#L76"&gt;&lt;code&gt;handle_click&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://github.com/sveltejs/sapper/blob/9e5a140c9658c3c01d00f2fd8a20d25f6f752f42/runtime/src/app/app.ts#L163"&gt;&lt;code&gt;navigate&lt;/code&gt;&lt;/a&gt; functions seem to have short circuits for when the urls contain hashes. Sapper's &lt;a href="https://github.com/sveltejs/sapper/blob/ca034d0857379491831fc78ba974c411f5173be3/runtime/src/app/goto/index.ts"&gt;&lt;code&gt;goto&lt;/code&gt;&lt;/a&gt; function doesn't appear to have any way to know about a hash link, so maybe that's the issue?&lt;/p&gt;

&lt;h3&gt;
  
  
  There IS a temporary fix
&lt;/h3&gt;

&lt;p&gt;There is &lt;a href="https://github.com/sveltejs/sapper/issues/904#issuecomment-540536561"&gt;a workaround&lt;/a&gt; for this bug.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;script&amp;gt;
  import { onMount } from 'svelte'

  onMount( () =&amp;gt; {
    document.querySelectorAll('a').forEach(a =&amp;gt; {
      if (!a.hash||!document.querySelectorAll(a.hash).length) {
        return
      }
      a.href = window.location + a.hash
    })
  })
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I am a strong proponent that all sites should work without JavaScript, so I don't love that this fix relies on the clientside &lt;code&gt;onMount&lt;/code&gt; lifecycle hook. On the other hand, if a user has all scripts blocked from running then Sapper won't be able to rehydrate in the first place. This means Sapper's router won't take over navigation and all links should keep their default browser behavior, so this is a non-issue. Still, this code takes a second to run after the component mounts and the links still momentarily don't work. Also depending on the CSS this could lead to some style flashes as &lt;code&gt;href&lt;/code&gt; shuffle around.&lt;/p&gt;

&lt;h2&gt;
  
  
  Components don't have programmatic access to their children
&lt;/h2&gt;

&lt;p&gt;Time to circle back to the &lt;code&gt;slugify&lt;/code&gt; and &lt;code&gt;getText&lt;/code&gt; functions I mentioned earlier.&lt;/p&gt;

&lt;p&gt;The HTML that MDX or MDsveX would replace looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;h1&amp;gt;The sun is shining, but the ice is slippery.&amp;lt;/h1&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The goal is to transform the markup into something that looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;h1 id="the-sun-is-shining-but-the-ice-is-slippery"&amp;gt;
  &amp;lt;a href="#the-sun-is-shining-but-the-ice-is-slippery"&amp;gt;
    The sun is shining, but the ice is slippery.
  &amp;lt;/a&amp;gt;
&amp;lt;/h1&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the React version of the Heading component the &lt;code&gt;id&lt;/code&gt; and &lt;code&gt;href&lt;/code&gt; attributes are derived by running the child text of the component through a function that will make them viable hash links.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const Heading = (props) =&amp;gt; {

  import { slugify } from 'helpers'
  const hashUrl = slugify(props.children)

  return (
    &amp;lt;h1 
      id={hashUrl}
      className='heading'
    &amp;gt;
      &amp;lt;a href={`#${hashUrl}`}&amp;gt;
        {props.children}
      &amp;lt;/a&amp;gt;
    &amp;lt;/h1&amp;gt;
  )
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works because &lt;code&gt;props.children&lt;/code&gt; can be accessed and manipulated like any other React prop. Svelte's API works differently, providing the &lt;code&gt;&amp;lt;slot /&amp;gt;&lt;/code&gt; component as a way to render child content.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;script&amp;gt;
  import { slugify } from 'helpers'
  let id
  let hashLink
  const getText = node =&amp;gt; {
    id = slugify(node.text)
    hashLink = `#${id}`
  }
&amp;lt;/script&amp;gt;

&amp;lt;h1 id={id}&amp;gt;&amp;lt;a use:getText href={hashLink}&amp;gt;&amp;lt;slot/&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/h1&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;props.children&lt;/code&gt; and &lt;code&gt;&amp;lt;slot /&amp;gt;&lt;/code&gt; are similar but have one major difference. React has access to the child content and can transform it during run time before render. Svelte needs to know about child content at compile time, and can't know about children until after they are rendered.&lt;/p&gt;

&lt;p&gt;The only way I found to get the child content to pass to the &lt;code&gt;slugify&lt;/code&gt; function was to query it from the DOM &lt;em&gt;after&lt;/em&gt; render. Svelte does have &lt;a href="https://svelte.dev/docs#use_action"&gt;&lt;code&gt;use:action&lt;/code&gt; binding&lt;/a&gt;, which makes it very easy to run a function as soon as an element is created. There are two big downsides to this. First, this leads to the flash of no-link styles I mentioned earlier. A component is created, then once the page is loaded enough to fire a DOM query the hash link is generated and appended. The second is that since this relies on direct DOM manipulation, this function won't fire if a user doesn't allow Javascript. This is a bummer since hash links navigation is something that browsers support natively and definitely shouldn't require Javascript.&lt;/p&gt;

&lt;h3&gt;
  
  
  There is an open Pull Request that will fix this
&lt;/h3&gt;

&lt;p&gt;Some good news about this missing feature is that there is currently an &lt;a href="https://github.com/sveltejs/svelte/pull/4604"&gt;open pull request&lt;/a&gt; to add a &lt;code&gt;$$slots&lt;/code&gt; prop. This will work similarly to the existing &lt;a href="https://svelte.dev/docs#Attributes_and_props"&gt;&lt;code&gt;$$props&lt;/code&gt;&lt;/a&gt; value. It is hard to say if this would solve my exact problem since it isn't implemented yet, but any programmatic access to the child content should work better than having to query text after it renders. The bad news is that this feature doesn't currently appear on &lt;a href="https://github.com/sveltejs/svelte/projects/1"&gt;Svelte's roadmap&lt;/a&gt; so it's hard to say when exactly it might land.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dealbreakers?
&lt;/h2&gt;

&lt;p&gt;So, do these handful of "missing" features mean I don't recommend trying Svelte?&lt;/p&gt;

&lt;p&gt;No, Svelte is still a great framework that has enough features to do most things. Since it is a little younger than some other frameworks and doesn't have a massive corporation behind it, it may not have features for a handful of fairly specific scenarios, yet.&lt;/p&gt;

&lt;p&gt;This does, unfortunately, mean that I won't currently be moving my blog from Gatsby to Sapper. I plan on giving this another shot when Sapper does a full 1.0 release. This should give Svelte time to release more features and give me time to refactor some of my React code to make it more portable. I'm also in the early planning stages of another web app and I'm still considering using Svelte and Sapper to build it. Like I said, Svelte is great, it just isn't not a great fit for my blog right now.&lt;/p&gt;

&lt;p&gt;I've also only been working with Svelte for a few months, and it is entirely possible that these things &lt;em&gt;are&lt;/em&gt; doable and I just haven't discovered how. If that's the case I would absolutely love to be told I'm wrong, let me know &lt;a href="https://twitter.com/ryanfiller_"&gt;@ryanfiller_&lt;/a&gt; on Twitter and I'll update this article with the fix.&lt;/p&gt;

</description>
      <category>code</category>
      <category>svelte</category>
      <category>sapper</category>
    </item>
  </channel>
</rss>
