<?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: Andrico</title>
    <description>The latest articles on DEV Community by Andrico (@andrico1234).</description>
    <link>https://dev.to/andrico1234</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%2F215913%2Fb0c70b41-9ec3-49d7-a095-a1149de69016.jpg</url>
      <title>DEV Community: Andrico</title>
      <link>https://dev.to/andrico1234</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/andrico1234"/>
    <language>en</language>
    <item>
      <title>The Two Lines of CSS That Tanked Performance (120fps to 40fps)</title>
      <dc:creator>Andrico</dc:creator>
      <pubDate>Mon, 02 Sep 2024 13:00:00 +0000</pubDate>
      <link>https://dev.to/andrico1234/the-two-lines-of-css-that-tanked-performance-120fps-to-40fps-3lnj</link>
      <guid>https://dev.to/andrico1234/the-two-lines-of-css-that-tanked-performance-120fps-to-40fps-3lnj</guid>
      <description>&lt;p&gt;I recently released &lt;a href="https://learn-wcs.com/" rel="noopener noreferrer"&gt;Learn WCs&lt;/a&gt; and If you’ve seen it, you’ve likely noticed the animation in the background, where the coloured circles move diagonally across the screen. It looks like this:&lt;/p&gt;


  


&lt;p&gt;It works nicely on Chrome and Safari, but I noticed a severe drop in performance on Firefox.&lt;/p&gt;


  


&lt;p&gt;The performance was so bad, that I straight up disabled this animation in Firefox.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does the animation work?
&lt;/h2&gt;

&lt;p&gt;The animation is built using two nested divs. The outer div is the first child of the site’s body tag.&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;body&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;"background-mask"&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;"background-gradient"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

    &lt;span class="c"&gt;&amp;lt;!-- Rest of content --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;.background-gradient&lt;/code&gt; element is responsible for creating a gradient that spans the entire width and height of its parent container. Like so:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7fkiy9n5vo5ccbgj3kts.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7fkiy9n5vo5ccbgj3kts.png" alt="The background with no mask is just a gradient that goes from purple to red to orange." width="800" height="329"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The outer &lt;code&gt;.background-mask&lt;/code&gt; is responsible for two things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It sets the position to &lt;code&gt;fixed&lt;/code&gt;, and makes the container fill the entire dimensions of the viewport.&lt;/li&gt;
&lt;li&gt;Creates a dotted mask over the gradient&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This ensures that the colour of the dots is the colour of the gradient directly underneath it:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdnj9t25upx0t8zf3d94z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdnj9t25upx0t8zf3d94z.png" alt="The background with mask." width="800" height="335"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here’s the CSS for everything I described above:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.background-mask&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;--mask-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;24px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c"&gt;/* Position Styles */&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;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;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;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="c"&gt;/* Mask Styles */&lt;/span&gt;
    &lt;span class="py"&gt;mask-image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;radial-gradient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;black&lt;/span&gt; &lt;span class="m"&gt;2px&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;transparent&lt;/span&gt; &lt;span class="m"&gt;2px&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="py"&gt;mask-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--mask-size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--mask-size&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="py"&gt;mask-position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0px&lt;/span&gt; &lt;span class="m"&gt;0px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;mask-move&lt;/span&gt; &lt;span class="m"&gt;3s&lt;/span&gt; &lt;span class="n"&gt;infinite&lt;/span&gt; &lt;span class="n"&gt;linear&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.background-gradient&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="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--red&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nl"&gt;background-image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--gradient&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;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="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;@keyframes&lt;/span&gt; &lt;span class="n"&gt;mask-move&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="py"&gt;mask-position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0px&lt;/span&gt; &lt;span class="m"&gt;0px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="err"&gt;100&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="py"&gt;mask-position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--mask-size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--mask-size&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefers-reduced-motion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;.hero-background-mask&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you’re interested in learning more about masks in CSS, then I can recommend this comprehensive post by &lt;a href="https://ishadeed.com/article/css-masking/" rel="noopener noreferrer"&gt;Ahmad Shadeed&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s causing this drop in performance?
&lt;/h2&gt;

&lt;p&gt;Not all CSS properties animate equally. Without going too much into how the browser renders HTML to the page (&lt;a href="https://component-odyssey.com/tips/02-how-does-the-browser-render-html" rel="noopener noreferrer"&gt;though I’ve outlined it here&lt;/a&gt;), there are a handful of stages it goes through. The three stages that we’re interested in are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Layout - When the browser calculates the size and positions of the elements on the page&lt;/li&gt;
&lt;li&gt;Paint - Draws all the visual aspects of the page, like images, colors, shadows, etc&lt;/li&gt;
&lt;li&gt;Composite - Layering the elements on top of one another in the correct order&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The order of the pipeline looks like this:&lt;/p&gt;

&lt;p&gt;Layout → Paint → Composite&lt;/p&gt;

&lt;p&gt;The layout and paint processes can be CPU-intensive, so it’s important to try and reduce the amount of times your CSS triggers the stages in the pipeline*.* The browser helps in some part by optimising performance for certain properties, some skip entire stages of the rendering pipeline and others can leverage &lt;em&gt;hardware acceleration&lt;/em&gt; to move computation from the CPU to the GPU.&lt;/p&gt;

&lt;p&gt;Animating certain properties, like &lt;code&gt;translate&lt;/code&gt; and &lt;code&gt;opacity&lt;/code&gt; , both avoids triggering a layout and uses hardware acceleration.&lt;/p&gt;

&lt;p&gt;Sadly, this is not the case when animating &lt;code&gt;mask-position&lt;/code&gt;. I took a look at Chrome and saw that the paint count for the background div was increasing on every frame. After a few seconds it had already triggered a paint over 1,000 times.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F326l8tlfjve5uqsl4g40.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F326l8tlfjve5uqsl4g40.png" alt="Paint count for the background div layer is 7941." width="800" height="255"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Even with this high paint count, the animation on Chrome feels smooth. However, it feels super janky on Firefox. Annoyingly, I couldn‘t find a way to measure the paint count on Firefox, so any assumptions I make about Firefox’s poor performance is purely conjecture.&lt;/p&gt;

&lt;p&gt;What I did notice is that the animation is fine for small devices, but gets worse as the size of the screen increases. My working theory is that Firefox doesn’t batch the layout triggers for each the 24x24 masks, which causes the FPS to tank when more 24x24 masks are present. Again, I might be completely wrong here.&lt;/p&gt;


  


&lt;h2&gt;
  
  
  How did I fix this?
&lt;/h2&gt;

&lt;p&gt;Instead of animating badly optimised CSS properties like &lt;code&gt;mask-position&lt;/code&gt; , I needed to lean on the more performant properties, like &lt;code&gt;translate&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The solution wasn’t to move the masks by 24px, but to instead move the entire background element using the &lt;code&gt;translate&lt;/code&gt; property.&lt;/p&gt;

&lt;p&gt;From an abstract standpoint, this is how the animation looks:&lt;/p&gt;


  


&lt;p&gt;Here’s the two line change in the CSS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* --mask-size = 24px */&lt;/span&gt;

&lt;span class="k"&gt;@keyframes&lt;/span&gt; &lt;span class="n"&gt;mask-move&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;translate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--mask-size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="err"&gt;*&lt;/span&gt; &lt;span class="m"&gt;-1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--mask-size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="err"&gt;*&lt;/span&gt; &lt;span class="m"&gt;-1&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="err"&gt;100&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;translate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0px&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0px&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 browser no longer animates the &lt;code&gt;mask-position&lt;/code&gt;, which triggered a layout on each frame. Even though the background moves on each frame, through &lt;code&gt;translate&lt;/code&gt; it doesn’t trigger a layout or a paint. You can see that the only paints twice, down from 1,000+ every minute.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdanbdbxfw0j66k400qnq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdanbdbxfw0j66k400qnq.png" alt="Paint count for the background div layer is 2." width="800" height="317"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Eagle-eyed viewers will have spotted a problem. If you remember, the height and width of the background fills the viewport. Shifting the background left and up by 24px leaves us with this empty space in the viewport.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxka0s18y20fqk63xx0h3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxka0s18y20fqk63xx0h3.png" alt="At the start of the animation the background doesn't cover the far right and far bottom of the viewport." width="800" height="494"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Solving it is as simple as adding the mask size to the width and height of the container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.background-mask&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;--mask-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;24px&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="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;100%&lt;/span&gt; &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--mask-size&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="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;100%&lt;/span&gt; &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--mask-size&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s take a look again in Firefox:&lt;/p&gt;


  


&lt;p&gt;It may not be a perfect solution, but it’s always a little satisfying pulling off a fun &lt;em&gt;smoke and mirrors&lt;/em&gt; CSS trick.&lt;/p&gt;

</description>
      <category>css</category>
      <category>webdev</category>
      <category>performance</category>
    </item>
    <item>
      <title>What is a component library and should you build your own?</title>
      <dc:creator>Andrico</dc:creator>
      <pubDate>Tue, 06 Aug 2024 09:18:11 +0000</pubDate>
      <link>https://dev.to/andrico1234/what-is-a-component-library-and-should-you-build-your-own-2j9i</link>
      <guid>https://dev.to/andrico1234/what-is-a-component-library-and-should-you-build-your-own-2j9i</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;💡 I want to give a special shoutout to stephband (&lt;a href="https://front-end.social/@stephband" rel="noopener noreferrer"&gt;Mastodon&lt;/a&gt;, &lt;a href="https://bsky.app/profile/stephen.band" rel="noopener noreferrer"&gt;Bluesky&lt;/a&gt;) for proofreading and providing feedback.&lt;/p&gt;

&lt;p&gt;Please note that this is a X-Post from my &lt;a href="https://component-odyssey.com/articles/10-what-is-a-library-should-you-build-your-own" rel="noopener noreferrer"&gt;site&lt;/a&gt;. The content is largely the same, but the original post has interactive snippets and videos that are omitted in this article.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What if there were no music albums?
&lt;/h2&gt;

&lt;p&gt;I’m a huge music fan. One of my favourite pastimes is to put on a record and listen to it front to back. A music album is simple in concept, it’s a suite of recorded tracks by an artist and is considered a complete and coherent set of songs.&lt;/p&gt;

&lt;p&gt;But what if recorded music didn’t exist? Instead of putting songs to tape, CD, or mp3 (or FLAC if you’re that way inclined), you could only listen to an album if the artist played it live for you. You’d need to go to the band, ask them to set up their equipment and get them to play the album front-to-back. They’d need to play it the same way every time to ensure that everyone had the exact same experience.&lt;/p&gt;

&lt;p&gt;The cracks would start to show. It’s not an efficient way to ensure that anyone interested in the band’s music can listen to it. If Taylor Swift were to play her song Fortnight personally for every person who listened to it on Spotify, it would take her 3,179 years. And that doesn’t account for any &lt;a href="https://www.threads.net/@adhd.international/post/C6ugTXAPt71?hl=en-gb" rel="noopener noreferrer"&gt;plane travel&lt;/a&gt;. Artists would get bored, maybe even careless, leading to a lesser experience for their listeners.&lt;/p&gt;

&lt;p&gt;So how does this relate to web development? Every time you build a UI control, you have to ensure it’s functioning, robust, and accessible. You’ll get bored if you keep rewriting the same UI every time. Mistakes will slip through, leading to a worse experience for your end users.&lt;/p&gt;

&lt;h2&gt;
  
  
  A little about me
&lt;/h2&gt;

&lt;p&gt;I’ve been a web developer for nearly 10 years, and I’ve written hundreds of components myself, often the same UI pattern many times. I’ve used dozens of component libraries, and have built admin dashboards, component libraries, mobile applications, blogs, figma plugins, VSCode extensions, and more. This article will be a distillation about where I see the role of components, libraries, and whether developers should write their own.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a component?
&lt;/h2&gt;

&lt;p&gt;When building user interfaces, we don’t write all the HTML markup from scratch every single time. We write our UIs using components— reusable building blocks that encapsulate common UI patterns. Writing a component lets you use it multiple times in a single project or even in independent projects.&lt;/p&gt;

&lt;p&gt;Here I’ve written a counter component, I’ve written it once and used it in multiple places on the page.&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;body&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;"wrapper"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;counter-button&amp;gt;&amp;lt;/counter-button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;counter-button&amp;gt;&amp;lt;/counter-button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;counter-button&amp;gt;&amp;lt;/counter-button&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;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"module"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;LitElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CounterButton&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;LitElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;super&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;count&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="p"&gt;}&lt;/span&gt;

      &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nx"&gt;properties&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;count&lt;/span&gt;&lt;span class="p"&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="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="nf"&gt;_increment&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;count&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="nf"&gt;render&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;html&lt;/span&gt;&lt;span class="s2"&gt;`
          &amp;lt;button @click=&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;_increment&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;gt;Count: &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;count&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/button&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;customElements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;counter-button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CounterButton&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;/body&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Us tutorial creators like to demo counters like they’re going out of style, but a real-world application will contain dozens of different UI patterns written as components.&lt;/p&gt;

&lt;p&gt;In this article, I’ll group CSS rules that provide styling for certain UI patterns under the components umbrella. The definition can get murky depending on who you ask.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a component library?
&lt;/h2&gt;

&lt;p&gt;Not all components are standalone. It makes sense for many components to be grouped within a single package, called a component library.&lt;/p&gt;

&lt;p&gt;If you want your site to have a specific look or feel, you can use a &lt;em&gt;component library&lt;/em&gt;. There are component libraries that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;offer components that adhere to a design specification.&lt;/li&gt;
&lt;li&gt;offer multiple solutions for a specific UI pattern.&lt;/li&gt;
&lt;li&gt;work with specific toolchain&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But they come in different shapes and sizes. The definition I’ve come to use when defining a component library is the following:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;A component library is a set of reusable components that are cohesive in their utility, or appearance (or both). A great component library will help developers achieve their UI needs efficiently, while offering an exemplary experience for the end user.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s the difference between a component library and a design system
&lt;/h2&gt;

&lt;p&gt;I talk about guidelines and design systems later in this article, so I’ll take a moment to disambiguate them. It can be difficult to see where one ends, one begins, or one subsumes the other.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Design Systems&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I see a design system as a specification for how things should look, feel, and behave. A design system can encompass a product, a brand, or a company to ensure consistency across the suite of experiences. A comprehensive design system will dictate everything from font families, font sizes, spacing sizes, UI patterns, and copy guidelines.&lt;/p&gt;

&lt;p&gt;A few of the most well-known design systems include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://m3.material.io/foundations/layout/understanding-layout/overview" rel="noopener noreferrer"&gt;Material Design&lt;/a&gt; (Google)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://base.uber.com/6d2425e9f/p/294ab4-base-design-system" rel="noopener noreferrer"&gt;Base Design&lt;/a&gt; (Uber)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.lightningdesignsystem.com/getting-started/" rel="noopener noreferrer"&gt;Lightning Design System&lt;/a&gt; (Salesforce)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While many design systems are specific to companies, there are design systems, like Material Design, that teams across the globe use to shortcut their way to building familiar feeling products. You’ve probably used a handful of products that use Material Design principles but they’re &lt;a href="https://www.smashingmagazine.com/2021/02/material-design-text-fields/" rel="noopener noreferrer"&gt;certainly not free from basic usability issues&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Component Libraries&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As for component libraries, they may or may not be the code implementation of a design system. If you work for a company with a design system, it’s likely that the corresponding component library (if one exists), is tightly integrated with it.&lt;/p&gt;

&lt;p&gt;For instance, Google’s &lt;a href="https://material-web.dev/" rel="noopener noreferrer"&gt;Material Web&lt;/a&gt; is a web component implementation of Material Design. &lt;a href="https://baseweb.design/components/button/" rel="noopener noreferrer"&gt;Base Web&lt;/a&gt; and &lt;a href="https://lwc.dev/guide/introduction" rel="noopener noreferrer"&gt;Lightning Web Components&lt;/a&gt; are also open source too.&lt;/p&gt;

&lt;h2&gt;
  
  
  A brief history of component libraries
&lt;/h2&gt;

&lt;p&gt;The concept of UI components (or widgets) has been around for a long time. If you want to see a museum’s worth of retro user interfaces, grab some popcorn and watch this &lt;a href="https://vimeo.com/61556918" rel="noopener noreferrer"&gt;2+ hour video of "all the widgets" from 1974-1990&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;From the early 2000s, we’d start seeing component libraries made to help developers build for the web. The web browser landscape back then was unrecognisable from what we see now. Versions of Internet Explorer deviated away from the spec entirely, which was particularly problematic given the &lt;a href="https://en.wikipedia.org/wiki/Usage_share_of_web_browsers#TheCounter.com_(2000_to_2009)" rel="noopener noreferrer"&gt;huge market share that IE had back in the day&lt;/a&gt;. &lt;a href="https://www.quora.com/Why-do-people-hate-IE6-so-much-and-want-it-to-die#:~:text=IE6%20doesn't%20support%20web,PNGs%20to%20natively%20support%20IE6" rel="noopener noreferrer"&gt;Internet Explorer 6 was famously known for being a pain to develop for&lt;/a&gt;. Mainly due to its incorrect implementation of the &lt;a href="https://www.webfx.com/blog/web-design/definitive-guide-to-taming-the-ie6-beast/#616723179a361-3" rel="noopener noreferrer"&gt;box model&lt;/a&gt;, and lack of CSS2 support.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 TIL, Internet Explorer supported a “&lt;a href="https://www.notion.so/Complete-written-content-806882f918204715a6e45df68f492bdd?pvs=21" rel="noopener noreferrer"&gt;quirks mode&lt;/a&gt;” that let developers write non-standard HTML and CSS to appease older browsers that didn’t support the standards.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Fortunately, when I started web development in earnest, many of these issues were ironed out. By this point, there were still a handful of libraries that made writing complex interfaces with cross-browser support a little easier. &lt;a href="https://jqueryui.com/" rel="noopener noreferrer"&gt;jQuery UI&lt;/a&gt;, the first component library I used, supported accordions and other widgets. But the browser is constantly evolving, and we now have a native way of implementing this accordion pattern using the &lt;code&gt;details&lt;/code&gt; and &lt;code&gt;summary&lt;/code&gt; elements, available in all browsers in 2020. With these elements, you can get pretty far along creating interactive accordions without JavaScript.&lt;/p&gt;

&lt;p&gt;Contrast this with 2009, and these elements haven’t been implemented in any browser. It required a fair bit of JS to get working. Have a look at the &lt;a href="https://code.jquery.com/ui/1.7.0/jquery-ui.js" rel="noopener noreferrer"&gt;jQuery UI v1.7 source code&lt;/a&gt;, and CTRL+F “accordion” if you want to see how web devs were implementing accordions 15 years ago.&lt;/p&gt;

&lt;p&gt;Over the next couple of decades, the capabilities of the web grew. More powerful devices meant more powerful browsers. More powerful browsers meant web applications became more ambitious. Developers responded by creating the tools to help us build these applications by allowing us to create UIs using &lt;em&gt;building blocks&lt;/em&gt;, i.e., a component model. We saw a proliferation of these component-based frameworks. I’m talking Angular, React, and Vue. Each with its own rich ecosystem of component libraries.&lt;/p&gt;

&lt;p&gt;There’s a reasonable argument to be made that there has been an over-correction and that the frontend landscape is now oversaturated with tools that are &lt;em&gt;too&lt;/em&gt; powerful for most people’s needs, but let’s not go there.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This version is extended with interactive snippets on the &lt;a href="https://component-odyssey.com/articles/10-what-is-a-library-should-you-build-your-own" rel="noopener noreferrer"&gt;original post&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What makes a good component library?
&lt;/h2&gt;

&lt;p&gt;The challenge with building a component library is that they’re not a &lt;em&gt;one-and-done&lt;/em&gt; deal. Many of the most popular libraries have been around for years and have had heaps of research, usage feedback, and contributions to get them to where they are now.&lt;/p&gt;

&lt;p&gt;I’ve found that a good component library often has the following traits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It understands the problems of its target developers and solves those problem well&lt;/li&gt;
&lt;li&gt;It has great documentation&lt;/li&gt;
&lt;li&gt;It ensures a good experience for the end-user&lt;/li&gt;
&lt;li&gt;It’s robust and caters for appropriate input modes and devices.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On the flip side, a way to discern if a component library &lt;em&gt;isn’t good&lt;/em&gt; is if it doesn’t consider accessibility, has an inconsistent API, has little to no project stewardship, or has no clear and consistent documentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are the benefits of using a component library?
&lt;/h2&gt;

&lt;p&gt;We know what a good component library looks like, so let’s see how one can make your life, and the lives of your users a little better.&lt;/p&gt;

&lt;h3&gt;
  
  
  Component libraries save you time
&lt;/h3&gt;

&lt;p&gt;If you’re on a project with a tight deadline, it’s important to be efficient. But efficiency shouldn’t come at the cost of crafting a robust web experience. Using a component library lets you spend less time reinventing the wheel and more time focusing on the finer details.&lt;/p&gt;

&lt;h3&gt;
  
  
  Component libraries make you and your users happier
&lt;/h3&gt;

&lt;p&gt;We’re not motivated by repetitive work. We enjoy technical challenges, and writing the same components over again is not a fun challenge. We’ve already spoken about what happens when we get bored and let mistakes slip through.&lt;/p&gt;

&lt;p&gt;If you wanted to implement a dialog component from scratch, you’d need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Handle focus trapping correctly&lt;/li&gt;
&lt;li&gt;Make the rest of the page inert&lt;/li&gt;
&lt;li&gt;Position the dialog correctly&lt;/li&gt;
&lt;li&gt;Ensure that it works with assistive technologies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It takes work to remember and implement the above, but the consequence of getting it wrong can render your interface literally unusable, such is the case if you &lt;a href="https://www.w3.org/WAI/WCAG21/Understanding/no-keyboard-trap.html" rel="noopener noreferrer"&gt;incorrectly handle focus&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;By using a component library that’s been built with the end users in mind, you can prevent the risk of introducing broken experiences, while spending less time rebuilding the same components.&lt;/p&gt;

&lt;h3&gt;
  
  
  Component libraries lead to consistent experiences
&lt;/h3&gt;

&lt;p&gt;If you work for a company with several different web applications, they’ll generally follow a set of guidelines. These guidelines might dictate the colour palette to use, the size of your typography, or how UI elements should look and behave.&lt;/p&gt;

&lt;p&gt;But you increase the likelihood of your application deviating from the style guide if you’re re-writing components. By having a component library, you can more easily audit your component’s UI against the brand guidelines so they look great, wherever they’re used.&lt;/p&gt;

&lt;p&gt;Uber has several different apps that share the same user interface elements. I’m almost certain that they use the same component library across these apps. That way each new app is virtually guaranteed to adhere to the brand’s guidelines.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4lh0etdq7yhk4zhmiac8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4lh0etdq7yhk4zhmiac8.png" alt="Different Uber apps side by side, showing how similar they are in terms of appearance" width="800" height="648"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What are the drawbacks of using a third-party component library?
&lt;/h2&gt;

&lt;p&gt;The benefits I’ve mentioned above are irrespective of whether you’re using your own component library or a third party. If you or your team has decided that they don’t want to build a library, and instead lean on a third-party, then it’s worth considering the following.&lt;/p&gt;

&lt;h3&gt;
  
  
  Vendor lock-in
&lt;/h3&gt;

&lt;p&gt;By choosing a component library, you’re picking a partner who will greatly impact how you write your frontend code and how your interfaces will look and behave.&lt;/p&gt;

&lt;p&gt;The former will have a big impact on you, and the latter will have a big impact on your end users. Using a component library is locking you into the standards of that component library.&lt;/p&gt;

&lt;p&gt;The library could introduce massive breaking changes in a major version that could require dedicated development time, and a lot of testing to ensure that no serious regressions were introduced.&lt;/p&gt;

&lt;p&gt;A few years back I used React Admin to build a complex admin dashboard to an internal division. The library offered a suite of components specifically dedicate for fetching and displaying complex data. Because our application at the time relied heavily on React Admin, upgrading between major versions was challenging, especially as many of the internal tools used by React Admin had been swapped out for others. The change surface was huge, and we spent a good deal of time upgrading and flagging the issues that we spotted.&lt;/p&gt;

&lt;p&gt;I don’t believe that building our own solution would have saved us any time in the long term, but this kind of vender lock-in is worth considering, especially before going all in on a tool.&lt;/p&gt;

&lt;h3&gt;
  
  
  Code bloat
&lt;/h3&gt;

&lt;p&gt;Shocking as it is, libraries with a lot of components tend to be written using lots of code. Code that you download when installing dependencies, and code you sending over to your end users.&lt;/p&gt;

&lt;p&gt;Modern tooling makes it easier to perform bundle optimisations like tree-shaking to remove unused code, but there’s no guarantee that you’re removing all of the code that your users won’t need.&lt;/p&gt;

&lt;p&gt;Unless you dig deep into the libraries that you’re using, you may not be aware of all the separate packages they’re importing. You could end up with hundreds of unnecessary dependencies. The folks in the &lt;a href="https://e18e.dev/" rel="noopener noreferrer"&gt;e18e&lt;/a&gt; community have been working hard at bringing this problem to light, &lt;a href="https://x.com/DanaWoodman/status/1819084012729798833" rel="noopener noreferrer"&gt;while also fixing it too&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Many of these problems could also be said about rolling your own component library. The biggest difference is that you have stewardship over your component library. You’re able to define how it solves your specific problems, and you have control over improving its shortcomings.&lt;/p&gt;

&lt;h2&gt;
  
  
  The different shapes a component library can take
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://cds.cern.ch/record/369245/files/dd-89-001.pdf" rel="noopener noreferrer"&gt;initial proposal&lt;/a&gt; for the World Wide Web was a tool to improve communication between researchers at CERN. The proposal outlined how documents could be shared, and linked to one another through the use of hypertext. This is the fundamental cornerstone of the web, and we still use the humble &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; tag to link between other HTML documents across the web.&lt;/p&gt;

&lt;p&gt;But the web has grown in scope over the last few decades, and the browsers we use to navigate the web have become beasts of their own. The browsers today can enable &lt;a href="https://plumegame.com/" rel="noopener noreferrer"&gt;powerful forms of creative expression&lt;/a&gt;, and the &lt;a href="https://medium.com/@addyosmani/photoshop-is-now-on-the-web-38d70954365a" rel="noopener noreferrer"&gt;running of native-like software&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There are hundreds of different solutions out there, some general purpose, others hyper-niche, but finding the right tool for your next project requires a complex decision process that might look like this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbn80olo3e0h8v4h4v311.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbn80olo3e0h8v4h4v311.png" alt="The different kinds of component libraries, and when you should use them. The diagram's context is explored below" width="800" height="1450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This isn’t a comprehensive list of ALL use cases or component library types, but it illustrates how component libraries differ in terms of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the technologies involved.&lt;/li&gt;
&lt;li&gt;the levels of abstraction they offer.&lt;/li&gt;
&lt;li&gt;the problems they solve.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s take a look at some of the most common component library types&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 A quick side note: If you're interested in building your own web component library, then consider checking out my course &lt;a href="https://component-odyssey.com/" rel="noopener noreferrer"&gt;Component Odyssey&lt;/a&gt;. You'll learn how to build and publish a component library that works in any frontend framework.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Utility classes libraries / CSS Style Guides
&lt;/h3&gt;

&lt;p&gt;For me, &lt;a href="https://getbootstrap.com/" rel="noopener noreferrer"&gt;Bootstrap&lt;/a&gt; is the first that comes to mind. Back in the day, if you wanted to give your site a quick lick of paint, you’d drop the CDN link to the bootstrap CSS file and immediately get that Bootstrap look. It was everywhere in the mid-2010s.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7zl86ek2nhsoxc043pnt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7zl86ek2nhsoxc043pnt.png" alt="A demo example of a Bootstrap web, circa 2013" width="800" height="471"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The technical scope of these kind of tools range from&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A single CSS file (&lt;a href="https://picocss.com/" rel="noopener noreferrer"&gt;Pico&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;to&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A toolchain to generate CSS classes based on your configuration (&lt;a href="https://tailwindcss.com/" rel="noopener noreferrer"&gt;Tailwind&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Dozens of other tools, like &lt;a href="https://open-props.style/" rel="noopener noreferrer"&gt;Open Props&lt;/a&gt;, fit somewhere in between.&lt;/p&gt;

&lt;h3&gt;
  
  
  Off-the-shelf component libraries
&lt;/h3&gt;

&lt;p&gt;If you’re building an interactive web application, you might just want to grab a suite of components that look great and function well. There are many of these off-the-shelf component libraries that give you everything you need and more.&lt;/p&gt;

&lt;p&gt;Regardless of which framework you’re writing your app with, there’s likely to be a set of great looking components for you to use.&lt;/p&gt;

&lt;p&gt;Another great component library is &lt;a href="https://shoelace.style/" rel="noopener noreferrer"&gt;Shoelace&lt;/a&gt;, which provides dozens of fully interactive and fully-styled components.&lt;/p&gt;

&lt;p&gt;What makes libraries like Shoelace particularly interestingly is that it’s built using web components, the browser’s built-in way of writing components. Building your UIs with tools like Shoelace, give you the added benefit of being able to use them across different frontend frameworks. &lt;a href="https://component-odyssey.com/articles/01-writing-components-that-work-in-any-framework" rel="noopener noreferrer"&gt;Which is something I’ve spoken a little about in the past.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's the same Shoelace component being used in Vue and React.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fanphi6seqnbqvjd0q7ty.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fanphi6seqnbqvjd0q7ty.gif" alt="Shoelace buttons in Vue and React" width="1308" height="720"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Unstyled components
&lt;/h3&gt;

&lt;p&gt;Depending on your project’s specs, you might not have the luxury of using an off-the-shelf tool. Your design specs might be very specific.&lt;/p&gt;

&lt;p&gt;I’ve seen teams roll components at the first sign of friction. And in one case of a hand-rolled data picker, led to a way worse user experience. In retrospect, using an unstyled component library would have given the team flexibility with appearance, while ensuring that the time&lt;/p&gt;

&lt;p&gt;That’s why you can reach for a library out there that offers completely unstyled components with flexible styling hooks. If it’s a good library, it’ll also take care of all those complex interactions. It’s a best of both worlds situation.&lt;/p&gt;

&lt;p&gt;It’s easy to mess up a checkbox if you want to push beyond the styling hooks that the browser provides, unless you test with a wide-range of devices and input modes.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The &lt;a href="https://component-odyssey.com/articles/10-what-is-a-library-should-you-build-your-own" rel="noopener noreferrer"&gt;original article&lt;/a&gt; has a video showing me interact with different checkbox implementations using a screen reader&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://www.radix-ui.com/" rel="noopener noreferrer"&gt;Radix&lt;/a&gt; is a popular example of a library, but it’s built using React.&lt;/p&gt;

&lt;p&gt;Other examples of component libraries like this are &lt;a href="https://lion-web.netlify.app/" rel="noopener noreferrer"&gt;Lion&lt;/a&gt; and &lt;a href="https://headlessui.com/" rel="noopener noreferrer"&gt;HeadlessUI&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Copy Pastable Component Libraries
&lt;/h3&gt;

&lt;p&gt;Some developers might want the best of both worlds. They might want a component built by a trusted third-party library, while also having full control over the markup, styles, and functionality. Libraries like &lt;a href="https://ui.shadcn.com/" rel="noopener noreferrer"&gt;ShadCN&lt;/a&gt; allow for this kind of work flow, by allowing developers to copy and paste the component definition into their own projects, effectively letting them &lt;em&gt;own&lt;/em&gt; the component.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why isn’t there one component library to rule them all?
&lt;/h2&gt;

&lt;p&gt;At this point, it’s probably clear why no such single component library exists. We’ve looked at a non-exhaustive look at different groups of component libraries.&lt;/p&gt;

&lt;p&gt;There is, however, a movement to introduce a “&lt;a href="https://bradfrost.com/blog/post/a-global-design-system/" rel="noopener noreferrer"&gt;Global Design System&lt;/a&gt;”, a concept spearheaded by Brad Frost.&lt;/p&gt;

&lt;p&gt;In the announcement, Brad outlines that in the hundreds of projects he’s been a part of, many of the UI controls behave (or should behave) similarly across these various projects, yet developers reimplement the same thing in every single project. This has lead to lots of wasted time and effort, and inconsistencies between projects. This also expands to the existing component libraries out there. You’ll see that the keyboard behaviour for a combobox in React Aria, is different to that of the combobox in ShadCN.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The &lt;a href="https://component-odyssey.com/articles/10-what-is-a-library-should-you-build-your-own" rel="noopener noreferrer"&gt;original article&lt;/a&gt; has a video showing me interact with different combobox implementations using a keyboard&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Brad Frost is proposing a Global Design System as a set of web components that should be adoptable by almost any frontend project to ensure a baseline of functionality for the controls that are not yet available in HTML.&lt;/p&gt;

&lt;p&gt;There are &lt;a href="https://github.com/openui/open-ui/issues/1066" rel="noopener noreferrer"&gt;discussions going on within the Open UI&lt;/a&gt; to see how this could start taking shape within the next few years.&lt;/p&gt;

&lt;h2&gt;
  
  
  Should you build your own component library?
&lt;/h2&gt;

&lt;p&gt;This article has been a wide dive into component libraries, and with all that context, you’ll inevitably ask yourself, when staring at the empty HTML page for your next big project, whether to build your own component library or use an existing one.&lt;/p&gt;

&lt;p&gt;My first thought is: &lt;em&gt;I don’t think you should build your library.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I generally favour picking a battle-tested library. Particularly one that has:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Been used across thousands of projects&lt;/li&gt;
&lt;li&gt;A strong community, in Discord, or GitHub&lt;/li&gt;
&lt;li&gt;Great documentation&lt;/li&gt;
&lt;li&gt;A strong focus on accessibility&lt;/li&gt;
&lt;li&gt;Worked with the strengths of the chosen framework&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most importantly out of all of these is to use a component library puts care into building accessible components.&lt;/p&gt;

&lt;p&gt;Take a combobox for instance. It’s a search input and a select menu mixed into one. If you’ve built your own, you may get it looking good, and working with your mouse. But you’ll also need to consider:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cross browser support&lt;/li&gt;
&lt;li&gt;Tab and focus behaviour&lt;/li&gt;
&lt;li&gt;Screen reader support&lt;/li&gt;
&lt;li&gt;Handle states for async loading of search results&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Konnor Rogers, who does excellent work in the web + web component space, has shared countless frustrations with his experiences building an accessible combobox. Here’s one such &lt;a href="https://x.com/RogersKonnor/status/1797529313279140294" rel="noopener noreferrer"&gt;tweet he shared&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Screen reader support is particularly complex, and is worth of its own bullet-point list. To support screen readers, you’ll also need to handle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;live regions&lt;/li&gt;
&lt;li&gt;interactive controls&lt;/li&gt;
&lt;li&gt;selected items&lt;/li&gt;
&lt;li&gt;support between different screen readers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As a side note, I only have access to Voiceover, meaning it’s difficult for me to test these complex UI patterns using different screen readers. Like browsers, there are differences between screen readers. In this article, &lt;a href="https://www.scottohara.me/blog/2022/02/05/are-we-live.html" rel="noopener noreferrer"&gt;Are We Live?&lt;/a&gt;, Scott O’Hara describes how there’s variance among the difference with how they treat the live regions.&lt;/p&gt;

&lt;p&gt;This means it’s also up to you, the developer to pick a component library that you can trust has been developed with accessibility in mind. This is why it’s also important to pick a component library that has a strong community. It’s important to be able to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;See the bugs and issues others have flagged for a given library&lt;/li&gt;
&lt;li&gt;Suggest (or even contribute) improvements and changes to such library&lt;/li&gt;
&lt;li&gt;Discuss ideas with members of the community and build working relationships with active members of the community or even maintainers themselves&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finally, and not least, a great component library will consider much more than the aesthetics of their components. For a component library designed for the web, it should try it’s best to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;adhere to the &lt;a href="https://www.w3.org/TR/WCAG21/" rel="noopener noreferrer"&gt;Web Content Accessibility Guidelines (WCAG)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;ensure that the components work across different input modalities (touch, keyboard, screen reader)&lt;/li&gt;
&lt;li&gt;ensure that the components are usable for folks with additional requirements, like those living with vestibular disorders, vision impairments, or a broken hand.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Building your own component library can be a good thing, actually
&lt;/h2&gt;

&lt;p&gt;If I haven’t scared you off from building a component library, then let me contradict myself and explain why it can be a really good thing to build your own.&lt;/p&gt;

&lt;p&gt;If you take the time to put care and attention into building a component library, then you’ll find yourself a developer who better understands the browser platform, accessibility best practices, testing practices, and more.&lt;/p&gt;

&lt;p&gt;But it doesn’t just stop there, there are some excellent reasons to build your own library.&lt;/p&gt;

&lt;p&gt;For starters, you can build something tailored to your needs, and avoid some of the bloat you might get form an off-the-shelf component library. It’s up to you and your team to understand your end users, and you can build something specifically for them.&lt;/p&gt;

&lt;p&gt;You also have the opportunity to experiment with novel approaches. If you have a hyper-niche problem, there might not be a component library out there to solves that need. It could be a component library that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Visualises data in specific way&lt;/li&gt;
&lt;li&gt;Has a distinct and unique visual identity&lt;/li&gt;
&lt;li&gt;Is built on a new framework&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That gives you the opportunity to build something tailored to your needs. You then have the opportunity to change and fix things as your needs change, or as you understand the problem space better.&lt;/p&gt;

&lt;p&gt;Importantly, you’ll learn more about the web by doing so. If it’s your first time building a component library, it can be an opportunity to dive deeper into the &lt;a href="https://html.spec.whatwg.org/multipage/" rel="noopener noreferrer"&gt;HTML browser specs&lt;/a&gt;, or brush up on your &lt;a href="https://www.w3.org/TR/WCAG21/" rel="noopener noreferrer"&gt;web accessibility knowledge&lt;/a&gt;. This will improve your abilities as a web developer, which will serve you well in any frontend project in the future. It could even help you land your next job.&lt;/p&gt;

&lt;p&gt;So whether you should build a component library depends on your end goals. Consider questions like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Do I want to better under stand the browser?&lt;/li&gt;
&lt;li&gt;Do I want to build something quickly?&lt;/li&gt;
&lt;li&gt;Do I want to make it usable for as many users as possible?&lt;/li&gt;
&lt;li&gt;Do libraries exist that solve my current problem?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Depending on what your answers are, you can make the right call for your project.&lt;/p&gt;




&lt;p&gt;Thanks for reading! If you're interested in building your own web component library, then consider checking out my course &lt;a href="https://component-odyssey.com/" rel="noopener noreferrer"&gt;Component Odyssey&lt;/a&gt;. You'll learn how to build and publish a component library that works in any frontend framework.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>css</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Using Heaps of Cutting Edge CSS Features to Build a Progress Indicator</title>
      <dc:creator>Andrico</dc:creator>
      <pubDate>Tue, 09 Jan 2024 09:00:00 +0000</pubDate>
      <link>https://dev.to/andrico1234/using-heaps-of-cutting-edge-css-features-to-build-a-progress-indicator-2ig1</link>
      <guid>https://dev.to/andrico1234/using-heaps-of-cutting-edge-css-features-to-build-a-progress-indicator-2ig1</guid>
      <description>&lt;p&gt;For the last 7 months, I’ve had my head down building &lt;a href="https://component-odyssey.com"&gt;Component Odyssey&lt;/a&gt;. It’s been a richly fulfilling project and I’m eager for people to take the course and learn heaps about building component libraries using web components.&lt;/p&gt;

&lt;p&gt;I’ve seen some incredible demos over the past year and wanted to bite my teeth into some of these cool new features. So I used some downtime over the Christmas period to cram tons of new CSS features into a lesson progress indicator for the Component Odyssey platform. The result is the following progress indicator that shows how much of the page the user has scrolled:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LselSQLg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eqfvrvxrgxf8fuprhcfn.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LselSQLg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eqfvrvxrgxf8fuprhcfn.gif" alt="the fully completed progress indicator as it exists on Component Odyssey" width="720" height="720"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Building it gave me some exposure to some of the latest CSS features like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;animation-timeline: scroll()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;CSS trig functions, &lt;code&gt;sin()&lt;/code&gt; and &lt;code&gt;cos()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;color-mix()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;the &lt;code&gt;@property&lt;/code&gt; at-rule&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I know the risks of building something with a particular tool in mind. As the saying goes “When all you have is a hammer, then something something nails”&lt;/p&gt;

&lt;p&gt;Yes, I have a hammer, and I’m going to smash the walls down with it&lt;/p&gt;

&lt;p&gt;In this article, I’ll run through how to create a pared-down version of this swanky progress animation while still using all of the CSS features mentioned above. I’ll also show you how to gracefully handle browsers that don’t support these features through &lt;strong&gt;progressive enhancement&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you want to follow along, then it’s best to use the latest versions of Chrome or Safari, currently Firefox doesn’t have general support for properties like &lt;code&gt;animation-timeline&lt;/code&gt;. Get started by jumping into the &lt;a href="https://codepen.io/andrico1234/pen/WNmQrGK"&gt;starter Codepen&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you want to peruse the finished code, you can &lt;a href="https://codepen.io/andrico1234/pen/qBvdjLd"&gt;check it out here.&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating the markup
&lt;/h2&gt;

&lt;p&gt;I’ve already provided a little markup to simulate a page with enough content that you need to scroll to get to the bottom. To get started creating the progress indicator, you’ll need to add some more markup.&lt;/p&gt;

&lt;p&gt;The markup itself is really simple, we’ll only need to create 3 div elements.&lt;/p&gt;

&lt;p&gt;The outer element is responsible for the positioning and layout of the loader. We’ll give this a class of &lt;code&gt;wrapper&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The middle element is responsible for rendering the track to the screen. We’ll give this element a class of &lt;code&gt;progress&lt;/code&gt;. We’ll later use a &lt;code&gt;::after&lt;/code&gt; pseudo-element to create the thumb.&lt;/p&gt;

&lt;p&gt;The innermost element will be used to create the circular hole in the middle, making the indicator look like a low-calorie doughnut. This will have a class of &lt;code&gt;inner&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Take a look at the following if you need a hand visualising the structure:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--q02kCzgn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cwov7oh80bvlbu0lyhwy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--q02kCzgn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cwov7oh80bvlbu0lyhwy.png" alt="A visual representation of the information outlined above" width="800" height="404"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Provide the following markup as the first child of the &lt;code&gt;main&lt;/code&gt; element create the following markup:&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;class=&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;class=&lt;/span&gt;&lt;span class="s"&gt;"progress"&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;"inner"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&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;You’ll also need to apply the following styles to give the markup a base visual experience:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.wrapper&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;--size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80px&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;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--size&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="py"&gt;aspect-ratio&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="m"&gt;1&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;24px&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;24px&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;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;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="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.progress&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;--track-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16px&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="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--size&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="py"&gt;aspect-ratio&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="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;50%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.inner&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;100%&lt;/span&gt; &lt;span class="n"&gt;-&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--track-size&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="py"&gt;aspect-ratio&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="m"&gt;1&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="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--background-color&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;50%&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;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;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;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;auto&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;Most of the CSS here shouldn’t come as a shock to you, so I won’t go over it line by line, but I will touch on some of the more interesting bits.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;.wrapper&lt;/code&gt; we’re fixing the element to the top left of the screen, and using flexbox to center the children horizontally and vertically.&lt;/p&gt;

&lt;p&gt;💡 I learnt that if you want an element to share the same value for both its width and height, you just set the width and use &lt;code&gt;aspect-ratio: 1/1&lt;/code&gt;. The browser will implicitly set the height. This is a neat trick because you won’t have to define the same value twice, and it makes it easier to guarantee that the width and the height share the same value.&lt;/p&gt;

&lt;p&gt;As for the &lt;code&gt;.inner&lt;/code&gt; element, I’ve used a mix of absolute positioning and the &lt;code&gt;margin: auto&lt;/code&gt; to center it in the middle of the &lt;code&gt;.progress&lt;/code&gt; element. We’ve also deducted the &lt;code&gt;--track-size&lt;/code&gt; from the full width of the container, to ensure that it’s correctly positioned over the &lt;code&gt;.progress&lt;/code&gt; element.&lt;/p&gt;

&lt;p&gt;You won’t be able to see anything just yet, but if you add a temporary &lt;code&gt;background-color: red&lt;/code&gt; to the &lt;code&gt;.progress&lt;/code&gt; element, it should render as follows:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--oT2eHmgE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/no85l7n02c25g9eup0t4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--oT2eHmgE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/no85l7n02c25g9eup0t4.png" alt="a basic doughnut shaped indicator without any animation" width="228" height="212"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating an animated progress indicator
&lt;/h2&gt;

&lt;p&gt;Creating a scroll-driven animation of this kind requires a lot of new CSS features that you may have not used before. Instead of learning everything all at once, we’ll start by decoupling the animation from the scrolling mechanics.&lt;/p&gt;

&lt;p&gt;That way, by the end of this section, you should have the following animation that plays automatically:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dpfJs2qr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z0fmw2lad1f9wou7dvf7.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dpfJs2qr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z0fmw2lad1f9wou7dvf7.gif" alt="A progress indicator playing automatically in an infinite loop" width="720" height="720"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We’ll start by creating a new animation called &lt;code&gt;load&lt;/code&gt;:&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="k"&gt;@keyframes&lt;/span&gt; &lt;span class="n"&gt;load&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="py"&gt;--progress&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="p"&gt;}&lt;/span&gt;

    &lt;span class="err"&gt;100&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="py"&gt;--progress&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="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;All this does is move the progress along from 0 to 100 over the course of the animation. In your &lt;code&gt;.progress&lt;/code&gt; rule, add the following CSS properties:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.progress&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="err"&gt;Existing&lt;/span&gt; &lt;span class="err"&gt;rules&lt;/span&gt;

    &lt;span class="nl"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;load&lt;/span&gt; &lt;span class="n"&gt;linear&lt;/span&gt; &lt;span class="m"&gt;1s&lt;/span&gt; &lt;span class="n"&gt;infinite&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="n"&gt;conic-gradient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="m"&gt;0deg&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="m"&gt;50%&lt;/span&gt; &lt;span class="m"&gt;50%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--red&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--progress&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--black&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--progress&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 &lt;code&gt;animation&lt;/code&gt; property should be pretty straightforward, but there’s a lot going on with the &lt;code&gt;background&lt;/code&gt; rule, so let’s step through it.&lt;/p&gt;

&lt;p&gt;For starters, we’re using a &lt;code&gt;conic-gradient&lt;/code&gt; as it makes it easy for us to animate the background over 360 degrees, in the way shown in the animation above. We’re starting from the &lt;code&gt;0deg&lt;/code&gt; position, which is top and center. We’re describing where we want the center of the gradient to be using &lt;code&gt;at 50% 50%&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;conic-gradient(from 0deg at 50% 50%)&lt;/code&gt; alone would render something like the following:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LV86wtSH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wdyz4tse2hx1836lpr2u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LV86wtSH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wdyz4tse2hx1836lpr2u.png" alt="A visualisation of a radial gradient starting from the center, like a sonar radar" width="800" height="835"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hopefully I’ve made it clear why that’s the case.&lt;/p&gt;

&lt;p&gt;As for the the second and third arguments of the &lt;code&gt;conic-gradient&lt;/code&gt; function, we’re linking the &lt;code&gt;--progress&lt;/code&gt; variable (which is being calculated via the &lt;code&gt;load&lt;/code&gt; animation) to the two colors. The &lt;code&gt;--red&lt;/code&gt; is used to denote the completed progress, while the &lt;code&gt;--black&lt;/code&gt; is used to denote the remaining position. It might be confusing why they share the same &lt;code&gt;--progress&lt;/code&gt; value. The &lt;code&gt;--progress&lt;/code&gt; value for the &lt;code&gt;--red&lt;/code&gt; value denotes where the gradient stop ends, while the &lt;code&gt;--progress&lt;/code&gt; value for the &lt;code&gt;--black&lt;/code&gt; denotes where the gradient stop begins. Because it’s the last stop on the gradient, it’s implied that it ends at 100%. By setting the same &lt;code&gt;--progress&lt;/code&gt; value to both stops in the gradient, we create a hard transition between the two colors. Without doing so, our progress indicator (with a &lt;code&gt;--progress&lt;/code&gt; value set to 16%) would look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qvLQVgOo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wq9oho98fpamg94svofp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qvLQVgOo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wq9oho98fpamg94svofp.png" alt="The progress indicator where the color gradient goes slowly from red to black" width="278" height="260"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, something strange is probably happening. Instead of your progress indicator transitioning gracefully across the entire perimeter of the circle, it’s instead flashing between the black and red.&lt;/p&gt;

&lt;p&gt;Why is this happening?&lt;/p&gt;

&lt;p&gt;This is because we’re making the browser interpolate between percentage values, which is something it can’t do automatically. Even though we’ve given the &lt;code&gt;--progress&lt;/code&gt; variable a percentage value, the browser doesn’t assume that it’s always going to be a percentage value.&lt;/p&gt;

&lt;p&gt;We can solve this by telling the browser that &lt;code&gt;--progress&lt;/code&gt; will always be a percentage value. We can do this by explicitly defining the &lt;code&gt;--progress&lt;/code&gt; property using the &lt;code&gt;@property&lt;/code&gt; CSS rule. Just add the following to the top-level of your CSS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@property&lt;/span&gt; &lt;span class="n"&gt;--progress&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;syntax&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;'&amp;lt;percentage&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;inherits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;initial-value&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’re telling the browser that &lt;code&gt;--progress&lt;/code&gt; should only support percentage values and that the initial value is 0%. We’re also not interested in having the custom element inherit its value.&lt;/p&gt;

&lt;p&gt;Finally, I don’t quite like the use of the &lt;code&gt;--black&lt;/code&gt; variable to signify empty progress. It looks too stark. I’d like to create a lighter shade created from the black to ensure a more homogenous visual palette. This is something we can easily achieve using the &lt;code&gt;color-mix()&lt;/code&gt; CSS function.&lt;/p&gt;

&lt;p&gt;Jump back up to the &lt;code&gt;:root&lt;/code&gt; CSS rule and add the following variable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="err"&gt;your&lt;/span&gt; &lt;span class="err"&gt;other&lt;/span&gt; &lt;span class="err"&gt;CSS&lt;/span&gt; &lt;span class="err"&gt;variables&lt;/span&gt;

    &lt;span class="py"&gt;--grey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;color-mix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;srgb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--black&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nb"&gt;transparent&lt;/span&gt; &lt;span class="m"&gt;60%&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 &lt;code&gt;color-mix&lt;/code&gt; function lets us mix two colors together. In this case, we’re mixing the color stored in our black variable with some transparency, which will result in a partially see-through grey color. You’ll need to replace the reference to the &lt;code&gt;--black&lt;/code&gt; variable in the &lt;code&gt;conic-gradient&lt;/code&gt; function with &lt;code&gt;--grey&lt;/code&gt; to see the color change in effect.&lt;/p&gt;

&lt;p&gt;Now that we’ve defined our custom property, the browser will be able to interpolate the correct values during the entire animation, so it should now transition smoothly from start to finish.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uCJw80U2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sujd1h1wgnpv0qu9hhq9.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uCJw80U2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sujd1h1wgnpv0qu9hhq9.gif" alt="A progress indicator playing automatically in an infinite loop" width="720" height="720"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Scroll driven animations
&lt;/h2&gt;

&lt;p&gt;The next stage of our animation journey is to tie our animation to the scrolling of the page.&lt;/p&gt;

&lt;p&gt;This should only take us a couple of lines of CSS.&lt;/p&gt;

&lt;p&gt;You’ll need to do two things. First adjust the &lt;code&gt;animation&lt;/code&gt; property in your &lt;code&gt;.progress&lt;/code&gt; class to remove the &lt;code&gt;infinite&lt;/code&gt; value, and to change the duration from &lt;code&gt;1s&lt;/code&gt; to &lt;code&gt;1ms&lt;/code&gt;. We can’t remove the value altogether because Firefox needs it for scroll animations to work.&lt;/p&gt;

&lt;p&gt;Next update your &lt;code&gt;.progress&lt;/code&gt; class to include the following&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.progress&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="err"&gt;other&lt;/span&gt; &lt;span class="err"&gt;CSS&lt;/span&gt; &lt;span class="err"&gt;properties&lt;/span&gt;

    &lt;span class="py"&gt;animation-timeline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;scroll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nearest&lt;/span&gt; &lt;span class="nb"&gt;block&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 &lt;code&gt;animation-timeline&lt;/code&gt; property tells the browser to tie the progress of the animation with a specific timeline. In this case it’s the scroll timeline, which we specify using the &lt;code&gt;scroll&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;You can see I’m providing two arguments to &lt;code&gt;scroll()&lt;/code&gt;, &lt;code&gt;nearest&lt;/code&gt; and &lt;code&gt;block&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;nearest&lt;/code&gt; value is used to tie the animation to the nearest ancestor that has a scrollbar, in this case it’s the document. If you’re certain that you only ever want to tie the animation to the document’s scrollbar, then you can use swap out &lt;code&gt;nearest&lt;/code&gt; for &lt;code&gt;root&lt;/code&gt; instead.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;block&lt;/code&gt; property denotes the axis that we want to tie our animation to. For most cases this will be the vertical scrollbar, but for vertical writing modes, this will be the horizontal scrollbar.&lt;/p&gt;

&lt;p&gt;Now that you’ve hooked up the animation to your page’s scroll, you should be able to scroll up and down the page and watch how your animation changes accordingly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PaQ46VB2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qqhxp66wddvl381kk8q4.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PaQ46VB2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qqhxp66wddvl381kk8q4.gif" alt="The basic progress indicator whose animation is tied to the progress of the page scroll" width="800" height="705"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Progressively enhancing your scroll animation
&lt;/h2&gt;

&lt;p&gt;While it’s exciting to use these new features in the browser, the &lt;code&gt;animation-timeline&lt;/code&gt; property doesn’t have universal support across browsers yet. It’s still very new in Chrome, and it’s only available in Firefox behind a feature flag. If you try opening the code in Firefox, you’ll notice that the progress ring just appears with a finished animation.&lt;/p&gt;

&lt;p&gt;In cases like this, it’s important to set up a solid base experience for all browsers, and then &lt;em&gt;progressively enhance&lt;/em&gt; your webpage with the newer features on compatible browsers. Because the progress indicator isn’t critical for the application to function, we can just hide it away if the browser doesn’t support the &lt;code&gt;animation-timeline&lt;/code&gt; property.&lt;/p&gt;

&lt;p&gt;We can do this by moving our &lt;code&gt;.wrapper&lt;/code&gt;, &lt;code&gt;.progress&lt;/code&gt;, and &lt;code&gt;.inner&lt;/code&gt; classes within CSS’s &lt;code&gt;@supports&lt;/code&gt; at-rule, like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@supports&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;animation-timeline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;scroll&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;.wrapper&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nc"&gt;.progress&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nc"&gt;.inner&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;Doing so ensures that if the browser doesn’t support &lt;code&gt;scroll()&lt;/code&gt;, then it will ignore all of the styles contained within the rule.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding the indicator thumb
&lt;/h2&gt;

&lt;p&gt;The final thing for us to add is a cool little indicator thumb, to both give our progress indicator a little more visual interest and to also let us play with the swanky CSS trigonometric functions.&lt;/p&gt;

&lt;p&gt;To create the indicator thumb, start by writing the following CSS inside of the &lt;code&gt;@supports&lt;/code&gt; block:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.progress&lt;/span&gt;&lt;span class="nd"&gt;::after&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;--radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="py"&gt;--track-offset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--track-size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="m"&gt;4&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;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="py"&gt;aspect-ratio&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="m"&gt;1&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="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--track-size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="m"&gt;2&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="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--red-dark&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;50%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;50%&lt;/span&gt; &lt;span class="n"&gt;-&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--track-offset&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;50%&lt;/span&gt; &lt;span class="n"&gt;-&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--track-offset&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1.5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates a new pseudo-element off of the &lt;code&gt;.progress&lt;/code&gt; class, and gives it it’s visual appearance. Once added, the indicator thumb should live in the center progress element. We’re using the &lt;code&gt;--track-offset&lt;/code&gt; variable to position the thumb correctly by taking into consideration the dimensions of the track.&lt;/p&gt;

&lt;p&gt;⚠️ I’m also increasing the size of the thumb to using &lt;code&gt;scale()&lt;/code&gt; so that its size in the DOM is still relative to the &lt;code&gt;--size&lt;/code&gt; variable. This just means a little less maths for us to worry about when setting the value for &lt;code&gt;--track-offset&lt;/code&gt;. Using &lt;code&gt;scale()&lt;/code&gt; makes it easy to change the size of the element without causing a shift in the DOM.&lt;/p&gt;

&lt;p&gt;The next step is to use the &lt;code&gt;color-mix()&lt;/code&gt; function again to create a dark red from the base red color. Add the following to your &lt;code&gt;:root&lt;/code&gt; rule.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="err"&gt;your&lt;/span&gt; &lt;span class="err"&gt;other&lt;/span&gt; &lt;span class="err"&gt;CSS&lt;/span&gt; &lt;span class="err"&gt;variables&lt;/span&gt;

    &lt;span class="py"&gt;--red-dark&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;color-mix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;srgb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--red&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--black&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="m"&gt;60%&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;Your progress indicator should look less like a UI widget and more like a dartboard:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4eUp_-7A--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lgzfgx48pn1niryq911u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4eUp_-7A--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lgzfgx48pn1niryq911u.png" alt="the thumb is in the center of the progress indicator" width="268" height="248"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s position the thumb on to the track.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.progress&lt;/span&gt;&lt;span class="nd"&gt;::before&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="err"&gt;rest&lt;/span&gt; &lt;span class="err"&gt;of&lt;/span&gt; &lt;span class="err"&gt;properties&lt;/span&gt;

    &lt;span class="py"&gt;translate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--radius&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;-&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--track-offset&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="err"&gt;*&lt;/span&gt; &lt;span class="n"&gt;cos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--angle&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
      &lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--radius&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;-&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--track-offset&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="err"&gt;*&lt;/span&gt; &lt;span class="n"&gt;sin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--angle&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;This is probably the gnarliest piece of CSS in this entire article. It isn’t anywhere near as complex if we break it down in half. Here’s the first half:&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;calc&lt;/span&gt;&lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="nt"&gt;var&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;--radius&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;-&lt;/span&gt; &lt;span class="nt"&gt;var&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;--track-offset&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nt"&gt;cos&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;var&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;--angle&lt;/span&gt;&lt;span class="o"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This uses a little trigonometry to calculate the position of the thumb based on the current angle (which will be tied to the scroll progress) and the radius of the circle. The &lt;code&gt;cos()&lt;/code&gt; function is used to determine the horizontal value of the position.&lt;/p&gt;

&lt;p&gt;The second half of the value is identical, except we’re using the &lt;code&gt;sin()&lt;/code&gt; function to determine the vertical position of the indicator:&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;calc&lt;/span&gt;&lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="nt"&gt;var&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;--radius&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;-&lt;/span&gt; &lt;span class="nt"&gt;var&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;--track-offset&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nt"&gt;sin&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;var&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;--angle&lt;/span&gt;&lt;span class="o"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;⚠️ I’m not going to use this article as an introduction into trigonometry, but I can point you in the direction of some amazing resources:&lt;/p&gt;


&lt;li&gt;
  &lt;a href="https://web.dev/articles/css-trig-functions"&gt;Trigonometric functions in CSS&lt;/a&gt;
&lt;/li&gt;
&lt;br&gt;
&lt;li&gt;
  &lt;a href="https://tympanus.net/codrops/2021/06/04/trigonometry-in-css-and-javascript-beyond-triangles/"&gt;Trigonometric functions in CSS and JavaScript: Beyond Triangles&lt;/a&gt;
&lt;/li&gt;

&lt;p&gt;You may have noticed that I’ve specified a variable, &lt;code&gt;--angle&lt;/code&gt; that I haven’t yet defined. Because we’ll be animating the &lt;code&gt;--angle&lt;/code&gt; we need to explicitly define it using the &lt;code&gt;@property&lt;/code&gt; rule, much like we did for the &lt;code&gt;--progress&lt;/code&gt; property. The only difference is that we’ll need to specify a different syntax value. Instead of &lt;code&gt;&amp;lt;percentage&amp;gt;&lt;/code&gt; the value will need to be &lt;code&gt;&amp;lt;angle&amp;gt;&lt;/code&gt; .&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="k"&gt;@property&lt;/span&gt; &lt;span class="n"&gt;--angle&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;syntax&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;'&amp;lt;angle&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;inherits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;initial-value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;-90deg&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;By setting the initial value to &lt;code&gt;-90deg&lt;/code&gt; we ensure that thumb is placed at the 12 o’clock position on the progress indicator.&lt;/p&gt;

&lt;p&gt;Your indicator should now look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GSL6pimL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e35g8l7xic1159dszolj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GSL6pimL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e35g8l7xic1159dszolj.png" alt="The thumb is positioned at the top center of the progress indicator" width="266" height="254"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The next step is to create the animation for the thumb and then bind the animation timeline to the scroll position of the page.&lt;/p&gt;

&lt;p&gt;Let’s start by creating a new animation:&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="k"&gt;@keyframes&lt;/span&gt; &lt;span class="n"&gt;rotate&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="py"&gt;--angle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;-90deg&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="err"&gt;100&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="py"&gt;--angle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;270deg&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;Over the course of the entire animation, the thumb will rotate 360 degrees, performing a full revolution over the progress element.&lt;/p&gt;

&lt;p&gt;Finally we need to add the following two properties to the thumb:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.progress&lt;/span&gt;&lt;span class="nd"&gt;::after&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="err"&gt;other&lt;/span&gt; &lt;span class="err"&gt;CSS&lt;/span&gt; &lt;span class="err"&gt;properties&lt;/span&gt;

    &lt;span class="nl"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rotate&lt;/span&gt; &lt;span class="n"&gt;linear&lt;/span&gt; &lt;span class="m"&gt;1ms&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;animation-timeline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;scroll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nearest&lt;/span&gt; &lt;span class="nb"&gt;block&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;Doing so applies the rotate animation to our thumb and binds it to the scroll position.&lt;/p&gt;

&lt;p&gt;Everything should now work flawlessly:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--G0LUYZhj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/minmc8ty3wwtfyq2aui6.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--G0LUYZhj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/minmc8ty3wwtfyq2aui6.gif" alt="The completed progress indicator UI" width="800" height="498"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;I created this progress indicator specifically to become more familiar with the amazing tools that CSS has shipped in the last couple years. Hopefully you learnt just as much from this lesson as I did making it.&lt;/p&gt;

&lt;p&gt;There were other CSS features I wanted to explore, like &lt;code&gt;popover&lt;/code&gt; and &lt;code&gt;:has&lt;/code&gt; but I couldn’t find a way to fit them in with this animation. If people find this article interesting, I might try and create more little changes to the Component Odyssey platform, using cutting-edge CSS features.&lt;/p&gt;

&lt;p&gt;By wary, that because a lot of the CSS features I’ve covered are still very new, check the browser support before using them in production. If they’re not supported in one or more browsers, but you’re desperate to use them, then use a &lt;strong&gt;progressive enhancement&lt;/strong&gt; strategy to ensure that those with compatible browsers get the full experience, while still offering users of unsupported browsers a solid baseline experience.&lt;/p&gt;

&lt;p&gt;If you enjoyed this article, and would love to learn more about Component Odyssey or other cool web development tips, then consider &lt;a href="https://component-odyssey.com/subscribe"&gt;subscribing to the newsletter&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://developer.chrome.com/docs/css-ui/scroll-driven-animations#getting_practical_with_scroll_progress_timelines"&gt;Getting practical with scroll progress timelines&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/afif/we-can-finally-animate-css-gradient-kdk"&gt;We can finally animate css gradients&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://codepen.io/LukyVj/pen/rNqvowZ"&gt;Fitness inspired loaders&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://drafts.css-houdini.org/css-properties-values-api-1/#at-property-rule"&gt;MDN: @property at-rule&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/animation-timeline"&gt;MDN: Animation Timeline&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Navigating Your Component Journey with Component Odyssey</title>
      <dc:creator>Andrico</dc:creator>
      <pubDate>Tue, 28 Nov 2023 13:00:00 +0000</pubDate>
      <link>https://dev.to/andrico1234/navigating-your-component-journey-with-component-odyssey-ajl</link>
      <guid>https://dev.to/andrico1234/navigating-your-component-journey-with-component-odyssey-ajl</guid>
      <description>&lt;h2&gt;
  
  
  The Complexities of Building Your Own Component Library
&lt;/h2&gt;

&lt;p&gt;At some point, every web developer has thought about creating their own component library, and for good reason.&lt;/p&gt;

&lt;p&gt;Building a library is an opportunity to express your creativity, learn to develop more accessible web experiences, contribute to the open source community, upskill yourself for a better-paid role, or simply become a more well-rounded web developer.&lt;/p&gt;

&lt;p&gt;A component library is also a valuable tool to have in your team, as reusing the same components over several projects is a great way of saving your team lots of time and effort.&lt;/p&gt;

&lt;p&gt;But building a component library is no easy task in part due to how complex the web development landscape has gotten over the years. With so many frameworks and runtimes to choose from, how do you know which is the best to pick for your component library? Picking one may very well lock your components out from being used with others.&lt;/p&gt;

&lt;p&gt;Ultimately, you want your end-users to have enjoyable web experiences, you want your library consumers to easily build interfaces with your library, and you want a smooth development experience to build, test, and publish your library.&lt;/p&gt;

&lt;p&gt;That’s what Component Odyssey will teach you to do.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing Component Odyssey
&lt;/h2&gt;

&lt;p&gt;Component Odyssey will help you navigate your component library journey.&lt;/p&gt;

&lt;p&gt;In Component Odyssey, I’ll be teaching you how to build a component library with a minimal tech stack. You’ll learn how to avoid locking yourself into the shiny framework of the month, and you’ll write components that work across any JavaScript framework.&lt;/p&gt;

&lt;p&gt;So why should you embark on your Component Odyssey?&lt;/p&gt;

&lt;h3&gt;
  
  
  You’ll become a more future-proof web developer
&lt;/h3&gt;

&lt;p&gt;The core technologies introduced in Component Odyssey are baked into the browser, meaning that as you become more familiar with these tools, you’ll also become more familiar with building for the browser as a whole. The skills you learn in this course will serve you in almost any front-end project you work on in the future.&lt;/p&gt;

&lt;h3&gt;
  
  
  You’ll build components that your users will love
&lt;/h3&gt;

&lt;p&gt;You won’t just be writing components in Component Odyssey, you’ll learn how to style, test, type, and publish them. These are all important in ensuring that the components you ship are robust and user-friendly.&lt;/p&gt;

&lt;h3&gt;
  
  
  You’ll boost your career opportunities
&lt;/h3&gt;

&lt;p&gt;Building a component library is an excellent way to contribute to the open-source community, which improves your chances of working on other cool projects, or landing some wonderful job roles. Just sharing the library you’ve built from scratch with a prospective job employer will help you stand out from the crowd of applicants.&lt;/p&gt;

&lt;p&gt;I did just this when I interviewed for my current job, and the interviewers loved my component library:&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%2Fkk4x5523g66n8vsvhlcp.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%2Fkk4x5523g66n8vsvhlcp.png" alt="Documentation for A2K component library"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  You’ll learn to do more with less
&lt;/h3&gt;

&lt;p&gt;The components you’ll be building in Component Odyssey are built using web components, the browser’s built-in way to create reusable components. You don’t need a framework to write and publish them, and as a result, you’ll be able to deliver components that work across the web.&lt;/p&gt;

&lt;p&gt;Web components aren’t new or radical, loads of large companies use web components to power their web experiences, like Microsoft, Adobe, and NASA.&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%2F1xqloe33c4g0nlx26p56.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%2F1xqloe33c4g0nlx26p56.png" alt="Adobe software, which uses web components"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You’ll also be introduced to other tools and technologies that lean into the browser’s built-in capabilities, which means that you, and your library consumers, can avoid complex build systems to run your code.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Component Odyssey platform
&lt;/h2&gt;

&lt;p&gt;Component Odyssey is a self-paced and highly interactive course, so there’ll be plenty of hands-on coding for you to do. There are also no deadlines, so you can go through the entire course on your own time.&lt;/p&gt;

&lt;p&gt;Every lesson is accompanied by written content and a video, and the course is jam-packed with code examples, exercises, and projects.&lt;/p&gt;

&lt;p&gt;Most lessons contain some interactive runnable code like the one below:&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%2Fuvstzumq1j744xv546b9.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%2Fuvstzumq1j744xv546b9.png" alt="Code examples embedded in the lesson"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Other lessons contain in-depth exercises for you to download and run locally on your computers.&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%2F1x04os6df1ji9qxy3xvh.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%2F1x04os6df1ji9qxy3xvh.png" alt="Example for a downloadable exercise, with interactive element, and unit tests."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Most importantly, throughout the entire course, you’ll be slowly building out a functioning component library, with multiple components, a testing strategy, and some handy workflow automation.&lt;/p&gt;

&lt;p&gt;You won’t be alone either, starting Component Odyssey will also give you access to a Discord community. You’ll be able to share the amazing things you’ve built, get help on any tough challenges you’re facing, and meet other developers on their journeys.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who should take Component Odyssey?
&lt;/h2&gt;

&lt;p&gt;You don’t need to be an expert web developer to take this course, it’s designed for anyone with some working HTML, CSS, and JavaScript experience. If you’ve built a few interactive websites and have a few months of web development experience, then you’re golden.&lt;/p&gt;

&lt;p&gt;Component Odyssey doesn’t cover the absolute basics of HTML, CSS, and JavaScript, but it does build on top of those browser fundamentals, so you can build great components without a framework.&lt;/p&gt;

&lt;p&gt;Is Component Odyssey worth taking if you’re a seasoned developer? Absolutely, there’ll still be so much for you to gain, as you’ll be diving deep into web components, and you’ll learn how to style, test, and publish them.&lt;/p&gt;

&lt;h2&gt;
  
  
  About me
&lt;/h2&gt;

&lt;p&gt;As for me, I’m Andrico, a web developer based in London. I’ve been a professional web developer for the last 7 years and have worked across a bunch of startups. I’m currently building design-to-code automation and design system automation at Anima.&lt;/p&gt;

&lt;p&gt;In previous roles, I was a front-end champion, where I ran workshops about front-end development, web accessibility, and front-end testing.&lt;/p&gt;

&lt;p&gt;I’ve also built a few personal projects on the side like Cali Skills, a bodyweight fitness tracker, and Handstand Journey, a popular mobile application designed as a fun introduction to the exciting world of arm balances.&lt;/p&gt;

&lt;p&gt;I’m also the creator of a2k, the Windows 2000-inspired web component library. I’ve also contributed to several open-source projects, most notably the Open UI, where I led the site rewrite from Gatsby to Astro.&lt;/p&gt;

&lt;p&gt;Component Odyssey is everything I’ve learnt about web development and component library development condensed down into a fun and highly interactive course.&lt;/p&gt;

&lt;p&gt;Fun fact: Before starting my career as a web developer, I worked in the family chip shop in South-East London (it’s still going strong, so stop by for some fish and chips!).&lt;/p&gt;

&lt;h2&gt;
  
  
  Register your interest
&lt;/h2&gt;

&lt;p&gt;Component Odyssey is still in development, with more release details coming out in the following months. If you’d like to be notified about Component Odyssey news, including beta testing details + launch discounts, then please &lt;a href="https://component-odyssey.com/subscribe" rel="noopener noreferrer"&gt;register your interest&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>programming</category>
    </item>
    <item>
      <title>Gather User Feedback On Any Website with Hermes WC</title>
      <dc:creator>Andrico</dc:creator>
      <pubDate>Mon, 13 Nov 2023 19:26:26 +0000</pubDate>
      <link>https://dev.to/andrico1234/hermes-wc-a-feedback-widget-built-on-web-components-198d</link>
      <guid>https://dev.to/andrico1234/hermes-wc-a-feedback-widget-built-on-web-components-198d</guid>
      <description>&lt;p&gt;Nothing’s more important for indie developers than gathering user feedback, especially for early-stage projects that are likely to undergo massive changes regularly. At the same time, many developers don’t need the kind of enterprise-level feature set that many of the most well-known customer platforms offer.&lt;/p&gt;

&lt;p&gt;For the last few months, I’ve been creating Component Odyssey, a course designed to help developers build and publish components that work everywhere. As I’m gearing up to open my platform to beta testers I need a free, quick, and easy mechanism to gather user feedback.&lt;/p&gt;

&lt;p&gt;I want an unintrusive widget that I could drop into my page which students could use to send feedback at any point during the course to flag bugs and unclear course content. Given that the Component Odyssey platform is built on top of SvelteKit, it would be easy for me to create a Svelte component that fires data to an endpoint.&lt;/p&gt;

&lt;p&gt;But I wanted to practice what I teach, and a form widget feels like a great candidate for a web component as the browser already has great support for building forms. On top of that, a form widget isn’t a novel piece of functionality and certainly shouldn’t rely on a specific framework to work. The idea of writing a simple, but very useful component, that I can’t then use in my React and VanillaJS projects feels like a waste of energy.&lt;/p&gt;

&lt;p&gt;That’s what led me to build &lt;a href="https://github.com/andrico1234/hermes-wc"&gt;Hermes WC&lt;/a&gt;. With Hermes WC, you can easily add an interactive form that dispatches the form data to a location of your choice.&lt;/p&gt;

&lt;p&gt;Here’s a demo of Hermes WC in action, sending feedback to Discord, Slack, and an HTTP endpoint.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6uaQ4rFa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dlp8p8798urfqmbsiels.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6uaQ4rFa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dlp8p8798urfqmbsiels.gif" alt="Hermes WC in action" width="600" height="309"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So let’s see how you can add Hermes WC to your own projects&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Installing Hermes WC
&lt;/h2&gt;

&lt;p&gt;Begin by installing Hermes WC:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;npm install hermes-wc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2: Adding the Hermes WC markup
&lt;/h2&gt;

&lt;p&gt;Once you’ve done that, you can update your webpage with the following markup:&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;hermes-wrapper&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"hermes-stack"&lt;/span&gt; &lt;span class="na"&gt;data-hermes=&lt;/span&gt;&lt;span class="s"&gt;"form"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;sl-input&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"feedback"&lt;/span&gt; &lt;span class="na"&gt;label=&lt;/span&gt;&lt;span class="s"&gt;"Feedback"&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"e.g., Not clear enough"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/sl-input&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;hermes-score-input&lt;/span&gt;
            &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"score"&lt;/span&gt;
            &lt;span class="na"&gt;label=&lt;/span&gt;&lt;span class="s"&gt;"Score"&lt;/span&gt;
            &lt;span class="na"&gt;score-count=&lt;/span&gt;&lt;span class="s"&gt;"5"&lt;/span&gt;
            &lt;span class="na"&gt;start-helper-text=&lt;/span&gt;&lt;span class="s"&gt;"Low Score"&lt;/span&gt;
            &lt;span class="na"&gt;end-helper-text=&lt;/span&gt;&lt;span class="s"&gt;"High score"&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;gt;&amp;lt;/hermes-score-input&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;"hermes-row"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;sl-button&lt;/span&gt; &lt;span class="na"&gt;data-hermes=&lt;/span&gt;&lt;span class="s"&gt;"close"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Close&lt;span class="nt"&gt;&amp;lt;/sl-button&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;sl-button&lt;/span&gt; &lt;span class="na"&gt;variant=&lt;/span&gt;&lt;span class="s"&gt;"primary"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Submit&lt;span class="nt"&gt;&amp;lt;/sl-button&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;p&lt;/span&gt; &lt;span class="na"&gt;data-hermes=&lt;/span&gt;&lt;span class="s"&gt;"helper-text"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/hermes-wrapper&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There’s heaps of stuff going on here, so let’s break it down, element by element.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hermes Wrapper&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the component that’s responsible for handling the layout of the Hermes form. It mainly handles the form’s open and close states, while also placing a button on the right hand side of the page.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Form&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the form element that’s in charge of listening and reacting to the submit event. A form element with the attribute &lt;code&gt;data-hermes="form"&lt;/code&gt; is required.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SL Input&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Because Hermes WC uses a standard HTML form element, we can use any old HTML form controls as children and their values will be captured in the submit event. As a result, I’m using the Shoelace library to render some pre-styled and accessible components.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fic0nNO7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kxvoyb89bl5o0z9ilkjf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fic0nNO7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kxvoyb89bl5o0z9ilkjf.png" alt="A Shoelace input component" width="586" height="192"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hermes Score Input&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is an additional web component that Hermes WC provides. It renders a horizontal radio button group with some attributes for helper text.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--M2w3S4Yq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/76u3ozgbmr2x67q029jg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--M2w3S4Yq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/76u3ozgbmr2x67q029jg.png" alt="The Hermes WC Score Input component" width="591" height="242"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SL Button&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is a button provided to us by Shoelace. The button on the left has a &lt;code&gt;data-hermes="close"&lt;/code&gt; attribute, which is used to close the feedback widget.&lt;/p&gt;

&lt;p&gt;The button on the right has an attribute &lt;code&gt;type="submit"&lt;/code&gt; and is used to submit the form when it’s clicked.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BVJwy5Mv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8vqebrlrz9390gkmx9zp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BVJwy5Mv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8vqebrlrz9390gkmx9zp.png" alt="Two Shoelace button components" width="586" height="134"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Whatever else you want…&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Like I mentioned earlier, because Hermes WC works with HTML forms, you can use any HTML form control to build your form.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Import the styles into your page
&lt;/h2&gt;

&lt;p&gt;To apply the default styles to Hermes WC, go ahead and import the styles into your project:&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;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;link&lt;/span&gt; &lt;span class="na"&gt;rel&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"path/to/node_modules/hermes-wc/src/style.css"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 4: Initializing the Hermes WC form
&lt;/h2&gt;

&lt;p&gt;With the markup written, you’ll need to add a small splash of JavaScript to initialize the form.&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;DiscordSubmissionAdapter&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;hermes-wc/src/submission-adapters/discord.js&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;initializeHermesForm&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;hermes-wc/src/index.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="nx"&gt;initializeHermesForm&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;submissionAdapters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;DiscordSubmissionAdapter&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;webhookUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;your webhook url&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;})],&lt;/span&gt;
  &lt;span class="na"&gt;submissionCompleteCallback&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&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;// handle the response }&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running &lt;code&gt;initializeHermesForm&lt;/code&gt; will do a few things under-the-hood, like set up event listeners and register the web components.&lt;/p&gt;

&lt;p&gt;You can then provide one or more &lt;strong&gt;&lt;em&gt;submission adapters&lt;/em&gt;&lt;/strong&gt; which send your data to a destination of your choice. As of now, Hermes WC supports sending feedback to Slack, Discord, or an HTTP GET endpoint. You can even create your own adapters.&lt;/p&gt;

&lt;p&gt;You can also pass a function through to the &lt;code&gt;submissionCompleteCallback&lt;/code&gt; option, which gets fired when all of the submission requests are settled. You’ll be given the response of all the promises, which you can use to display a success or error message to your users.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;I hope you find a tool like Hermes WC useful. I plan to open source a bunch of the tools I create for Component Odyssey. Other tools include &lt;a href="https://github.com/andrico1234/sandpack-lit"&gt;Sandpack Lit&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you’d like to learn how to build your own components that work anywhere, regardless of framework, then please register your interest for my course, &lt;a href="https://component-odyssey.com/"&gt;Component Odyssey&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Writing Components That Work In Any Frontend Framework</title>
      <dc:creator>Andrico</dc:creator>
      <pubDate>Mon, 06 Nov 2023 13:00:00 +0000</pubDate>
      <link>https://dev.to/andrico1234/writing-components-that-work-in-any-frontend-framework-2dhp</link>
      <guid>https://dev.to/andrico1234/writing-components-that-work-in-any-frontend-framework-2dhp</guid>
      <description>&lt;p&gt;The browser has a built-in way of writing reusable components in the form of &lt;strong&gt;web components&lt;/strong&gt;. They’re an excellent choice for building interactive, and reusable components that work in any frontend framework. With that said, writing highly interactive and robust web components isn’t simple. They require a lot of boilerplate and feel much less intuitive than the components you may have written in frameworks like React, Svelte, and Vue.&lt;/p&gt;

&lt;p&gt;In this article, I’ll give you an example of an interactive component written as a web component, and then refactor it using a library that softens the edges and removes heaps of boilerplate.&lt;/p&gt;

&lt;p&gt;Don’t sweat if you’re not familiar with web components. In the next section, I’ll do a (very brief and limited) overview of what web components are, and what they’re made out of. If you have some basic experience with them, you can skip the next section.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are web components?
&lt;/h2&gt;

&lt;p&gt;Before web components, the browser didn’t have a standard way of writing reusable components. Many libraries solve this problem, but they often run into limitations like performance, interoperability, and issues with web standards.&lt;/p&gt;

&lt;p&gt;They’re a technology made up of 3 different browser features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Custom elements&lt;/li&gt;
&lt;li&gt;Shadow DOM&lt;/li&gt;
&lt;li&gt;HTML Templates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We’ll do a quick crash course of these technologies, but it’s by no means a comprehensive breakdown.&lt;/p&gt;

&lt;h3&gt;
  
  
  Custom Elements
&lt;/h3&gt;

&lt;p&gt;With Custom Elements you can author your own custom HTML elements that you can reuse across your site. They can be as simple as text, images, or visual decorations. You can push them further and build interactive components, complex widgets, or entire web applications.&lt;/p&gt;

&lt;p&gt;You’re not just limited to using them in your projects, but you can publish them and allow other developers to use them on their sites.&lt;/p&gt;

&lt;p&gt;Here are some of the reusable components from my library A2K. You can see that they come in all shapes and sizes, and have a range of different functionalities. Using them in your projects is similar to using any old HTML element.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IlxP7s0---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v8f1dvq8qeb657nzx4n1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IlxP7s0---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v8f1dvq8qeb657nzx4n1.png" alt="A small collection of web components from the A2K library" width="800" height="651"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here’s how you’d use the progress element in your project:&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="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Quick Start&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;charset=&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
        &lt;span class="c"&gt;&amp;lt;!-- Use web components in your HTML like regular built-in elements. --&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;a2k-progress&lt;/span&gt; &lt;span class="na"&gt;progress=&lt;/span&gt;&lt;span class="s"&gt;"50"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

        &lt;span class="c"&gt;&amp;lt;!-- a2k web components use standard JavaScript modules. --&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"module"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://cdn.jsdelivr.net/npm/@a2000/progress@0.0.5/lib/src/a2k-progress.js&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;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you’ve imported the third-party scripts, you can start using the component, &lt;code&gt;a2k-progress&lt;/code&gt; in this case, just like any other HTML element.&lt;/p&gt;

&lt;p&gt;If you’re building your own web components, there’s virtually no limit to how complex you can make your custom elements. I recently created a web component that renders a CodeSandbox code editor in the browser. And because it’s a web component, you can use it in any framework you like! If you’d like to learn a little more about that, &lt;a href="https://component-odyssey.com/articles/00-sandpack-lit-universal"&gt;you can read more here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Shadow DOM
&lt;/h3&gt;

&lt;p&gt;If you have a working knowledge of CSS, you’ll know that vanilla CSS is scoped globally. Writing something like this in your global.css:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;p&lt;/span&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="no"&gt;tomato&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;Will give all &lt;code&gt;p&lt;/code&gt; elements a nice orange/red color, assuming that no other, more specific CSS selectors are applied to a &lt;code&gt;p&lt;/code&gt; element.&lt;/p&gt;

&lt;p&gt;Take this select menu for example:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--aKBslrls--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9s0qvhtdgbu73xljke6a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aKBslrls--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9s0qvhtdgbu73xljke6a.png" alt="A select menu component with a visual design reminiscent of the old Windows operating systems" width="800" height="388"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It has a distinct character which is driven by the visual design. You might want to use this component, but if your global styles affect things like the font family, the color, or the font size, it could cause issues with the appearance of the component:&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;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;body&lt;/span&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="no"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;12px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;system-ui&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;/style&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;a2k-select&amp;gt;&amp;lt;/a2k-select&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;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--C2nPF764--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qq3qw5ebvzi49c672w3c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--C2nPF764--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qq3qw5ebvzi49c672w3c.png" alt="The same select menu, but with a lot of its defining characteristics overridden by global CSS." width="800" height="346"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is where the Shadow DOM comes in. The Shadow DOM is an encapsulation mechanism that prevents the rest of the DOM from interfering with your web component, this ensures that the global styles of the web application don’t interfere with any components that you consume. It also means that component library developers can author their component with the confidence that they’ll look and behave as expected across different web applications.&lt;/p&gt;

&lt;p&gt;There’s a lot more nuance when it comes to the Shadow DOM, as well as other features that we’re not going to touch on in this article. If you’d like to learn more about web components though, I have an entire course (&lt;a href="https://component-odyssey.com/"&gt;Component Odyssey&lt;/a&gt;) dedicated to teaching you how to build reusable components that work in any framework.&lt;/p&gt;

&lt;h3&gt;
  
  
  HTML Templates
&lt;/h3&gt;

&lt;p&gt;The last feature in our whistle-stop tour of web components features is HTML Templates.&lt;/p&gt;

&lt;p&gt;What makes this HTML element different from other elements, is that the browser doesn’t render its content to the page. If you were to write the following HTML you wouldn’t see the text “I’m a header” displayed on the page:&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;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;template&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;I'm a header&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of being used to render the content directly, the content of the template is designed to be copied. The copied template can then be used to render content to the page. You can think of the template element much like the template for a 3D print. The template isn’t a physical entity, but it’s used to create real-life clones.&lt;/p&gt;

&lt;p&gt;You would then reference the template element in your web component, clone it, and render the clone as the markup for your component.&lt;/p&gt;

&lt;p&gt;I won’t spend any more time on these web component features, but you’ve probably already noticed that to write vanilla web components, there are a lot of new browser features that you need to know and understand. You’ll see in the next section that the mental model for building web components doesn’t feel as streamlined as it does for other component frameworks.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Basic Web Component
&lt;/h2&gt;

&lt;p&gt;Now that we’ve briefly covered the fundamental technologies powering a web component, here’s how to build a &lt;em&gt;hello world&lt;/em&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;template&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;template&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;p&amp;gt;Hello World&amp;lt;/p&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;HelloWorld&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;HTMLElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;super&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;attachShadow&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;open&lt;/span&gt;&lt;span class="dl"&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;shadowRoot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cloneNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;customElements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hello-world&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;HelloWorld&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the most simple component one could write, but there’s already so much going on. For someone completely new to web components, and without the background knowledge I provided above, they’re going to be left with a lot of questions, and a lot of confusion.&lt;/p&gt;

&lt;p&gt;For me, there are at least two key reasons why web components can be challenging to write, at least within the context of the hello world examples.&lt;/p&gt;

&lt;h3&gt;
  
  
  The markup is decoupled from the component logic
&lt;/h3&gt;

&lt;p&gt;In many frameworks, the markup of the component is often treated as a first-class citizen. It’s often the content that gets returned from the component function, or has direct access to the component’s state, or has built-in utilities to help manipulate markup (like loops, conditionals, etc).&lt;/p&gt;

&lt;p&gt;This isn’t the case for web components. In fact, the markup is often defined outside of the component’s class. There’s also no built-in way for the template to reference the current state of the component. This becomes a cumbersome limitation as the complexity of a component grows.&lt;/p&gt;

&lt;p&gt;In the world of frontend, components are designed to help developers reuse markup in several pages. As a result, the markup and the component logic are inextricably linked, and so they should be colocated with one another.&lt;/p&gt;

&lt;h3&gt;
  
  
  Writing a web component requires understanding all of its underlying technologies
&lt;/h3&gt;

&lt;p&gt;As we saw above, web components are made up of three technologies. You can also see in the hello world code snippet, that we explicitly need to know and understand these three technologies.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We’re creating a &lt;strong&gt;template element&lt;/strong&gt; and setting its inner HTML&lt;/li&gt;
&lt;li&gt;We’re creating a &lt;strong&gt;shadow root&lt;/strong&gt;, and explicitly setting its mode to ‘open’.&lt;/li&gt;
&lt;li&gt;We’re cloning our &lt;strong&gt;template&lt;/strong&gt; and appending it to our &lt;strong&gt;shadow root&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;We’re registering a new &lt;strong&gt;custom element&lt;/strong&gt; to the document&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There’s nothing inherently wrong with this, since web components are supposed to be a “lower-level” browser API, making them prime for building abstractions on top of. But for a developer coming from a React or a Svelte background, having to understand these new browser features, and then having to write components with them can feel like too much friction.&lt;/p&gt;

&lt;h2&gt;
  
  
  A more advanced web component
&lt;/h2&gt;

&lt;p&gt;Let’s take a look at a more advanced web component, a counter button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Z45f_Sx1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hnc9uf6jidty3lvcxhbz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Z45f_Sx1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hnc9uf6jidty3lvcxhbz.png" alt="A simple counter button. There's a clickable button, and some text showing how many times the button has been clicked" width="388" height="228"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You click the button, and the counter increments.&lt;/p&gt;

&lt;p&gt;The following examples contains a few extra web component concepts, like lifecycle functions and observable attributes. You don’t need to understand everything going on in the code snippet. This example is really only used to illustrate how much boilerplate is required for the most basic of interactive interfaces, a counter button:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;templateEl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;template&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;templateEl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`
&amp;lt;button&amp;gt;Press me!&amp;lt;/button&amp;gt;
&amp;lt;p&amp;gt;You pressed me 0 times.&amp;lt;/p&amp;gt;
`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;OdysseyButton&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;HTMLElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&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;attachShadow&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;open&lt;/span&gt;&lt;span class="dl"&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;shadowRoot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;templateEl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cloneNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="o"&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;shadowRoot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button&lt;/span&gt;&lt;span class="dl"&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;p&lt;/span&gt; &lt;span class="o"&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;shadowRoot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;p&lt;/span&gt;&lt;span class="dl"&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;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;count&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Note: Web components have lifecycle methods,&lt;/span&gt;
  &lt;span class="c1"&gt;// If we're setting event listeners when the component is added to the DOM, it's our job to clean&lt;/span&gt;
  &lt;span class="c1"&gt;// them up when it gets removed from the DOM&lt;/span&gt;
  &lt;span class="nx"&gt;connectedCallback&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;button&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="s2"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handleClick&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;disconnectedCallback&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;button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;removeEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handleClick&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Unlike frameworks like React, Web Components don't automatically rerender when a prop (or attribute)&lt;/span&gt;
  &lt;span class="c1"&gt;// changes. Instead, we need to explicitly define which attributes we want to observe.&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nx"&gt;observedAttributes&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;disabled&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;count&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// When one of the above attributes change, this lifecycle method runs, and we can&lt;/span&gt;
  &lt;span class="c1"&gt;// react to the new attribute's value accordingly.&lt;/span&gt;
  &lt;span class="nx"&gt;attributeChangedCallback&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="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newVal&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;name&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;count&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`You pressed me &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;newVal&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; times.`&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;name&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;disabled&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;disabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// In HTML, attribute values are always strings. This means that it's our job to&lt;/span&gt;
  &lt;span class="c1"&gt;// convert types. You can see below that we're converting a string -&amp;gt; number, and then back to a string&lt;/span&gt;
  &lt;span class="nx"&gt;handleClick&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Number&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;getAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;count&lt;/span&gt;&lt;span class="dl"&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;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;count&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;span class="nx"&gt;counter&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="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;Web component authors, we need to consider a lot of things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Setting up the shadow DOM&lt;/li&gt;
&lt;li&gt;Setting up the HTML templates&lt;/li&gt;
&lt;li&gt;Cleaning up event listeners&lt;/li&gt;
&lt;li&gt;Defining properties that we want to observe&lt;/li&gt;
&lt;li&gt;Reacting to properties when they change&lt;/li&gt;
&lt;li&gt;Handling type conversions for attributes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And there are still so many other things to consider that I haven’t touched on in this article.&lt;/p&gt;

&lt;p&gt;That isn’t to say that web components are bad and that you shouldn’t write them, in fact, I’d argue that you learn so much about the browser platform by building with them. However, I feel that there are better ways to write components if your priority is to write interoperable components in a much more streamlined and ergonomic way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing web components with less boilerplate
&lt;/h2&gt;

&lt;p&gt;As I mentioned earlier, there are a lot of tools out there to help make writing web components much easier. One such tool is called Lit, which is developed by a team at Google. &lt;a href="https://lit.dev/"&gt;Lit&lt;/a&gt; is a lightweight library design to make writing web components simple, by removing the need for the boilerplate we’ve already seen above.&lt;/p&gt;

&lt;p&gt;As we’ll see, Lit does a lot of heavy lifting under-the-hood to help cut down the total lines of code by nearly half! And because Lit is a wrapper around web components and other native browser features, all your existing knowledge about web components is all transferable.&lt;/p&gt;

&lt;p&gt;To start seeing how Lit simplifies your web components, here’s the &lt;strong&gt;hello world&lt;/strong&gt; example from earlier, but refactored to use Lit instead of a vanilla web 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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;LitElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;HelloWorld&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;LitElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="s2"&gt;`&amp;lt;p&amp;gt;Hello World!&amp;lt;/p&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`

customElements.define('hello-world', HelloWorld);
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There’s a lot less boilerplate with the Lit component, and Lit handles the two problems I mentioned earlier, a little bit differently. Let’s see how:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The markup is directly defined from within the component class. While you can define your templates outside of the class, it’s common practice to the template as the result of the &lt;code&gt;render&lt;/code&gt; function. This is more inline with the mental model presented in other UI frameworks, where the UI is a function of the state.&lt;/li&gt;
&lt;li&gt;Lit also doesn’t require developers to attach the shadow DOM, or create template and clone template elements. While having an understanding of the underlying web component features will help when developing Lit components, they’re not required for getting started, so the barrier for entry is much lower.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So now for the big finale, what does the counter component look like once we’ve migrated it over to Lit?&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;LitElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;OdysseyCounter&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;LitElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nx"&gt;properties&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// We define the component's properties as well as their type.&lt;/span&gt;
        &lt;span class="c1"&gt;// These properties will trigger the component to re-render when their values change.&lt;/span&gt;
        &lt;span class="c1"&gt;// While they're not the same, you can think of these "properties" as being&lt;/span&gt;
        &lt;span class="c1"&gt;// Lit's alternatives to "observed attributes"&lt;/span&gt;
        &lt;span class="c1"&gt;// If the value is passed down as an attribute, Lit converts the value&lt;/span&gt;
        &lt;span class="c1"&gt;// to the correct type&lt;/span&gt;
    &lt;span class="na"&gt;count&lt;/span&gt;&lt;span class="p"&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="nb"&gt;Number&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;disabled&lt;/span&gt;&lt;span class="p"&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="nb"&gt;Boolean&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="c1"&gt;// There's no need to create a shadow DOM, clone the template,&lt;/span&gt;
        &lt;span class="c1"&gt;// or store references to our DOM nodes.&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;count&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="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;onCount&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;count&lt;/span&gt; &lt;span class="o"&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;count&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Instead of using the attributeChangedCallback lifecycle, the&lt;/span&gt;
        &lt;span class="c1"&gt;// render function has access to all of the component's properties,&lt;/span&gt;
        &lt;span class="c1"&gt;// which simplifies the process of manipulating our templates.&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="s2"&gt;`
      &amp;lt;button ?disabled=&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;disabled&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; @click=&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;onCount&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;gt;
        Press me!
      &amp;lt;/button&amp;gt;
      &amp;lt;p&amp;gt;You pressed me &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;count&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; times.&amp;lt;/p&amp;gt;
    `&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The amount of code we’re writing is cut down by almost half! And this difference becomes more noticeable when creating more complex user interfaces.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why am I going on about Lit?
&lt;/h2&gt;

&lt;p&gt;I’m a big believer in web components, but I recognise that the barrier of entry is high for many developers. Writing complex web components requires understanding heaps of browser features and the education around web components isn’t as comprehensive as other technologies, like React or Vue.&lt;/p&gt;

&lt;p&gt;This is why I think it’s important to use tools like Lit can make writing performant and interoperable web components much easier. This is great if you want your components to work within any frontend framework.&lt;/p&gt;

&lt;p&gt;If you’d like to learn even more, this is the approach I teach in my upcoming course &lt;a href="https://component-odyssey.com/"&gt;Component Odyssey&lt;/a&gt;. This course is excellent for anyone that wants to understand how to write components that work in any framework. I do this by covering the absolute basics of web components, before moving on to tools like Lit that simplify the process of writing web components without complicating your development environment. By the end, you’ll learn how to build and publish a component library that works across any frontend framework.&lt;/p&gt;

&lt;p&gt;If you want early-bird discount codes for Component Odyssey, then head on &lt;a href="https://component-odyssey.com/subscribe"&gt;over to the site to get notified&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>components</category>
      <category>react</category>
    </item>
    <item>
      <title>Introducing Sandpack Lit: The Universal CodeSandbox</title>
      <dc:creator>Andrico</dc:creator>
      <pubDate>Mon, 23 Oct 2023 15:19:27 +0000</pubDate>
      <link>https://dev.to/andrico1234/introducing-sandpack-lit-the-universal-codesandbox-178o</link>
      <guid>https://dev.to/andrico1234/introducing-sandpack-lit-the-universal-codesandbox-178o</guid>
      <description>&lt;p&gt;Sandpack was a game-changer back when the team at CodeSandbox officially released it. Thanks to Sandpack, running a CodeSandbox in your browser-based projects was easier than ever. With it, you could open up a little window inside of your application that runs an entirely different application. And you weren’t limited to running simple sites, but entire web applications or Node servers.&lt;/p&gt;

&lt;p&gt;If you haven’t directly used Sandpack yourself, you’ve probably been on a site that has for displaying runnable code snippets or code exercises. In fact, the React docs site uses Sandpack for its code exercises.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0JOTMqi_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://component-odyssey.s3.amazonaws.com/react-docs.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0JOTMqi_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://component-odyssey.s3.amazonaws.com/react-docs.gif" alt="Runnable code examples in the React docs site" width="800" height="414"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, there’s one major problem… CodeSandbox only officially provides a React wrapper for Sandpack. If you wanted to run a CodeSandox from within your project, you’d need to either:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create your project in React&lt;/li&gt;
&lt;li&gt;Create your own wrapper around the sandpack-core library&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introducing Sandpack Lit
&lt;/h2&gt;

&lt;p&gt;With more frameworks than ever to build web applications, it makes sense to offer a drop-in Sandpack solution for frameworks that aren’t React.&lt;/p&gt;

&lt;p&gt;Developers could build their own Sandpack wrapper around their favourite frameworks, or the team at CodeSandbox could release official wrappers around each framework, but that feels like a lot of work for a lot of developers.&lt;/p&gt;

&lt;p&gt;That’s where Sandpack Lit comes in.&lt;/p&gt;

&lt;p&gt;Sandpack Lit is a standalone web component that allows you to add a CodeSandbox to any client-side web application. Let’s see it in action!&lt;/p&gt;

&lt;h2&gt;
  
  
  Sandpack Lit in action
&lt;/h2&gt;

&lt;p&gt;You’ll see just how easy it is to render a CodeSandbox inside of different projects using different frameworks. I’ll render the same interactive theme picker inside the sandbox&lt;/p&gt;

&lt;p&gt;If you want to go ahead and use it yourself, you can begin by installing &lt;code&gt;sandpack-lit&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;sandpack-lit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;If you’re using Svelte, create the following 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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  import "sandpack-lit/dist/presets/sandpack";
  import "sandpack-lit/dist/themes/odyssey.css";
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&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;main&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;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Svelte&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&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;class&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"container"&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;sandpack&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="na"&gt;preset&lt;/span&gt;
      &lt;span class="na"&gt;options&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;closableTabs&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="na"&gt;files&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/** files go here */&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;customSetup&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="s2"&gt;dependencies&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2.6.1&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="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;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;main&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;style&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  .container &lt;span class="si"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;900&lt;/span&gt;&lt;span class="nx"&gt;px&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;style&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run and open your development server, and you should see Sandpack working:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ugvVMMqm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/864xbzr2r1j5fwxmpw8b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ugvVMMqm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/864xbzr2r1j5fwxmpw8b.png" alt="Sandpack Lit running in Svelte" width="800" height="518"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Vue
&lt;/h3&gt;

&lt;p&gt;If you’re using Vue, create the following 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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt; &lt;span class="na"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
import "sandpack-lit/dist/presets/sandpack";
import "sandpack-lit/dist/themes/odyssey.css";

const files = &lt;span class="si"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/** files go here */&lt;/span&gt; &lt;span class="si"&gt;}&lt;/span&gt;

const dependencies = &lt;span class="si"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;lit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2.6.1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="si"&gt;}&lt;/span&gt;;

const options = &lt;span class="si"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;closableTabs&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;files&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;customSetup&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;dependencies&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;script&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;template&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;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Vue&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&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="s"&gt;"width: 900px"&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;sandpack&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="na"&gt;preset&lt;/span&gt; &lt;span class="na"&gt;v-bind&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="na"&gt;options&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;/&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;template&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Kick off your dev server, and you should see Sandpack working:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cmLwb53P--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/agh3utm19mx492q77nyr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cmLwb53P--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/agh3utm19mx492q77nyr.png" alt="Sandpack Lit running in Vue" width="800" height="518"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Consuming web components in a React project is a little different to the others, since React doesn’t offer out-of-the-box support for web components. Luckily Sandpack Lit offers a exports a React component that you can use instead:&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;SandpackLitComponent&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;sandpack-lit/dist/presets/sandpack-react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sandpack-lit/dist/themes/odyssey.css&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;App&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;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;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;900px&lt;/span&gt;&lt;span class="dl"&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;h1&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;textAlign&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;center&lt;/span&gt;&lt;span class="dl"&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;React&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&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;SandpackLitComponent&lt;/span&gt;
                &lt;span class="na"&gt;options&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;closableTabs&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="na"&gt;files&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="cm"&gt;/** files go here */&lt;/span&gt;
                    &lt;span class="p"&gt;},&lt;/span&gt;
                    &lt;span class="na"&gt;customSetup&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="na"&gt;dependencies&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                            &lt;span class="na"&gt;lit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2.6.1&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="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;div&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;App&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run and open your development server, and you should see Sandpack working:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--T0yM26oD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zrp5hr9z5i6qqjm8ntvb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--T0yM26oD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zrp5hr9z5i6qqjm8ntvb.png" alt="Sandpack Lit running in React" width="800" height="518"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Vanilla
&lt;/h3&gt;

&lt;p&gt;If you’re opting to not use a framework at all, you can add Sandpack Lit to your project using the following:&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;body&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;"app"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"width:900px"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;h1&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"text-align: center; color: black;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;VanillaJS&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;sandpack-preset&amp;gt;&amp;lt;/sandpack-preset&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;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"module"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sandpack-lit/dist/presets/sandpack&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sandpack-lit/dist/themes/odyssey.css&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;files&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="cm"&gt;/** your files here */&lt;/span&gt;

        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sandpackEl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sandpack-preset&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;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;closableTabs&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;files&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;customSetup&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="s2"&gt;dependencies&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2.6.1&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="nx"&gt;sandpackEl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run your local development server, and you can see Sandpack Lit working without a framework:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wYWo2anY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/deu91s0zg9pfagi5i5pl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wYWo2anY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/deu91s0zg9pfagi5i5pl.png" alt="Sandpack Lit running in VanillaJS" width="800" height="518"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So how does Sandpack Lit work across all frameworks? Instead of being built using a framework, I’ve instead opted to use a handy native browser feature called &lt;strong&gt;web components&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the Heck are Web Components?
&lt;/h2&gt;

&lt;p&gt;The browser offers developers a way to write reusable components without needing an external framework. They can create a custom element (with styles, attributes, and more), and use it like any other HTML element.&lt;/p&gt;

&lt;p&gt;You can even import and use web components made by other developers!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://nolanlawson.com/2023/08/23/use-web-components-for-what-theyre-good-at/"&gt;Nolan Lawson recently wrote&lt;/a&gt; about the use cases that web components are best for:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You have some component at the leaf of the DOM tree, it doesn’t need to be rendered server-side, and it doesn’t &lt;code&gt;&amp;lt;slot&amp;gt;&lt;/code&gt; any content inside of it. Examples include: a rich text editor, a calendar widget, a color picker, etc. To me, this is the most unambiguously slam-dunk use case for web components.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I feel that this applies perfectly to an embedded code editor, like Sandpack, since a code editor is a highly interactive client-side component that doesn’t rely on the consumer passing down child content.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-world usages of Sandpack Lit (Component Odyssey)
&lt;/h2&gt;

&lt;p&gt;While Sandpack Lit is still new, I’ve been using it over the last few months to build the exercises and code examples in my web development course &lt;a href="https://component-odyssey.com/"&gt;Component Odyssey&lt;/a&gt;. Component Odyssey is dedicated to helping developers learn the ins-and-outs of component library development.&lt;/p&gt;

&lt;p&gt;I’m using Sandpack Lit to run both browser-based code and Node projects in the browser:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pvgRUBxk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7ezat9t77a2rq0po7b2x.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pvgRUBxk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7ezat9t77a2rq0po7b2x.gif" alt="Sandpack Lit used in Component Odyssey to run a code exercise in the browser" width="800" height="523"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you’d like to learn build, manage, and distribute your component library in an ever-increasingly complex web development landscape, then &lt;a href="https://component-odyssey.com/subscribe"&gt;register your interest&lt;/a&gt; to get an early-bird discount when Component Odyssey releases.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learn more about Sandpack Lit
&lt;/h2&gt;

&lt;p&gt;You may have noticed that I used the &lt;code&gt;sandpack-preset&lt;/code&gt; web component. This is a component that renders a preset configuration of some additional components, ones that render the code editor, ones that render the preview, and one that sets up the shared context. Sandpack Lit also exports these components, so you can have more flexibility. If you’d like to learn more, you can check out the &lt;a href="https://github.com/andrico1234/sandpack-lit"&gt;Sandpack Lit GitHub repo&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s next for Sandpack Lit?
&lt;/h2&gt;

&lt;p&gt;Sandpack Lit is still in early days, if there are features that you’d like to see added, please add an issue in the GitHub, or try your hand at implementing it yourself. If you like the look of Sandpack Lit, then please leave a GitHub star and share online!&lt;/p&gt;

</description>
      <category>react</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>vue</category>
    </item>
    <item>
      <title>Making your design tokens future-proof</title>
      <dc:creator>Andrico</dc:creator>
      <pubDate>Tue, 20 Jun 2023 14:09:42 +0000</pubDate>
      <link>https://dev.to/andrico1234/making-your-design-tokens-future-proof-3adp</link>
      <guid>https://dev.to/andrico1234/making-your-design-tokens-future-proof-3adp</guid>
      <description>&lt;p&gt;Over the last decade, design systems have become a mainstay in many product development teams, and for good reason! Product teams want to deliver value to their users quickly but they also want to provide a consistent experience to their users.&lt;/p&gt;

&lt;p&gt;As a result, there’s been a huge increase in the number of products designed to help teams of all sizes manage design systems. Software like Anima, Zeroheight, and Storybook all offer slightly different ways to improve workflows around building and maintaining design systems.&lt;/p&gt;

&lt;p&gt;While many tools work at the macro level (like offering teams and stakeholders a way to view guidelines and interact with the components) there are other, more niche tools that teams can use to iron out the finer details.&lt;/p&gt;

&lt;p&gt;This is especially true when it comes to managing design tokens. Before we dive into design tokens tooling, let’s review what design tokens are.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are design tokens?
&lt;/h2&gt;

&lt;p&gt;Design tokens are discrete pieces of data that represent the specific values of a design system. These values are often intentional design decisions made by the product team. These design tokens will then make their way over to the codebase, commonly as CSS variables, but can be represent in many other way. &lt;/p&gt;

&lt;p&gt;Let’s look at how design tokens can be used to represent a colour palette. &lt;/p&gt;

&lt;p&gt;In Figma, the designer has come up with a colour palette. The primary colour is a &lt;em&gt;Tomato&lt;/em&gt; red. Our designer has specifically chosen 12 steps of the Tomato colour, with an alternative palette for the dark theme:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gKAwpo2R--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1o193ez8976nvzfpzgq8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gKAwpo2R--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1o193ez8976nvzfpzgq8.png" alt="Two swatches of 12 shades for a tomato red color. One for a light mode, another for a dark mode" width="800" height="230"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Design system tools may output this information as platform-agnostic data, usually in the form of a JSON object. There might be an additional conversion step where the data is converted into something platform specific, like CSS variables.&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;--tomato-4&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;#FFDED6&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="nt"&gt;--tomato-9&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;#F55442&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="nt"&gt;--tomato-11&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;#D43029&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefers-color-scheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;--tomato-4&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#401A12&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;--tomato-9&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#F55442&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;--tomato-11&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#FF7D66&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;button&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="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--tomato-11&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nl"&gt;border-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--tomato-9&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--tomato-4&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is what the above styles would look when rendered to the browsers:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--H42j-Xvx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cts5xxofin20edw33kr1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--H42j-Xvx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cts5xxofin20edw33kr1.png" alt="Two buttons next to each other. The left is for the light mode and has a darker background, the right is for the dark mode and has a lighter background" width="800" height="278"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Things get messy in the in-between stages where we’re:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Exporting the code to a platform-agnostic object.&lt;/li&gt;
&lt;li&gt;Converting that object to something developers can use in their projects.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Just for the sake of example, this is what the platform-agnostic data looks like when exported out of Figma using three popular tools:&lt;/p&gt;

&lt;h3&gt;
  
  
  Anima
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.figma.com/community/plugin/857346721138427857/Anima---Export-Figma-to-HTML%2C-React---Manage-Design-Systems"&gt;Link to plugin&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tomato-10-dark"&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="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"f991b0d7b53deca9"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"$type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"color"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"$value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#ff634f"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"$description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&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="nl"&gt;"tomato-10-light"&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="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"456aff72c86eb003"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"$type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"color"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"$value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#e34236"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"$description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&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;h3&gt;
  
  
  Design Tokens
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.figma.com/community/plugin/888356646278934516/Design-Tokens"&gt;By Lukas Oppermann&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"color"&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="nl"&gt;"tomato-10-dark"&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="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"color"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#ff634fff"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"blendMode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"normal"&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="nl"&gt;"tomato-10-light"&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="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"color"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#e34236ff"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"blendMode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"normal"&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;h3&gt;
  
  
  Token Studio
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.figma.com/community/plugin/843461159747178978/Tokens-Studio-for-Figma-(Figma-Tokens)"&gt;Link to plugin&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"global"&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="nl"&gt;"tomato-10-dark"&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="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#ff634f"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"color"&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="nl"&gt;"tomato-10-light"&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="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#e34236"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"color"&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;p&gt;If a team wants to move away from Token Studio and use Anima instead, we’d need to ensure that the tokens we’re exporting from Token Studio are compatible with Anima’s token format.&lt;/p&gt;

&lt;p&gt;It’s highly likely that when migrating from any tool there will be inconsistencies in the data structures. This is because there’s no definitive way of writing design tokens.&lt;/p&gt;

&lt;p&gt;Fortunately, the people over at the Design Tokens Community Group have been hard at work creating a &lt;em&gt;&lt;a href="https://tr.designtokens.org/format/"&gt;design tokens specification&lt;/a&gt;&lt;/em&gt;, a set of rules and guidelines about what constitutes valid design tokens. It’s a specification that library authors and design system teams can use to ensure that whatever they’re building will be interoperable with the breadth of tooling out there. &lt;/p&gt;

&lt;p&gt;While a single design token is usually a small, discrete value. Design systems may have a collection of thousands of design tokens. These design tokens may not be values, but aliases for other design tokens. They may be heavily nested within groups. Multiple design tokens might exist for different themes. At a point, it becomes very difficult for an individual to review a set of design tokens and cross-reference it with the specification. &lt;/p&gt;

&lt;p&gt;Having a concrete specification defined not only means that the tools can start formatting their tokens consistently, but it also unlocks improved design tokens tooling that serve the greater design and web development community. A &lt;em&gt;&lt;a href="https://animaapp.github.io/design-token-validator-site/"&gt;design tokens validator&lt;/a&gt;&lt;/em&gt; is one such tool, which Anima recently released as a free open-source tool, for anyone to use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Validating your tokens
&lt;/h2&gt;

&lt;p&gt;With this validator, you’re able to check that your design system’s entire set of design tokens matches the rules of the specification. And if they don’t, the validator provides links to the right part of the specification, to help you understand the problem and how to fix it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ziyXP0Z8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nlj97mtk8ipy6f7btpen.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ziyXP0Z8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nlj97mtk8ipy6f7btpen.png" alt="The design tokens validator" width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s try it ourselves now with an invalid set of design tokens:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"colors"&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="nl"&gt;"red"&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="nl"&gt;"100"&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="nl"&gt;"$value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#FFCDD2"&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="nl"&gt;"200"&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="nl"&gt;"$type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"color"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#EF9A9A"&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;span class="nl"&gt;"button"&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="nl"&gt;"primary"&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="nl"&gt;"color"&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="nl"&gt;"$value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{colors.red.200}"&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="nl"&gt;"padding"&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="nl"&gt;"$type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dimension"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"$value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2em"&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;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At first glance, this set of tokens makes sense. We have a handful of colors defined at the top, as well as some component-specific tokens underneath. The color of the button is even an &lt;em&gt;alias&lt;/em&gt; to an existing token. There are, however, a small number of issues that might be tricky for an individual to spot.&lt;/p&gt;

&lt;p&gt;Let’s begin by opening the &lt;a href="https://animaapp.github.io/design-token-validator-site/"&gt;site&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once everything’s loaded, paste in the above tokens.&lt;/p&gt;

&lt;p&gt;You’ll see that the validator has flagged several issues:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3c2sE1a2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6aa91o2q9m8gvmtxr01c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3c2sE1a2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6aa91o2q9m8gvmtxr01c.png" alt='A series of errors that the validator has flagged up. Including "Token does not have a type" and "Invalid dimensions"' width="800" height="492"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Why not treat this as an exercise and look to fix the tokens yourself using the validator’s hints (you can also skip to the answers below)&lt;/p&gt;

&lt;p&gt;.&lt;/p&gt;

&lt;p&gt;.&lt;/p&gt;

&lt;p&gt;.&lt;/p&gt;

&lt;p&gt;.&lt;/p&gt;

&lt;p&gt;.&lt;/p&gt;

&lt;p&gt;.&lt;/p&gt;

&lt;p&gt;Fixes for the tokens:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Give “colors.red.100” a “$type: “color”&lt;/li&gt;
&lt;li&gt;Update “colors.red.200” to have property “$value” and not “value”&lt;/li&gt;
&lt;li&gt;Update “button.primary.padding” to have a “$value” of “2rem”, as “em” is not a valid unit.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After updating the tokens with the above values, you can click validate again and the validator should pass! 🙌🏼&lt;/p&gt;

&lt;p&gt;For the developers out there, you can even integrate the validator within your projects using the &lt;a href="https://github.com/AnimaApp/design-token-validator"&gt;design-token-validator&lt;/a&gt; npm package.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;It’s exciting to see the Design Tokens CG specification slowly proliferate into more design tokens tooling. I hope that once the draft officially lands that design system tooling start adopting so designers and developers don’t have to worry about managing compatibility layers across tokens, but about actually building their design system.&lt;/p&gt;

&lt;p&gt;If you enjoy the validator, please &lt;a href="https://github.com/AnimaApp/design-token-validator"&gt;star on GitHub&lt;/a&gt; and share on Twitter&lt;/p&gt;

&lt;h2&gt;
  
  
  About me
&lt;/h2&gt;

&lt;p&gt;I’m Andrico, a software engineer at Anima. If you enjoyed this piece, then please consider following me on &lt;a href="https://twitter.com/AndricoKaroulla"&gt;Twitter&lt;/a&gt;, or checking out my &lt;a href="https://andri.co"&gt;personal blog&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;The design tokens validator is just one of many tools created by Anima to help product teams improve their design system workflow.&lt;/p&gt;

&lt;p&gt;Check out &lt;a href="https://www.animaapp.com/lp/design-token-automation?utm_source=design-token-validator&amp;amp;utm_medium=web&amp;amp;utm_campaign=readme"&gt;Anima&lt;/a&gt; if you'd like to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Convert styles to design tokens inside Figma&lt;/li&gt;
&lt;li&gt;Push generated tokens directly to GitHub&lt;/li&gt;
&lt;li&gt;Pull code updates back into Figma&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>designsystem</category>
      <category>productivity</category>
      <category>css</category>
    </item>
    <item>
      <title>Introducing a2k - A UI library designed to capture that retro 2000s feeling</title>
      <dc:creator>Andrico</dc:creator>
      <pubDate>Thu, 01 Dec 2022 21:34:19 +0000</pubDate>
      <link>https://dev.to/andrico1234/introducing-a2k-a-ui-library-designed-to-capture-that-retro-2000s-feeling-192j</link>
      <guid>https://dev.to/andrico1234/introducing-a2k-a-ui-library-designed-to-capture-that-retro-2000s-feeling-192j</guid>
      <description>&lt;p&gt;Take a look at a new 2000s-inspired component library. You’ll learn what a2k is, how it’s been built, and how to use it to build retro applications in React, Vue, vanilla, or your favourite frontend framework.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a2k?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/andrico1234/a2k" rel="noopener noreferrer"&gt;a2k&lt;/a&gt; is a web component library designed to evoke memories of the early 2000s era of computing.&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%2F2fxdz2qbv2gcb5oy43kr.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%2F2fxdz2qbv2gcb5oy43kr.png" alt="Introducing a2k"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why does a2k exist?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  To embrace the browser platform
&lt;/h3&gt;

&lt;p&gt;Over the years, the browser has introduced several mechanisms to allow developers to create reusable custom components. These are known as &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Web_Components" rel="noopener noreferrer"&gt;web components&lt;/a&gt;. Web components are a native way of reusing custom markup across several places in a codebase, or different codebases entirely.&lt;/p&gt;

&lt;p&gt;Web components make it easy to author components for others to use in their own projects. As a consumer, it requires little more than to import a component and then use it like an HTML element.&lt;/p&gt;

&lt;p&gt;This makes it a great tool for building a2k.&lt;/p&gt;

&lt;h3&gt;
  
  
  To create a framework agnostic component library
&lt;/h3&gt;

&lt;p&gt;You may be familiar with frameworks like React and Vue, which popularised the notion of building user interfaces using components. While these tools exist for more than writing and using components, there is an entire ecosystem dedicated to component libraries. &lt;a href="https://mui.com/" rel="noopener noreferrer"&gt;MUI&lt;/a&gt; and &lt;a href="https://chakra-ui.com/" rel="noopener noreferrer"&gt;Chakra UI&lt;/a&gt;, are both excellent examples of React component libraries that allow developers to build complex user interfaces from battle-tested components.&lt;/p&gt;

&lt;p&gt;There is a catch when it comes to writing components using frameworks. Libraries written in React can’t be used in Vue projects and vice versa. In fact, component libraries written in &lt;a href="https://twitter.com/claviska/status/1575505203763335169" rel="noopener noreferrer"&gt;Vue 2 aren’t necessarily compatible with Vue 3&lt;/a&gt;. Building a2k using web components means that we have an interoperable way of building retro user interfaces that won’t be obsolete by the next cycle of frontend tooling&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%2Fmdbnqehoa3jx2yvpbybn.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%2Fmdbnqehoa3jx2yvpbybn.png" alt="Vanilla, Vue, and React components side-by-side"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  To put the fun back in uhh.. fruntend (😅) development
&lt;/h3&gt;

&lt;p&gt;a2k is designed to evoke a feeling of nostalgia and to help others form the foundations of their own retro web applications. For me, this alone is a motivating enough reason to pursue a project.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://a2000.netlify.app/" rel="noopener noreferrer"&gt;Andricos2000&lt;/a&gt; is a fun example of a project that uses a2k to build a retro web application&lt;/p&gt;


  


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

&lt;p&gt;Like with many UI libraries, a2k is comprised of a base stylesheet and a suite of fully-functional components&lt;/p&gt;

&lt;h3&gt;
  
  
  Base Stylesheet
&lt;/h3&gt;

&lt;p&gt;This CSS file has a bunch of jobs. First off, it sets the base styles, like the default font family, loading cursor assets, setting the default element appearances, etc.&lt;/p&gt;

&lt;p&gt;The file also defines some utility classes. For common styles that don’t need to be entire components, we can create utilities that we can reuse across our entire application.&lt;/p&gt;

&lt;p&gt;Finally, the CSS file also creates the variables which we can use throughout our entire site. It also defines the variables for individual components. The file also applies the default values for these variables, which can be overridden by the user.&lt;/p&gt;

&lt;h3&gt;
  
  
  Component Suite
&lt;/h3&gt;

&lt;p&gt;a2k exports a growing number of different web components. Many of these components can be categorised based on complexity or expected usage.&lt;/p&gt;

&lt;h3&gt;
  
  
  Layout UI
&lt;/h3&gt;

&lt;p&gt;These are components with no built-in interactivity but are used to help build component/application layouts. One example is the &lt;code&gt;a2k-panel&lt;/code&gt;, which is a box that applies some retro styling. It can be used alone, but it’s also used to build more complex components, like the &lt;code&gt;a2k-window&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Behavioural UI
&lt;/h3&gt;

&lt;p&gt;These are the common UI patterns, like buttons, select menus, and progress bars. You can use these components any place you’d use the native counterpart with the same behaviour but with the added benefit of cool retro UI.&lt;/p&gt;

&lt;p&gt;Components like &lt;code&gt;a2k-button&lt;/code&gt; offer style and functionality outside of the box. More complex UI components like &lt;code&gt;a2k-select&lt;/code&gt; are built with the &lt;a href="https://www.w3.org/WAI/ARIA/apg/" rel="noopener noreferrer"&gt;ARIA authoring practices&lt;/a&gt; in mind, so they’re accessible alternatives to the native browser components.&lt;/p&gt;

&lt;h3&gt;
  
  
  Composite Components
&lt;/h3&gt;

&lt;p&gt;These are complex components that are built on top of other layout or behavioural components.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;a2k-taskbar&lt;/code&gt; is one such example. The taskbar is a complex UI pattern that’s built using a mix of HTML, &lt;code&gt;a2k-button&lt;/code&gt;, &lt;code&gt;a2k-icon&lt;/code&gt;, and &lt;code&gt;a2k-panel&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%2Fmzsl85l43yo0w0k8yqzx.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%2Fmzsl85l43yo0w0k8yqzx.png" alt="Can you spot all the different components used?"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These components are highly specialised and you will likely not need to use them frequently. They might be used to build desktop-inspired UIs. You can even build your composite components using the building blocks that a2k provides.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started with a2k
&lt;/h2&gt;

&lt;p&gt;Because a2k is built using web components, it’s easy to integrate with different frameworks, like Vue and React.&lt;/p&gt;

&lt;p&gt;I’ve provided some handy guides below to get a2k up and running using different frameworks; vanilla JS, Vue, and React.&lt;/p&gt;

&lt;h3&gt;
  
  
  Vanilla JS
&lt;/h3&gt;

&lt;p&gt;To begin, we’ll need to do a little repo setup. We’ll be using &lt;a href="https://modern-web.dev/guides/dev-server/getting-started/" rel="noopener noreferrer"&gt;Web Dev Server&lt;/a&gt;, which we’ll use to run a local development server. This will allow us to easily use the node modules we install via npm.&lt;/p&gt;

&lt;p&gt;We’ll also use &lt;a href="https://lit.dev/docs/" rel="noopener noreferrer"&gt;Lit&lt;/a&gt;, which is a simple library to help build web components. While we won’t use this library directly, a2k uses it under the hood so we need to ensure that it’s available.&lt;/p&gt;

&lt;p&gt;Start by initialising your project using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm init &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can now install the necessary node modules:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i @a2000/styles @a2000/select @web/dev-server lit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the root of your project, create an index.html file with the following contents:&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="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;a2k web components&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;body&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;"width: 300px"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;a2k-select&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Favourite OS"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;option&amp;gt;&lt;/span&gt;Windows&lt;span class="nt"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;option&amp;gt;&lt;/span&gt;Linux&lt;span class="nt"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;option&amp;gt;&lt;/span&gt;Apple&lt;span class="nt"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/a2k-select&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;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lastly, kick off your development server with the following script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx wds &lt;span class="nt"&gt;--node-resolve&lt;/span&gt; &lt;span class="nt"&gt;--open&lt;/span&gt; &lt;span class="nt"&gt;--watch&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open up your site at localhost:8000 and you should be greeted with some unstyled HTML. This is completely expected as we haven’t linked up the a2k styles yet. We also haven’t registered our web components either, so the browser will display the fallback content. In this case, the contents are the three option elements.&lt;/p&gt;

&lt;p&gt;Inside the head element add the following HTML tag:&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;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"./node_modules/@a2000/styles/a2k-styles.css"&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;If you jump back to localhost:8000, you can see that the font family and the sizes of the HTML have changed, which shows that the stylesheet is being imported properly.&lt;/p&gt;

&lt;p&gt;The final step is to register our web component. We do this by simply calling importing the script that a2k uses to register the select component. At the bottom of your &lt;code&gt;body&lt;/code&gt; element, paste the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"module"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@a2000/select/a2k-select.js&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Jump back to localhost:8000, and you should see a fully functioning select component.&lt;/p&gt;


  


&lt;p&gt;And that’s it!&lt;/p&gt;

&lt;p&gt;The a2k web components are working in your basic HTML file. Try and experiment with some of the other components laid out in the &lt;a href="https://a2000-docs.netlify.app/" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;. If you come up with anything cool, please share it with me on &lt;a href="https://twitter.com/AndricoKaroulla" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; or &lt;a href="https://front-end.social/@andrico" rel="noopener noreferrer"&gt;Mastodon&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Vue
&lt;/h3&gt;

&lt;p&gt;Because Vue scores a perfect score in the &lt;a href="https://custom-elements-everywhere.com/libraries/vue/results/results.html" rel="noopener noreferrer"&gt;Custom Elements tests&lt;/a&gt;, integrating web components in your Vue project should be a breeze. Let’s see how we can do this with a2k.&lt;/p&gt;

&lt;p&gt;In a new directory, run the following script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm init vue@latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The terminal will prompt you with a few options but for the sake of this tutorial, you can choose no for all of them.&lt;/p&gt;

&lt;p&gt;Move into the directory and install all the necessary dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;vue-example
npm i @a2000/styles @a2000/select lit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once that’s done, you can kick off your dev server with the following&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Begin by updating your &lt;code&gt;App.vue&lt;/code&gt; so that it looks like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;setup&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;template&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;style &lt;/span&gt;&lt;span class="na"&gt;scoped&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you open up &lt;a href="http://localhost:5173" rel="noopener noreferrer"&gt;localhost:5173&lt;/a&gt; in your browser, you should see an empty page. Let’s go ahead and add some web components.&lt;/p&gt;

&lt;p&gt;First off, add the following to your &lt;code&gt;index.html&lt;/code&gt;'s &lt;code&gt;head&lt;/code&gt; component:&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;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"./node_modules/@a2000/styles/a2k-styles.css"&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;Update your &lt;code&gt;App.vue&lt;/code&gt; file to import and render the a2k select component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;setup&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@a2000/select/a2k-select.js&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;template&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;"width: 300px"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;a2k-select&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Favourite OS"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;option&amp;gt;&lt;/span&gt;Windows&lt;span class="nt"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;option&amp;gt;&lt;/span&gt;Linux&lt;span class="nt"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;option&amp;gt;&lt;/span&gt;Apple&lt;span class="nt"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/a2k-select&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;/template&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;style &lt;/span&gt;&lt;span class="na"&gt;scoped&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;a2k-select&lt;/span&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="no"&gt;black&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;/style&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Jump back to &lt;a href="http://localhost:5173" rel="noopener noreferrer"&gt;localhost:5173&lt;/a&gt; and you should see the select menu in the browser. That’s it!&lt;/p&gt;

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

&lt;p&gt;Sadly, React’s support for custom elements isn’t as comprehensive as Vue’s, so we’ll have to do a little extra work to get this working.&lt;/p&gt;

&lt;p&gt;Let’s create a react project by running the following in your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx create-react-app react-example
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;let’s install the necessary packages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i @a2000/styles @a2000/select lit @lit-labs/react
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The packages we’re installing are pretty much the same as earlier, but with an additional package, &lt;code&gt;@lit-labs/react&lt;/code&gt;. This is a &lt;a href="https://www.npmjs.com/package/@lit-labs/react" rel="noopener noreferrer"&gt;handy package&lt;/a&gt; by the Lit team to integrate web components with React.&lt;/p&gt;

&lt;p&gt;Clean out your &lt;code&gt;App.js&lt;/code&gt; file so it looks like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&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;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;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;}&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;App&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Kickoff the dev server using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;localhost:3000&lt;/a&gt; should be empty at this point.&lt;/p&gt;

&lt;p&gt;Go ahead and create a new file in your &lt;code&gt;src&lt;/code&gt; directory called &lt;code&gt;SelectComponent.js&lt;/code&gt; with the following contents:&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="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&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="s2"&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createComponent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@lit-labs/react&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;A2kSelect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@a2000/select&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@a2000/select/a2k-select.js&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;SelectComponent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createComponent&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;tagName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;a2k-select&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;elementClass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;A2kSelect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;react&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;events&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;onactivate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;activate&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;onchange&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;change&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without getting too much into the weeds, I’m simply following the documentation for the &lt;code&gt;createComponent&lt;/code&gt; API. I am however importing twice from &lt;code&gt;@a2000/select&lt;/code&gt; twice. Once to access the &lt;code&gt;A2kSelect&lt;/code&gt; component class itself, the second to register the custom element to the window. In the future, I’ll look to simplify this process.&lt;/p&gt;

&lt;p&gt;This file exports a component which is a wrapper around the web component. The wrapper ensures that any props and events are handled correctly as they’re passed through to the custom element.&lt;/p&gt;

&lt;p&gt;Next, let’s import our styles and use our component. Clear out &lt;code&gt;App.js&lt;/code&gt; and replace it with the following:&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="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@a2000/styles/a2k-styles.css&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;SelectComponent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./SelectComponent&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&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;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;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;300px&lt;/span&gt;&lt;span class="dl"&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="nc"&gt;SelectComponent&lt;/span&gt; &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Favourite OS"&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;option&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Windows&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;option&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;option&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Linux&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;option&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;option&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Apple&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;option&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;SelectComponent&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;)&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;App&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the most part, the markup looks very similar to the vanilla and Vue examples, so everything should look familiar to you. Jump back into your browser and have a little play with the components. Everything should be working as expected.&lt;/p&gt;

&lt;h2&gt;
  
  
  How can I support a2k?
&lt;/h2&gt;

&lt;p&gt;a2k is still a work in progress, so any feedback is welcome. If you run into any problems while following the set-up guides, please open a &lt;a href="https://github.com/andrico1234/a2k" rel="noopener noreferrer"&gt;GitHub issue&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Other ways you can support the library is by using a2k in your own projects and requesting the changes and features you’d like to see.&lt;/p&gt;

&lt;p&gt;Finally, if you enjoyed the article and want to show a little support. You can simply give &lt;a href="https://github.com/andrico1234/a2k" rel="noopener noreferrer"&gt;a2k a star on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does the future look like for a2k?
&lt;/h2&gt;

&lt;p&gt;I’m nowhere near finished building a2k, there’s still much more I’d like to add in the near/long-term future. Some of the cool things I’d like to add include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Support for server-side rendered components using declarative shadow DOM.&lt;/li&gt;
&lt;li&gt;More components&lt;/li&gt;
&lt;li&gt;Improved customisation&lt;/li&gt;
&lt;li&gt;React components for each component&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’d also like to use a2k as a springboard for any cool and interesting ideas that I have. I’m also happy for others in the web community to contribute with some great ideas too.&lt;/p&gt;

&lt;p&gt;Thanks for reading, and I hope that you have a lot of fun using a2k in your own projects.&lt;/p&gt;

</description>
      <category>webcomponents</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>css</category>
    </item>
    <item>
      <title>Create a Borderlands-style skill tree in 5 minutes</title>
      <dc:creator>Andrico</dc:creator>
      <pubDate>Wed, 21 Aug 2019 06:36:05 +0000</pubDate>
      <link>https://dev.to/andrico1234/create-a-borderlands-style-skill-tree-in-5-minutes-591a</link>
      <guid>https://dev.to/andrico1234/create-a-borderlands-style-skill-tree-in-5-minutes-591a</guid>
      <description>&lt;h2&gt;
  
  
  How to combine your two favourite things to make an exciting and engaging experience for your users.
&lt;/h2&gt;

&lt;p&gt;Growing up, I spent my spare time doing what most programmers did; played video games every waking moment. I loved Adventure games and what a time sink they were; if time was the Mary Rose, and I was the French, my artillery were games like Kingdom Hearts, Ōkami, and Borderlands. &lt;/p&gt;

&lt;p&gt;Why did I, and others, spend so much of our spare time exploring, surviving, dying, and (so, so much)grinding? Hundreds of factors contribute toward making an engaging experience, but the one I'm going to focus on is the notion of progression.&lt;/p&gt;

&lt;p&gt;The idea of gamnification isn't new, with many popular applications (like &lt;a href="https://todoist.com/?lang=en"&gt;todoist&lt;/a&gt;, or &lt;a href="https://productivitychallengetimer.com/"&gt;challenge timer&lt;/a&gt;) incorporating some sort of progression scheme to make us, the consumer, use their app, give them money, and hand over our personal data. So I decided to go about my way of enabling others to do just that, through beautiful skill trees! Note: I expect neither money nor data from those using my personal skill trees.&lt;/p&gt;

&lt;p&gt;The last few weeks have seen me toil away to create what I hope to be a pleasant plug'n'play React package to help you create exciting skill trees. You can test it yourself by following the tutorial. I hope it to be a frictionless experience.&lt;/p&gt;

&lt;p&gt;We hope to have something resembling the skill tree below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://thepracticaldev.s3.amazonaws.com/i/sn9tgrqpv98tm4ftqvqx.png"&gt;https://thepracticaldev.s3.amazonaws.com/i/sn9tgrqpv98tm4ftqvqx.png&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Grab the starter repo by using &lt;code&gt;git clone git@github.com:andrico1234/borderlands-skill-tree.git&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Move into the directory and run the starting script,  &lt;code&gt;yarn start&lt;/code&gt;. Give the site a whirl, you'll see nothing but the Borderlands logo and environment.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;beautiful-skill-tree&lt;/code&gt; exposes three components: the &lt;code&gt;SkillProvider&lt;/code&gt;, &lt;code&gt;SkillTreeGroup&lt;/code&gt;, and the &lt;code&gt;SkillTree&lt;/code&gt; components.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;SkillProvider&lt;/code&gt;: This takes in no props and supplies the children with the skill tree's context. This puppy handles all of the global data related to the skill tree. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;SkillTreeGroup&lt;/code&gt;: Is more involved in that it can take an optional &lt;code&gt;theme&lt;/code&gt; property, where we can pass in some custom styling, to make our skill tree feel very Borderlands. The &lt;code&gt;SkillTreeGroup&lt;/code&gt; also uses the children-as-a-function pattern to give us access to some imperative api functionality, such as skill tree reset, selected skills counter, etc. We don't need to worry about any of those for the scope of this article.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;SkillTree&lt;/code&gt;: This is the most exciting of the package's exports, unless you're a sucker for typings (which are also exported, for all you TS fans). The &lt;code&gt;SkillTree&lt;/code&gt; doesn't take any children but requires 3 props: &lt;code&gt;treeId&lt;/code&gt;, &lt;code&gt;title&lt;/code&gt;, and &lt;code&gt;data&lt;/code&gt;. The &lt;code&gt;treeId&lt;/code&gt; should be an id that's unique to each skill tree, but should be persistent across user sessions as this is used as the key for getting and setting the data to local storage. I'm not going to explain what the &lt;code&gt;title&lt;/code&gt; prop does, I'll leave you to experiment. The &lt;code&gt;data&lt;/code&gt; is the mixing pot of the application. You'll pass in your skill tree data structure which the app will use to render a &lt;code&gt;beautiful-skill-tree&lt;/code&gt;.  Let's get a real basic tree going before we move on to our multi-tree, multi-branch Borderlands spectacular. &lt;/p&gt;

&lt;p&gt;In App.tsx, import the 3 components like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;SkillProvider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;SkillTreeGroup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;SkillTree&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;beautiful-skill-tree&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Place it underneath your &lt;code&gt;img&lt;/code&gt; tag, outside of the image's container div, but within the outer div. Add the &lt;code&gt;SkillProvider&lt;/code&gt;, passing the &lt;code&gt;SkillTreeGroup&lt;/code&gt; as a child. Before you do the same with the &lt;code&gt;SkillTree&lt;/code&gt;, remember that as &lt;code&gt;SkillTreeGroup&lt;/code&gt; uses function-as-a-child pattern, you'll need to render a function that returns the child components. Return a single &lt;code&gt;SkillTree&lt;/code&gt; and give it a &lt;code&gt;treeId&lt;/code&gt; and a &lt;code&gt;title&lt;/code&gt; prop. Pass an empty array into the &lt;code&gt;data&lt;/code&gt; prop so your &lt;code&gt;App.tsx&lt;/code&gt; looks like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;App&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="c1"&gt;// &amp;lt;div&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;//  &amp;lt;div headercontent /&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;SkillProvider&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;SkillTreeGroup&lt;/span&gt;&lt;span class="o"&gt;&amp;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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;SkillTree&lt;/span&gt; &lt;span class="nx"&gt;treeId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;basic-birch&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;First Skill Tree&lt;/span&gt;&lt;span class="dl"&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="sr"&gt;/&lt;/span&gt;&lt;span class="err"&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/SkillTreeGroup&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="sr"&gt;/SkillProvider&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="c1"&gt;// &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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Go to &lt;a href="http://localhost:3000"&gt;localhost:3000&lt;/a&gt; to see the application running. You should see the logo, background, and a grey rectangle. If you're running into any errors, go through the introduction again and check to see if there any syntax error or incorrect imports. &lt;/p&gt;

&lt;p&gt;Next, let's create a real basic tree. Just 3 items that move in a linear line. The data structure for &lt;code&gt;data&lt;/code&gt; looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Skill&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;tooltip&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&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;Skill&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;Each skill requires four properties, with one being optional. You should also notice that the &lt;code&gt;children&lt;/code&gt; property is a recursive type, meaning that it takes an array of the same data structure, which it uses to render the children of the skill. This can go on infinitely, and make for some real complicated, winding trees. I'll create the first skill for you, and I'll trust you with carrying on for the next two items.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&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="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;first-skill&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;The root node&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;tooltip&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;The parent node, all of the descendants will be locked until it's selected&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="nl"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="c1"&gt;// rinse and repeat; always repeat.&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;Add the above snippet to the &lt;code&gt;App.tsx&lt;/code&gt; file, and replace the empty array inside of the &lt;code&gt;SkillTree&lt;/code&gt;'s &lt;code&gt;data&lt;/code&gt; property with our &lt;code&gt;data&lt;/code&gt; definition. Load your page, and you should have an interactive node. Give it a hover and a click and it should be reacting to your actions. If things are working, then I'll task you with creating two (or more) child nodes. Experiment with children and sibling lengths, to see what you can come up with. (If you also happen to break my precious package, leave me a &lt;a href=""&gt;GitHub issue&lt;/a&gt; so I can patch things up).&lt;/p&gt;

&lt;p&gt;Once you're comfortable with creating a skill tree, let's go ahead and create our Borderlands skill tree. Fortunately, I've done all of the tedious work for you and have already created the data structures and accumulated the images.&lt;/p&gt;

&lt;p&gt;You'll need to import the three trees from the &lt;code&gt;data&lt;/code&gt; file, which can be done via&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;motion&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;harmony&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cataclysm&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./data/data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The next step is creating two additional &lt;code&gt;SkillTrees&lt;/code&gt; alongside the current one. You'll need to wrap them in a &lt;code&gt;React.Fragment&lt;/code&gt; as your &lt;code&gt;SkillTreeGroup&lt;/code&gt; will now be trying to render 3 top level components. Pass in the data accordingly, and if you're unsure, I've posted the code snippet below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Fragment&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;SkillTree&lt;/span&gt; &lt;span class="nx"&gt;treeId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;motion&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Motion&lt;/span&gt;&lt;span class="dl"&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;motion&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;SkillTree&lt;/span&gt; &lt;span class="nx"&gt;treeId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;harmony&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Harmony&lt;/span&gt;&lt;span class="dl"&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;harmony&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;SkillTree&lt;/span&gt; &lt;span class="nx"&gt;treeId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cataclysm&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Cataclysm&lt;/span&gt;&lt;span class="dl"&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;cataclysm&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="sr"&gt;/React.Fragment&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Go ahead and check your web browser, it should be &lt;strong&gt;aaallmoost&lt;/strong&gt; ready. We've got the skills rendered, but the styling feels a little lackluster. It doesn't feel very Borderlands. Fortunately for you, I'm a regular &lt;a href="https://www.youtube.com/watch?v=0evlWSY8kTc"&gt;Neil Buchanan&lt;/a&gt; and prepared a custom theme. Import the theme and pass it through to the &lt;code&gt;SkillTreeGroup&lt;/code&gt;'s &lt;code&gt;theme&lt;/code&gt; prop. The theme object is export via &lt;code&gt;import theme from './data/theme';&lt;/code&gt;. Easy!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://thepracticaldev.s3.amazonaws.com/i/efhb04w09pc44j8z44dr.png"&gt;https://thepracticaldev.s3.amazonaws.com/i/efhb04w09pc44j8z44dr.png&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you've done the above, check out the finished product. If you're still not satisfied with the styles, check out the theme object and customise it yourself, there are a bunch of additional attributes whose styles can be adjusted, so just peek into the typings of the package.&lt;/p&gt;

&lt;p&gt;I mentioned earlier that there are a few additional properties and values that can be used to tweak the skill tree, so have a mess around yourself, and link me to any cool trees you create. I'd love to add it to the growing list of trees found &lt;a href="https://github.com/andrico1234/beautiful-skill-tree#examples"&gt;here&lt;/a&gt;. &lt;a href="https://calisthenicsskills.com/"&gt;Here's&lt;/a&gt; an example of the skill tree that kickstarted this obsession. &lt;/p&gt;

&lt;p&gt;I hope you've enjoyed tinkering with the &lt;code&gt;beautiful-skill-tree&lt;/code&gt; package. I'm always adding new features and updating, so give it a star on github! You can find an online demo of the borderlands skill tree &lt;a href="http://borderlands-skill-tree.s3-website.eu-west-2.amazonaws.com/"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can find me on &lt;a href=""&gt;Instagram&lt;/a&gt; or GitHub if you want to chat code, music or fitness!&lt;/p&gt;

</description>
      <category>react</category>
      <category>typescript</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
