<?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: Estee Tey</title>
    <description>The latest articles on DEV Community by Estee Tey (@lyqht).</description>
    <link>https://dev.to/lyqht</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%2F278796%2F66803230-d39c-49a4-aded-308c149ed7e6.png</url>
      <title>DEV Community: Estee Tey</title>
      <link>https://dev.to/lyqht</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lyqht"/>
    <language>en</language>
    <item>
      <title>Expenses made easier with Cloudflare AI</title>
      <dc:creator>Estee Tey</dc:creator>
      <pubDate>Thu, 11 Apr 2024 14:50:45 +0000</pubDate>
      <link>https://dev.to/lyqht/expense-journal-with-cloudflare-ai-5f05</link>
      <guid>https://dev.to/lyqht/expense-journal-with-cloudflare-ai-5f05</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/devteam/join-us-for-the-cloudflare-ai-challenge-3000-in-prizes-5f99"&gt;Cloudflare AI Challenge&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;This project is a &lt;strong&gt;Cloudflare serverless worker&lt;/strong&gt; with an AI service binding deployed for processing either audio, image or text submitted by users into semantic useful information, especially in the context for &lt;em&gt;submitting expenses&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;For users that prefer having a UI to interact with, a simple &lt;strong&gt;NextJS web app&lt;/strong&gt; is created to demonstrate how to upload files and utilizes API routes to make a POST request to the Cloudflare worker endpoint.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;The diagram below looks a bit blurry on this platform, please refer to the raw image &lt;a href="https://github.com/lyqht/cf-journal/blob/main/demo/How%20it%20works.png?raw=true" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&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%2Fgithub.com%2Flyqht%2Fcf-journal%2Fblob%2Fmain%2Fdemo%2FHow%2520it%2520works.png%3Fraw%3Dtrue" 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%2Fgithub.com%2Flyqht%2Fcf-journal%2Fblob%2Fmain%2Fdemo%2FHow%2520it%2520works.png%3Fraw%3Dtrue" alt="Illustrates the user journey below"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;User can pass any of the following types of content - audio, image or text. Based on the input type given, we will process them eventually to form JSON like this that &lt;strong&gt;can be used by any app&lt;/strong&gt; to display useful expense information for them.&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;SAMPLE_RECORD&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Expense&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2024-04-11&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;time&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;14:00&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;product&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Rubber Ducky&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;expenditure&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;100.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;h3&gt;
  
  
  Models used in this project:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Image-to-text: &lt;a href="https://developers.cloudflare.com/workers-ai/models/uform-gen2-qwen-500m/" rel="noopener noreferrer"&gt;@cf/unum/uform-gen2-qwen-500m&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Automatic Speech Recognition: &lt;a href="https://developers.cloudflare.com/workers-ai/models/whisper/" rel="noopener noreferrer"&gt;@cf/openai/whisper&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Text Generation: &lt;a href="https://developers.cloudflare.com/workers-ai/models/mistral-7b-instruct-v0.1/" rel="noopener noreferrer"&gt;@hf/thebloke/mistral-7b-instruct-v0.1-awq&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Try out the worker!
&lt;/h3&gt;

&lt;p&gt;The worker is deployed at &lt;a href="https://cf-journal.senchatea.workers.dev" rel="noopener noreferrer"&gt;https://cf-journal.senchatea.workers.dev&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Text input&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--location&lt;/span&gt; &lt;span class="s1"&gt;'https://cf-journal.senchatea.workers.dev?type=text'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: text/plain'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="s1"&gt;'2 weeks ago, I went to eat a buffet at Swensens Unlimited at the T2 airport, it'&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;'s really nice but it costs like $36 per person after GST, and there were 2 of us.'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Audio input&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--location&lt;/span&gt; &lt;span class="s1"&gt;'https://cf-journal.senchatea.workers.dev?type=audio'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/octet-stream'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Authorization: Bearer pYrzMvsyURxsCUeaQDsa3lSO_tBDQEuiPB3iLEQt'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="s1"&gt;'@postman-cloud:///1eef4b5b-a64c-4c10-ad4b-5f22c528880b'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Image input&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--location&lt;/span&gt; &lt;span class="s1"&gt;'https://cf-journal.senchatea.workers.dev?type=image'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/octet-stream'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Authorization: Bearer pYrzMvsyURxsCUeaQDsa3lSO_tBDQEuiPB3iLEQt'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="s1"&gt;'@postman-cloud:///1eef4b68-e5d7-49e0-a28d-f60b3bd7d70e'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Try it in the web app!
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://cf-journal.vercel.app/" rel="noopener noreferrer"&gt;https://cf-journal.vercel.app/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Flyqht%2Fcf-journal%2Fblob%2Fcc8bc6d1aae0a638214d5ab197c5d7c1e8425237%2Fdemo%2Fweb_demo.gif%3Fraw%3Dtrue" 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%2Fgithub.com%2Flyqht%2Fcf-journal%2Fblob%2Fcc8bc6d1aae0a638214d5ab197c5d7c1e8425237%2Fdemo%2Fweb_demo.gif%3Fraw%3Dtrue" alt="On load of the app, user sees a sample record of a Rubber Ducky costing $100, but not for sale. User then tries to upload a .mp3 voice recording file and sees "&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  My Code
&lt;/h2&gt;

&lt;p&gt;The project has 2 folders:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;serverless&lt;/code&gt;: Code that is deployed to a Cloudflare worker. You can find the text generation prompt &amp;amp; code to use the AI models at &lt;code&gt;utils.ts&lt;/code&gt;. The Cloudflare worker request handler is found at &lt;code&gt;main.ts&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;app&lt;/code&gt;: Nextjs app code that is deployed to Vercel&lt;/li&gt;
&lt;/ul&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&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%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/lyqht" rel="noopener noreferrer"&gt;
        lyqht
      &lt;/a&gt; / &lt;a href="https://github.com/lyqht/cloudflare-journal" rel="noopener noreferrer"&gt;
        cloudflare-journal
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Introduction&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;This project is a &lt;strong&gt;Cloudflare serverless worker&lt;/strong&gt; with an AI service binding deployed for processing either audio, image or text submitted by users into semantic useful information, especially in the context for &lt;em&gt;submitting expenses&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;For users that prefer a UI, a simple &lt;strong&gt;NextJS web app&lt;/strong&gt; is created to demonstrate how to upload files and utilizes API routes to make a POST request to the Cloudflare worker endpoint.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;How it works&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/lyqht/cloudflare-journal./demo/How%20it%20works.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Flyqht%2Fcloudflare-journal.%2Fdemo%2FHow%2520it%2520works.png" alt="An image of a green tea bottle is inputted into an &amp;quot;image-to-text&amp;quot; system which describes the bottle and its features. The description is then converted into a structured JSON format with fields like &amp;quot;date,&amp;quot; &amp;quot;type,&amp;quot; &amp;quot;brand,&amp;quot; and &amp;quot;item,&amp;quot; most of which are left null except for &amp;quot;type,&amp;quot; which is &amp;quot;product,&amp;quot; and &amp;quot;item,&amp;quot; which is &amp;quot;Green Tea.&amp;quot; An audio clip is transcribed by an &amp;quot;audio-to-text&amp;quot; system, with the spoken words &amp;quot;I went to the gym for an hour today. The session cost around 20 dollars.&amp;quot; This transcription is then converted into JSON format with fields such as &amp;quot;date,&amp;quot; &amp;quot;time,&amp;quot; &amp;quot;type,&amp;quot; &amp;quot;item,&amp;quot; &amp;quot;expenditure,&amp;quot; and others, some filled with specific data from the audio input like &amp;quot;type&amp;quot; as &amp;quot;activity,&amp;quot; &amp;quot;item&amp;quot; as &amp;quot;gym session,&amp;quot; and &amp;quot;expenditure&amp;quot; as 20. A text input reading &amp;quot;2 weeks ago, I went to eat a buffet at Swensens Unlimited at the T2 airport, it's really nice but it costs like $36 per person after GST, and there were 2 of us.&amp;quot; is converted directly into a JSON format with details of the dining experience including &amp;quot;date,&amp;quot; &amp;quot;time,&amp;quot; &amp;quot;type,&amp;quot; &amp;quot;item,&amp;quot; and &amp;quot;expenditure,&amp;quot; among others. The &amp;quot;item&amp;quot; is listed as &amp;quot;Swensens Unlimited,&amp;quot; and &amp;quot;expenditure&amp;quot; is doubled to 72, considering two people"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Models used&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;From &lt;a href="https://developers.cloudflare.com/workers-ai/models" rel="nofollow noopener noreferrer"&gt;Cloudflare AI Models&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Image-to-text: &lt;a href="https://developers.cloudflare.com/workers-ai/models/uform-gen2-qwen-500m/" rel="nofollow noopener noreferrer"&gt;@cf/unum/uform-gen2-qwen-500m&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Automatic Speech Recognition: &lt;a href="https://developers.cloudflare.com/workers-ai/models/whisper/" rel="nofollow noopener noreferrer"&gt;@cf/openai/whisper&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Text Generation: &lt;a href="https://developers.cloudflare.com/workers-ai/models/mistral-7b-instruct-v0.1/" rel="nofollow noopener noreferrer"&gt;@hf/thebloke/mistral-7b-instruct-v0.1-awq&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Try out the worker!&lt;/h2&gt;

&lt;/div&gt;

&lt;p&gt;The worker is deployed at &lt;a href="https://cf-journal.senchatea.workers.dev" rel="nofollow noopener noreferrer"&gt;https://cf-journal.senchatea.workers.dev&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Text input&lt;/p&gt;

&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;curl --location &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;'&lt;/span&gt;https://cf-journal.senchatea.workers.dev?type=text&lt;span class="pl-pds"&gt;'&lt;/span&gt;&lt;/span&gt; \
--header &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;'&lt;/span&gt;Content-Type: text/plain&lt;span class="pl-pds"&gt;'&lt;/span&gt;&lt;/span&gt; \
--data &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;'&lt;/span&gt;2 weeks ago, I went to eat a buffet at Swensens Unlimited at the T2 airport, it&lt;span class="pl-pds"&gt;'&lt;/span&gt;&lt;/span&gt;&lt;span class="pl-cce"&gt;\'&lt;/span&gt;&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;'&lt;/span&gt;s really nice but it costs like $36 per person after GST, and there were&lt;/span&gt;&lt;/pre&gt;…
&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/lyqht/cloudflare-journal" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



&lt;h2&gt;
  
  
  Journey
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How the idea came about
&lt;/h3&gt;

&lt;p&gt;Before OpenAI introduced ChatGPT and revitalized the AI scene, I once created &lt;a href="https://github.com/lyqht/Billy" rel="noopener noreferrer"&gt;Billy&lt;/a&gt;, a cute a little expense tracker app to help my mom to track her expenses easier. It actually received good reception with my friends both online &amp;amp; offline because of the clean and straight forward UI. &lt;/p&gt;

&lt;p&gt;However, the user retention wasn't very good because &lt;strong&gt;the act of logging expenses is very tedious&lt;/strong&gt; because of the multitude of fields to fill. It was very difficult to encourage the habit of logging expenses for the users.&lt;/p&gt;

&lt;p&gt;For many users, it is much direct and easier to talk or take a picture with a phone as compared to typing. But it was rather difficult to interpret information from the data of these input types back then, so I dropped that project eventually.&lt;/p&gt;

&lt;p&gt;Now, by utilizing different AI models, we can interpret and organize important information for them more easily, simplifying the expense submission process.&lt;/p&gt;

&lt;p&gt;This project is meant to be a MVP to demonstrate that.&lt;/p&gt;

&lt;h3&gt;
  
  
  Room for improvements
&lt;/h3&gt;

&lt;p&gt;I'm very new to AI so there will definitely a lot of things to improve on!&lt;/p&gt;

&lt;p&gt;The webapp in this submission is a very early MVP of what I envision the actual expense tracker app to be. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For now, it only create expenses based on the information in the file input (audio/mp3/text).&lt;/li&gt;
&lt;li&gt;Ideally after submission, you can still modify the individual fields of the expense form. The AI portion is just there to assist the auto-filling process.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm also not very familiar with prompt engineering, but I would also like to improve the text generation prompt in the future.&lt;/p&gt;

&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;p&gt;These are some helpful resources from Cloudflare that I have used to work on this project since I wasn't familiar with the AI models available for text generation/inference.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://playground.ai.cloudflare.com/" rel="noopener noreferrer"&gt;Workers AI LLM Playground&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developers.cloudflare.com/workers-ai/tutorials/how-to-choose-the-right-text-generation-model/" rel="noopener noreferrer"&gt;Guide on choosing the right
text generation model&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;My project is eligible for the &lt;strong&gt;Triple Task&lt;/strong&gt; type category.&lt;/p&gt;

</description>
      <category>cloudflarechallenge</category>
      <category>devchallenge</category>
      <category>ai</category>
    </item>
    <item>
      <title>DeepL Translate Github Action</title>
      <dc:creator>Estee Tey</dc:creator>
      <pubDate>Mon, 22 May 2023 16:32:28 +0000</pubDate>
      <link>https://dev.to/lyqht/deepl-translate-github-action-3nk0</link>
      <guid>https://dev.to/lyqht/deepl-translate-github-action-3nk0</guid>
      <description>&lt;h2&gt;
  
  
  What I built
&lt;/h2&gt;

&lt;p&gt;I created a &lt;a href="https://github.com/lyqht/deepl-translate-github-action" rel="noopener noreferrer"&gt;github action that translates text documents&lt;/a&gt; into target languages that &lt;a href="https://www.deepl.com/" rel="noopener noreferrer"&gt;DeepL&lt;/a&gt; supports. DeepL supports 32 languages at the moment.&lt;/p&gt;

&lt;p&gt;DeepL is a AI translation service that uses deep learning techniques to translate entire sentences from one language to another, rather than translating words individually. It offers a rather generous free API for small translation volumes, so hobbyists like me and you 😉 or pretty much any open source project maintainer can try it out!&lt;/p&gt;

&lt;p&gt;The GitHub action main script has been tested on simple text documents, as well as relatively complex text documents such as &lt;a href="https://dev.tourl"&gt;Supabase's README.md&lt;/a&gt; which contains many different symbols and html elements.&lt;/p&gt;

&lt;h3&gt;
  
  
  Category Submission
&lt;/h3&gt;

&lt;p&gt;Maintainer Must-Haves&lt;/p&gt;

&lt;h3&gt;
  
  
  App Link
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/lyqht/deepl-translate-github-action" rel="noopener noreferrer"&gt;https://github.com/lyqht/deepl-translate-github-action&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Screenshots
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw512f3n6zhjo4b3zpmzw.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%2Fw512f3n6zhjo4b3zpmzw.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;PR for screenshot can be found &lt;a href="https://github.com/lyqht/deepL-demo/pull/2" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I updated &lt;code&gt;original.md&lt;/code&gt; here, and you can see the GitHub action generating a commit for the translated &lt;code&gt;ja.md&lt;/code&gt; with very accurate translation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Description
&lt;/h3&gt;

&lt;p&gt;This action uses the DeepL Translate API to translate text files in your repository to your target languages. The output will follow the file extension of the given input file.&lt;/p&gt;

&lt;h3&gt;
  
  
  Link to Source Code
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/lyqht/deepl-translate-github-action" rel="noopener noreferrer"&gt;https://github.com/lyqht/deepl-translate-github-action&lt;/a&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Permissive License
&lt;/h3&gt;

&lt;p&gt;MIT&lt;/p&gt;

&lt;h2&gt;
  
  
  Background (What made you decide to build this particular app? What inspired you?)
&lt;/h2&gt;

&lt;p&gt;I've been thinking about accessibility, and they say pictures speak a thousand words, but sometimes we really should express ourselves through words. That makes alt text and semantic html structure important as focal points of accessibility for the general public. We can also include language in there, by making sure that text in a native language can easily be offered to the user that is viewing the content so they can enjoy it too.&lt;/p&gt;

&lt;p&gt;While there are many plugins for i18n nowadays, the task of translating the text itself isn't trivial. Hence, I built this to allow easy translation of files in GitHub repositories using the DeepL API.&lt;/p&gt;

&lt;h3&gt;
  
  
  How I built it (How did you utilize GitHub Actions or GitHub Codespaces? Did you learn something new along the way? Pick up a new skill?)
&lt;/h3&gt;

&lt;p&gt;I have built 2 other GitHub actions before:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/lyqht/generate-supabase-db-types-github-action" rel="noopener noreferrer"&gt;generate-supabase-db-types-github-action&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/lyqht/article-badge-counter-github-action" rel="noopener noreferrer"&gt;article-badge-counter-github-action&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the little experience I have does help me to build this project easier, but I still got to learn new stuff about the DeepL API stuff (both how cool/convenient it is and its limitations) and how to pass secret environment variables and inputs to the GitHub Action runner properly so that the file paths are parsed correctly.&lt;/p&gt;

&lt;p&gt;I also tried GitHub Codespaces to create the project, it's pretty cool and easy to use.&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%2Fij08tqomfb2b7xi2m0ee.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%2Fij08tqomfb2b7xi2m0ee.png" alt="Preview of codespace in GitHub"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The generated codespace was named after a birdie, how cute!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Additional Resources/Info
&lt;/h3&gt;

&lt;p&gt;For a demo, refer to this repository &lt;a href="https://github.com/lyqht/deepl-demo" rel="noopener noreferrer"&gt;deepl-demo&lt;/a&gt;&lt;/p&gt;

</description>
      <category>githubhack23</category>
      <category>javascript</category>
      <category>githubactions</category>
      <category>github</category>
    </item>
    <item>
      <title>My Hacktoberfest Experience For The Third Time: 2022 Edition</title>
      <dc:creator>Estee Tey</dc:creator>
      <pubDate>Thu, 03 Nov 2022 04:25:18 +0000</pubDate>
      <link>https://dev.to/lyqht/my-hacktoberfest-experience-for-the-third-time-2022-edition-4cib</link>
      <guid>https://dev.to/lyqht/my-hacktoberfest-experience-for-the-third-time-2022-edition-4cib</guid>
      <description>&lt;p&gt;It is my 3rd time joining Hacktoberfest. This time, I chose to join the event as both a maintainer and a contributor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Being a contributor
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://blog.esteetey.dev/my-second-attempt-at-hacktoberfest-2021-edition"&gt;Last year, I was quite active as a contributor&lt;/a&gt;. However, this year, I went on a 3-week holiday and couldn’t access the computer as much.&lt;/p&gt;

&lt;p&gt;Since I didn’t have too much time to code, I picked up some simpler issues to work on that still give me some room for learning - since I’ve not implemented the features to be worked on are stuff that before.&lt;/p&gt;

&lt;h3&gt;
  
  
  My contributions
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--w11hw-kR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1667237175632/K8MylAcUL.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--w11hw-kR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1667237175632/K8MylAcUL.png" alt="My progress in Hacktoberfest as a contributor - as listed below" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This year, discounting the 1 PR counted to my own project, I have merged 6 PRs to other open source projects. These are the open source projects that I've contributed to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://github.com/withfig/autocomplete"&gt;Fig Autocomplete&lt;/a&gt;: A tool that adds autocomplete to your terminal

&lt;ul&gt;
&lt;li&gt;Autogenerated Supabase Fig autocomplete specs using Fig's integration with golang CLI framework, Cobra.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://supabase.com/docs/reference/cli"&gt;supabase&lt;/a&gt;: An awesome open source alternative to Firebase, where it provides you with a Postgres database, Authentication, instant APIs, Edge Functions, Realtime subscriptions, and Storage.

&lt;ul&gt;
&lt;li&gt;Updated docs for Supabase Fig autocomplete specs&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://nodejs.dev"&gt;nodejs.dev&lt;/a&gt;: Nodejs.org website

&lt;ul&gt;
&lt;li&gt;Added a simple GitHub action to sync auto-generated docs daily, and added prettier formatting for .yml files&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/voracious/octo"&gt;octo&lt;/a&gt;: A minimal knowledge system app like Obsidian

&lt;ul&gt;
&lt;li&gt;Added import and export markdown documents functionality&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It was nice to learn how to do different things as a developer by being a contributor 😊 However, being a maintainer is another story. &lt;/p&gt;

&lt;h2&gt;
  
  
  Being a maintainer
&lt;/h2&gt;

&lt;p&gt;While I do not fully understand the concept of open source in its totality, I really like the idea of &lt;strong&gt;building&lt;/strong&gt;, &lt;strong&gt;collaborating&lt;/strong&gt; and &lt;strong&gt;learning together with other developers&lt;/strong&gt; to write better quality code that also translates into features. In my free time after work, I have created a few open source apps using tech stacks that I was keen to learn about and these apps usually also help to serve a specific use case for myself. I have usually worked on these projects by myself, so I thought it will be a nice idea to leverage on Hacktoberfest this year to work together with the GitHub community on these projects.&lt;/p&gt;

&lt;h3&gt;
  
  
  Projects participating in Hacktoberfest
&lt;/h3&gt;

&lt;p&gt;For Hacktoberfest, I have chosen to open up 4 of my open source side projects for contributions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Er1q9aDe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1667237531907/_c6K5I7cX.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Er1q9aDe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1667237531907/_c6K5I7cX.png" alt="hacktoberfest projects as listed below" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/lyqht/Billy"&gt;Billy&lt;/a&gt;: a mobile expense tracker application made with React Native, with a simple Docsaurus documentation website&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/lyqht/Octokit-lite"&gt;Octokit-lite&lt;/a&gt;: a web app to perform GitHub operations on multiple repositories easily, made with Next.js. Inspired by GitHub's &lt;a href="https://github.com/octokit"&gt;Octokit&lt;/a&gt; and Hacktoberfest's &lt;a href="https://github.com/Hacktoberfest/hacktoberfest-repo-topic-apply"&gt;hacktoberfest-repo-topic-apply&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/lyqht/generate-supabase-db-types-github-action"&gt;generate-supabase-db-types-action&lt;/a&gt;: a GitHub action to create PRs for creating/updating Supabase type definitions file in the project&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/lyqht/awesome-supabase"&gt;awesome-supabase&lt;/a&gt;: an awesome-list of official+community starters &amp;amp; resources. &lt;em&gt;This project is created during Hacktoberfest itself as a way to encourage non-code contributions!&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How I decide the issues to be created
&lt;/h3&gt;

&lt;p&gt;To make my project easier for contributing for newcomers, I tried to &lt;strong&gt;restrict the scope&lt;/strong&gt; of most issues I create to simpler frontend issues.&lt;/p&gt;

&lt;p&gt;In every issue, I also give a simple &lt;strong&gt;suggested solution&lt;/strong&gt;, where &lt;strong&gt;they can propose suitable alternatives&lt;/strong&gt;. I also try to provide relevant code e.g. which method or files to modify, but not to give them too much details on how to implement the logic so that they get the chance to practice problem solving themselves. &lt;/p&gt;

&lt;h3&gt;
  
  
  Getting contributions
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Octokit-lite&lt;/em&gt; is my &lt;strong&gt;most active project&lt;/strong&gt; for Hacktoberfest in terms of contributions received and the time/effort that I’ve invested in collaboration.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;5 people picked up issues within the &lt;strong&gt;1st day of sharing this project&lt;/strong&gt; on &lt;a href="https://discord.com/invite/hacktoberfest"&gt;Hacktoberfest Discord&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;3 PRs are merged over the &lt;strong&gt;1st weekend&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Total of 10 PRs merged to &lt;em&gt;Octokit-lite&lt;/em&gt; &lt;strong&gt;during the entire event&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Contributions to &lt;em&gt;Octokit-lite&lt;/em&gt; vary in size and below are some examples of how I gauge the size&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;XXS: adding a Code of Conduct file (&lt;a href="https://github.com/lyqht/Octokit-lite/pull/17"&gt;Sumit Bisht&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;XS: Input reset after deleting repos at &lt;em&gt;Unfork&lt;/em&gt; (&lt;strong&gt;&lt;a href="https://github.com/lyqht/Octokit-lite/pull/26"&gt;najeebkp&lt;/a&gt;)&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;S: Improve History Logs Page layout (&lt;a href="https://github.com/lyqht/Octokit-lite/pull/19"&gt;LoftyBrambles&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;M: Sort repos by last push date at &lt;em&gt;Unfork&lt;/em&gt; (&lt;a href="https://github.com/lyqht/Octokit-lite/issues?q=is%3Apr+author%3AIlikepizza2"&gt;ilikepizza2&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;L: Add &lt;em&gt;Unlabel&lt;/em&gt; to &lt;em&gt;Octokit-lite&lt;/em&gt;: functionality of removing label(s) from selected repositories (&lt;a href="https://github.com/Dolir"&gt;Dolir&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I usually create L size issues for my own personal tracking of what to work on next, so it was &lt;strong&gt;a pleasant surprise&lt;/strong&gt; for me to see that there would be developers from the community who would volunteer to pick up issues of this scale! ✨&lt;/p&gt;

&lt;p&gt;Aside from Octokit-lite, I also merged 1 PR to fix styling on bullet points &amp;amp; video rendering for Billy’s docsaurus website, 1 PR for updating generate-supabase-db-types-action to use the latest &lt;a href="https://github.com/supabase/setup-cli"&gt;supabase CLI&lt;/a&gt; to generate types, and 4 awesome-supabase PRs merged that added online courses, starters &amp;amp; update links for Supabase resources.&lt;/p&gt;

&lt;h3&gt;
  
  
  An unexpected collaboration
&lt;/h3&gt;

&lt;p&gt;While sharing my project on Twitter, I got to know of &lt;a href="https://twitter.com/gr2m"&gt;Gregor Martynus&lt;/a&gt;, the maintainer of &lt;a href="https://github.com/octokit/octokit.js/"&gt;the JavaScript Octokit&lt;/a&gt; – GitHub's official JavaScript SDK and his project &lt;em&gt;&lt;a href="https://github.com/octoherd/octoherd"&gt;Octoherd&lt;/a&gt;&lt;/em&gt;. He &lt;a href="https://twitter.com/gr2m/status/1575883757034184711"&gt;tweeted a reply&lt;/a&gt; that it will be nice to have a GUI like &lt;em&gt;Octokit-lite&lt;/em&gt; for &lt;em&gt;Octoherd&lt;/em&gt;, and that sparked an inspiration for me ✨&lt;/p&gt;

&lt;p&gt;I thought that having an Octoherd integration will be really promising and useful since &lt;em&gt;Octokit-lite&lt;/em&gt; was intended to be a compilation of microapps to help users perform handy GitHub operations on repositiories in bulk, and this meant achieving &lt;strong&gt;a similar goal&lt;/strong&gt; as Octoherd. In fact, &lt;em&gt;Octokit-lite&lt;/em&gt;’s name is inspired by Octokit, so it is no surprise that the two have similar goals 😊&lt;/p&gt;

&lt;p&gt;Then, I tried to think about how technically feasible this integration is and wrote down some tasks down in &lt;a href="https://github.com/lyqht/Octokit-lite/issues/15"&gt;an issue&lt;/a&gt;. There, I also seeked Gregor’s advice on how we can go about adding Octoherd functionality. &lt;/p&gt;

&lt;p&gt;While I was worried that the implementation plan isn’t perfect before executing it, he said something that felt really assuring.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I'd say go whatever path works for you right now. Make it work, then make it great, and let the community help. - Gregor Martynus&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Being a creator of an open source project has made me feel &lt;em&gt;very obliged&lt;/em&gt; to be the expert who can answer every question about how to go about doing anything, but that didn’t necessarily have to be the way. While I’m already standing on the shoulders of giants who provided cool open source libraries to use in my project, I can still continue to tap on many other sources of knowledge — and that’s &lt;strong&gt;the beauty of open source&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This also brings me to talk about my Hacktoberfest experience as a whole.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overall Experience
&lt;/h2&gt;

&lt;h3&gt;
  
  
  It turned out better than expected
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9UgoXJPv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.holopin.io/_next/image%3Furl%3Dhttps%253A%252F%252Fassets.holopin.io%252FeyJidWNrZXQiOiJob2xvcGluLWFzc2V0cyIsImtleSI6ImFzc2V0cy9jbDhkOHVrb3MwMDk0MDlqbnVuaGRhcDd3IiwiZWRpdHMiOnsicm90YXRlIjpudWxsfX0%253D%26w%3D1920%26q%3D75" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9UgoXJPv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.holopin.io/_next/image%3Furl%3Dhttps%253A%252F%252Fassets.holopin.io%252FeyJidWNrZXQiOiJob2xvcGluLWFzc2V0cyIsImtleSI6ImFzc2V0cy9jbDhkOHVrb3MwMDk0MDlqbnVuaGRhcDd3IiwiZWRpdHMiOnsicm90YXRlIjpudWxsfX0%253D%26w%3D1920%26q%3D75" alt="A pretty Level 4 Contributor Badge by Hacktoberfest awarded on Holopin for having 4 merged PRs" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Despite the 3-week holiday, Hacktoberfest was still a refreshing experience for me. Being a contributor this year was much more relaxed since I wasn’t as ambitious as &lt;a href="https://blog.esteetey.dev/my-second-attempt-at-hacktoberfest-2021-edition"&gt;last year&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;On the other hand, it is my very first time being a maintainer so I was pretty afraid I would screw up a lot, but the experience turned out better than expected 😊. To be honest, I thought no one would even volunteer to help out on my side projects but I'm glad that I was proven otherwise 😄! &lt;/p&gt;

&lt;p&gt;As I learn to work with my contributors, I understand better how to write better setup instructions (big thanks to my first contributor &lt;a href="https://github.com/Lofty-Brambles"&gt;LoftyBrambles&lt;/a&gt; for pointing out problems and helping other contributors!) and learn how to give clearer review comments to work together with my contributors on pull requests. &lt;/p&gt;

&lt;p&gt;However, some things didn’t go as well as I wanted as a maintainer during Hacktoberfest.&lt;/p&gt;

&lt;h3&gt;
  
  
  Confusion with the event rules
&lt;/h3&gt;

&lt;p&gt;The rules for Hacktoberfest was slightly different this year, which has caused some confusion for me as well as the contributors that helped out with my project. &lt;/p&gt;

&lt;p&gt;Last year, it was required for the issues/PRs has the &lt;strong&gt;label&lt;/strong&gt; &lt;code&gt;hacktoberfest&lt;/code&gt;, to be counted. This year, as long as the project has the &lt;strong&gt;topic&lt;/strong&gt; &lt;code&gt;hacktoberfest&lt;/code&gt;&lt;strong&gt;,&lt;/strong&gt; any PR merged for that project will count for users that have signed up for Hacktoberfest. This has led to me/other contributors asking for project maintainers to add the label to the issue &amp;amp; PR. 😂&lt;/p&gt;

&lt;p&gt;On the other hand, the &lt;code&gt;hacktoberfest-accepted&lt;/code&gt; &lt;strong&gt;label&lt;/strong&gt; on PRs is only meant for keeping track of PRs merged &lt;strong&gt;for maintainers.&lt;/strong&gt; However as a maintainer, I don’t really see the website helping me to keep track of this even though I signed up as both a contributor and a maintainer - so honestly I don’t know how well I’m doing as a maintainer. Even though I have over 10 merged &lt;code&gt;hacktoberfest-accepted&lt;/code&gt; PRs, I don’t know if that is good or not.&lt;/p&gt;

&lt;h3&gt;
  
  
  It is difficult to be a maintainer
&lt;/h3&gt;

&lt;p&gt;Although my project can be considered as a relatively small scale project, however, I do experience some troubles as follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Ghosting by contributors&lt;/strong&gt;: This is a quite common thing that happens for open source projects. However, this is not ideal during the Hacktoberfest period because hoarding these issues and not working on them meant that other contributors who were keen to contribute to the project would be turned away due to the lack of open issues to be worked on. I had quite a few issues that got ghosted by my assignees and had to be reassigned. This could also happen multiple times for the same issue.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Getting spammed by contributors to merge PRs&lt;/strong&gt;: I wish that I didn't have to manually point out that I was on holiday, but there was an instance where a contributor pinged me quite frequently to review the PR. I hope the open source community understands that maintainers are also normal people living out their lives and often have other priorities on hand. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Getting questions on non-project-specific problems&lt;/strong&gt;: I had contributors asking me for help on very basic problems such as having a outdated node version causing package prroblems, not knowing how to use &lt;code&gt;.env&lt;/code&gt; file to set environment variables. While I’m not sure how to ensure that the contributors have a basic understanding of these things before jumping into the issues, it’s a little frustrating when I get these sort of questions because it shows a significant lack of effort on the contributors’ side in troubleshooting problems and it also wastes my time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Contributions start to decline after the 2nd week&lt;/strong&gt;: Probably because many contributors have already completed Hacktoberfest by then, there’s a significant drop in activity on my projects thereafter. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lack of discord community:&lt;/strong&gt; I took the time to look up on how to create an ok-ish discord server with project specific channels, however aside from my friend, no one else joined it 😂This is also attributed to the fact that I created this discord only after the 2nd week of Hacktoberfest where contributors have become less active.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  That’s a wrap!
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6fbv8uJt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://c.tenor.com/eoM1uCVuXtkAAAAM/yay-excited.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6fbv8uJt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://c.tenor.com/eoM1uCVuXtkAAAAM/yay-excited.gif" alt="https://c.tenor.com/eoM1uCVuXtkAAAAM/yay-excited.gif" width="220" height="124"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thank you for reading, hope you enjoyed the article!&lt;/p&gt;

&lt;p&gt;Did you joined Hacktoberfest too? Share your experience below in the comments, I would love to hear it!&lt;/p&gt;

&lt;p&gt;If you find the article awesome, hit the &lt;em&gt;reactions&lt;/em&gt; 🧡 and &lt;em&gt;share&lt;/em&gt; it 🐦~&lt;/p&gt;

&lt;p&gt;To stay updated whenever I post new stuff, follow me on &lt;a href="https://twitter.com/estee_tey"&gt;Twitter&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;To support me on content creation, you can &lt;a href="https://blog.esteetey.dev/sponsor"&gt;buy a cup of tea&lt;/a&gt; for me ✨&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>hacktoberfest</category>
      <category>opensource</category>
      <category>codenewbie</category>
      <category>community</category>
    </item>
    <item>
      <title>How to create and test a GitHub Action that generates Supabase database types</title>
      <dc:creator>Estee Tey</dc:creator>
      <pubDate>Sat, 07 May 2022 09:32:58 +0000</pubDate>
      <link>https://dev.to/lyqht/how-to-create-and-test-a-github-action-that-generates-supabase-database-types-1l6b</link>
      <guid>https://dev.to/lyqht/how-to-create-and-test-a-github-action-that-generates-supabase-database-types-1l6b</guid>
      <description>&lt;h2&gt;
  
  
  How to generate types from a Supabase database
&lt;/h2&gt;

&lt;p&gt;On the Supabase documentation, there is a page on &lt;a href="https://supabase.com/docs/guides/api/generating-types"&gt;generating types&lt;/a&gt;. Following the instructions, the script to run looks something like this. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note the additional &lt;code&gt;--version=2&lt;/code&gt; parameter. This probably became a mandatory prop after some update to the &lt;code&gt;openapi-typescript&lt;/code&gt; tool.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx openapi-typescript https://&lt;span class="o"&gt;{&lt;/span&gt;projectId&lt;span class="o"&gt;}&lt;/span&gt;.supabase.co/rest/v1/?apikey&lt;span class="o"&gt;={&lt;/span&gt;specialApiKey&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;--output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;types/supabase.ts &lt;span class="nt"&gt;--version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The values for the anon key and URL can be found in the Supabase dashboard.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3ok1mT_4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1649239351647/ANXQsJpXa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3ok1mT_4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1649239351647/ANXQsJpXa.png" alt="Screenshot of Supabase project dashboard" width="800" height="588"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Running the script generates a file like &lt;a href="https://github.com/lyqht/Billy/blob/main/src/types/supabase.ts"&gt;this&lt;/a&gt;. With the &lt;strong&gt;definitions&lt;/strong&gt; that are generated there, I could create a type for &lt;code&gt;Bill&lt;/code&gt; with just the code block 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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;definitions&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;../types/supabase&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;type&lt;/span&gt; &lt;span class="nx"&gt;Bill&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;definitions&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Bill&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;Most of my project source code still works fine after changing the type. The only code I had to refactor is related to the &lt;code&gt;deadline&lt;/code&gt; field which is Date type. The &lt;strong&gt;definitions&lt;/strong&gt; has the field declared as a string, even though they do acknowledge it is a Date format.&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="nx"&gt;Bill&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cm"&gt;/** Format: date */&lt;/span&gt;
    &lt;span class="nl"&gt;deadline&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Personally I think this is ok, since this forces you to convert date objects to appropriate ISO strings, so there is less ambiguity about passing the Date object that you have constructed in JS to Supabase. If you don’t like this you could override it in your self-declared type. &lt;/p&gt;

&lt;p&gt;In the same documentation, they also included a section on &lt;a href="https://supabase.com/docs/reference/javascript/generating-types#update-types-automatically-with-github-actions"&gt;how to update types automatically with GitHub Actions&lt;/a&gt;. This ensures that whenever you update the database, your GitHub action will automatically update the types that you have in your DB. Then, the next time you pull your source code, you would know what are the new changes and how to accommodate them. &lt;/p&gt;

&lt;p&gt;In the docs, there’s a script to be added to the &lt;code&gt;package.json&lt;/code&gt; as such.&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="nl"&gt;"update-types"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npx openapi-typescript https://your-project.supabase.co/rest/v1/?apikey=your-anon-key --output types/database/index.ts"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, I wasn’t too sure how to replace the values in the package.json script safely with &lt;strong&gt;environmental variables&lt;/strong&gt; when I push the source code. I’m not very familiar with bash syntax, but I assume the script should look something like this for my project.&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="nl"&gt;"update-types"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npx openapi-typescript https://${SUPABASE_URL}/rest/v1/?apikey=${ANON_KEY} --version=2 --output types/database/index.ts"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To double confirm the syntax, I decided to use the Sourcegraph Visual Studio Code plugin to look for projects that have implemented this GitHub action.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cross-reference code with Sourcegraph Visual Studio Code plugin
&lt;/h2&gt;

&lt;p&gt;The Sourcegraph Visual Studio Code plugin can be installed through the Extensions panel. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kzhnqdLy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1649239394629/QM9DB0arX.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kzhnqdLy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1649239394629/QM9DB0arX.png" alt="" width="800" height="610"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you have it installed, you can find the Sourcegraph icon at the side bar. When you click it, you will see the usual UI to do a universal code search. To find other projects that also generate types, we can pass in a query &lt;code&gt;npx openapi-typescript&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YWmlJ8Zt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1649239412401/MM0qAP2pv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YWmlJ8Zt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1649239412401/MM0qAP2pv.png" alt="" width="800" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We see quite a few results, and luckily, in the 3rd project called &lt;a href="https://github.com/tone-row/flowchart-fun"&gt;tone-row/flowchart-fun&lt;/a&gt;, I see that they have &lt;code&gt;package.json&lt;/code&gt; script that looks like a promising candidate. They have variables such as  &lt;code&gt;SB_URL&lt;/code&gt; and &lt;code&gt;SB_ANON_KEY&lt;/code&gt; which resemble Supabase stuff ✨&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="nl"&gt;"generate:types"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"export $(cat .env.local | xargs) &amp;amp;&amp;amp; npx openapi-typescript &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;${SB_URL}/rest/v1/?apikey=${SB_ANON_KEY}&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; --output types/database/index.ts"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this cross-reference and knowledge, we know that we are missing out on wrapping the whole API endpoint in a string with escape characters. Then, we can modify our previous script to match this script.&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="nl"&gt;"update-types"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npx openapi-typescript &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;${SUPABASE_URL}/rest/v1/?apikey=${SUPABASE_ANON_KEY}&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; --version=2 --output types/database/index.ts"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we run this script locally via &lt;code&gt;npm run update-types&lt;/code&gt; , we can see it generates the same output that we have done previously ✨&lt;/p&gt;

&lt;p&gt;Out of curiosity, I also want to know if their GitHub workflow follows the same structure that was given in the example given in Supabase docs. The cool thing is with the Sourcegraph plugin, I can &lt;strong&gt;explore the repository in the editor&lt;/strong&gt; itself.&lt;/p&gt;

&lt;p&gt;Sadly, this project is not using the &lt;code&gt;generate:types&lt;/code&gt; script that they have written in a GitHub workflow. They’re probably only running the npm script locally. Thankfully with the help of the Supabase documentation folks, at least we don’t have to write the action from scratch. &lt;/p&gt;

&lt;h2&gt;
  
  
  How to create the GitHub workflow
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;This section has been merged to Supabase documentation &lt;a href="https://supabase.com/docs/guides/api/generating-types"&gt;here&lt;/a&gt; after publication of this article.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To add GitHub action workflows to your project, you need a new folder &lt;code&gt;.github/workflows&lt;/code&gt; to store the workflows. For the &lt;code&gt;workflow.yml&lt;/code&gt; that consist the steps for the GitHub action workflow, we will be using the &lt;a href="https://supabase.com/docs/reference/javascript/generating-types#update-types-automatically-with-github-actions"&gt;sample GitHub action workflow&lt;/a&gt; on the Supabase documentation as a base. I have &lt;strong&gt;modified&lt;/strong&gt; it a little to insert some GitHub secrets as environment variables, you can refer to the comments below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Update database types&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;schedule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# sets the action to run daily. You can modify this to run the action more or less frequently&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;cron&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;0&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;0&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*'&lt;/span&gt;
    &lt;span class="c1"&gt;# for the workflow to be dispatched manually via GitHub actions dashboards.&lt;/span&gt;
  &lt;span class="na"&gt;workflow_dispatch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;update&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;SUPABASE_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{secrets.SUPABASE_URL}}&lt;/span&gt;
        &lt;span class="na"&gt;SUPABASE_ANON_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{secrets.SUPABASE_URL}}&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;persist-credentials&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
          &lt;span class="na"&gt;fetch-depth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v2.1.5&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;14&lt;/span&gt;
      &lt;span class="c1"&gt;# -- add echo -- #&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;npm run update-types &lt;/span&gt;
          &lt;span class="s"&gt;echo "$(cat types/database/index.ts)"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;check for file changes&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;git_status&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;echo "::set-output name=status::$(git status -s)"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Commit files&lt;/span&gt;
        &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{contains(steps.git_status.outputs.status, ' ')}}&lt;/span&gt;
        &lt;span class="c1"&gt;# -- change git stage filename -- #&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;git add types/database/index.ts&lt;/span&gt;
          &lt;span class="s"&gt;git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"&lt;/span&gt;
          &lt;span class="s"&gt;git config --local user.name "billy-github-actions[bot]"&lt;/span&gt;
          &lt;span class="s"&gt;git commit -m "Update database types" -a&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Push changes&lt;/span&gt;
        &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{contains(steps.git_status.outputs.status, ' ')}}&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ad-m/github-push-action@master&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;github_token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
          &lt;span class="na"&gt;branch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.ref }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The changes include: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Addition of a trigger &lt;code&gt;workflow_dispatch&lt;/code&gt; so that the workflow can be dispatched manually via GitHub actions dashboards &lt;em&gt;if&lt;/em&gt; necessary.&lt;/li&gt;
&lt;li&gt;Addition of an &lt;code&gt;echo&lt;/code&gt; statement to &lt;strong&gt;see the content of the result file&lt;/strong&gt; from running &lt;code&gt;update-types&lt;/code&gt; script&lt;/li&gt;
&lt;li&gt;Pass GitHub action secrets into environment variables.&lt;/li&gt;
&lt;li&gt;Change the filename to be staged &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you are not familiar with GitHub action secrets, think of it as separate environment variables solely for GitHub actions. This is a screenshot of where you can add them at the settings of your GitHub project.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KzTJc_Pp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1649239786702/uJmbgzaCP.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KzTJc_Pp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1649239786702/uJmbgzaCP.png" alt="Settings &amp;gt; Secrets &amp;gt; GitHub secrets" width="800" height="490"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To deploy the workflow, simply push the &lt;code&gt;.github/workflows&lt;/code&gt; folder. &lt;/p&gt;

&lt;p&gt;Then you will see the new workflow available in the Actions tab of your GitHub project. When you click on the additional options button, you can manually dispatch the workflow.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UJDWBjY3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1649240835522/EO6FMCWKR.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UJDWBjY3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1649240835522/EO6FMCWKR.png" alt="" width="800" height="292"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also view the logs of each workflow run if you encounter any issues. For example, here in the succeeded job, I can see the &lt;code&gt;src/types/supabase.ts&lt;/code&gt; file content that is retrieved since we logged it out as part of the steps of the workflow.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JTg4aCmx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1649240993687/0XKnycv5B.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JTg4aCmx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1649240993687/0XKnycv5B.png" alt="You will see similar content as the definitions file you obtained by manually running the npm script" width="800" height="561"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the next section, I’ll introduce you to a handy tool that I learnt while building &lt;a href="https://github.com/lyqht/article-badge-counter-workflow"&gt;a custom composite GitHub action&lt;/a&gt; to &lt;strong&gt;test GitHub actions locally&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to test GitHub Actions locally with act
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/nektos/act"&gt;act&lt;/a&gt;&lt;/strong&gt; is a tool that lets you test GitHub actions locally. The benefit of this is that you get faster feedback without clogging up the GitHub Action Runner history on your project. &lt;/p&gt;

&lt;p&gt;Refer to the &lt;a href="https://github.com/nektos/act"&gt;GitHub repository README.md&lt;/a&gt; to see how to install &lt;strong&gt;act&lt;/strong&gt;. You would need Docker installed too. The first time you start up act, you will be asked to choose the image that you want to create. For this GitHub Action, to &lt;strong&gt;test the functionality of updating the database types&lt;/strong&gt;, the &lt;strong&gt;Micro&lt;/strong&gt; image should be sufficient. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bA-kdp-G--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1649239856663/HLUyBPUdG.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bA-kdp-G--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1649239856663/HLUyBPUdG.png" alt="3 choices given to you for the image, can refer to docs." width="800" height="83"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After this config is completed, you can navigate to &lt;code&gt;~/.actrc&lt;/code&gt; to see the config for your docker image.&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="nt"&gt;-P&lt;/span&gt; ubuntu-latest&lt;span class="o"&gt;=&lt;/span&gt;node:16-buster-slim
&lt;span class="nt"&gt;-P&lt;/span&gt; ubuntu-20.04&lt;span class="o"&gt;=&lt;/span&gt;node:16-buster-slim
&lt;span class="nt"&gt;-P&lt;/span&gt; ubuntu-18.04&lt;span class="o"&gt;=&lt;/span&gt;node:16-buster-slim
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note that the Micro image does not have the full capability of a GitHub Action runner, so you will see errors related to git commands. I don’t want to explode my computer’s memory with the &lt;strong&gt;Large&lt;/strong&gt; image &lt;strong&gt;(~20GB)&lt;/strong&gt;, but you can try if you like.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To run this workflow locally with &lt;em&gt;act&lt;/em&gt;, we can run this bash command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;act &lt;span class="nt"&gt;-j&lt;/span&gt; update &lt;span class="nt"&gt;--secret-file&lt;/span&gt; .env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
we can pass in the following parameters:&lt;/p&gt;

&lt;p&gt;Explanation of parameters&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-j ${jobName}&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;we only have 1 job declared in this workflow, so we can pass the &lt;strong&gt;update&lt;/strong&gt; job&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;--secret-file ${filePath}&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;for the project, I am already using &lt;code&gt;.env&lt;/code&gt; for all the environment variables, so I can pass this same file for secrets.
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;SUPABASE_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your_url
&lt;span class="nv"&gt;SUPABASE_ANON_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;hehe
&lt;/code&gt;&lt;/pre&gt;


&lt;ul&gt;
&lt;li&gt;If you rather not have .env files locally for some reason, you can also pass variables manually with the parameter &lt;code&gt;-s SUPBASE_URL=your_url -s SUPABASE_ANON_KEY=hehe&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After running the bash command, here’s a simplified extract of the terminal output&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="o"&gt;[&lt;/span&gt;Update database types/update]   ✅  Success - actions/setup-node@v2.1.5
&lt;span class="o"&gt;[&lt;/span&gt;Update database types/update] ⭐  Run npm run update-types
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;src/types/supabase.ts&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;Update database types/update]   🐳  docker &lt;span class="nb"&gt;exec &lt;/span&gt;&lt;span class="nv"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;=[&lt;/span&gt;bash &lt;span class="nt"&gt;--noprofile&lt;/span&gt; &lt;span class="nt"&gt;--norc&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; pipefail /var/run/act/workflow/2] &lt;span class="nv"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;workdir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
| 
| &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; billy@0.0.1 update-types /Users/lyqht/self-study/Billy
| &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; npx openapi-typescript &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SUPABASE_URL&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/rest/v1/?apikey=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SUPABASE_ANON_KEY&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2 &lt;span class="nt"&gt;--output&lt;/span&gt; src/types/supabase.ts
| 
| npx: installed 10 &lt;span class="k"&gt;in &lt;/span&gt;3.328s
| ✨ openapi-typescript 5.2.0
🚀 &lt;span class="k"&gt;***&lt;/span&gt;/rest/v1/?apikey&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;***&lt;/span&gt; -&amp;gt; /Users/lyqht/self-study/Billy/src/types/supabase.ts &lt;span class="o"&gt;[&lt;/span&gt;28ms]
| /&lt;span class="k"&gt;**&lt;/span&gt;
|  &lt;span class="k"&gt;*&lt;/span&gt; This file was auto-generated by openapi-typescript.
|  &lt;span class="k"&gt;*&lt;/span&gt; Do not make direct changes to the file.
|  &lt;span class="k"&gt;*&lt;/span&gt;/
| 
| &lt;span class="nb"&gt;export &lt;/span&gt;interface paths &lt;span class="o"&gt;{&lt;/span&gt;
|   &lt;span class="s2"&gt;"/"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
|     get: &lt;span class="o"&gt;{&lt;/span&gt;
|       responses: &lt;span class="o"&gt;{&lt;/span&gt;
|         /&lt;span class="k"&gt;**&lt;/span&gt; OK &lt;span class="k"&gt;*&lt;/span&gt;/
|         200: unknown&lt;span class="p"&gt;;&lt;/span&gt;
|       &lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
|     &lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
|   &lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
|   &lt;span class="s2"&gt;"/Bill"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
|     get: &lt;span class="o"&gt;{&lt;/span&gt;
|     .... &lt;span class="o"&gt;}&lt;/span&gt; // i won&lt;span class="s1"&gt;'t show you all of it, but you will be able to see all these
[Update database types/update]   ✅  Success - npm run update-types
echo "$(cat src/types/supabase.ts)"
[Update database types/update] ⭐  Run check for file changes
[Update database types/update]   🐳  docker exec cmd=[bash --noprofile --norc -e -o pipefail /var/run/act/workflow/git_status] user= workdir=
| /var/run/act/workflow/git_status: line 2: git: command not found
[Update database types/update]   ⚙  ::set-output:: status=
[Update database types/update]   ✅  Success - check for file changes
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you run this, you can can see the output contains the paths portion for '/Bill' as a result of running the &lt;code&gt;update-types&lt;/code&gt; script. This meant that it worked correctly 🎉 &lt;/p&gt;

&lt;p&gt;⚠️ Remember that these changes happen on the docker instance itself, &lt;strong&gt;you will not see any changes on your local folder&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus: In Action
&lt;/h2&gt;

&lt;p&gt;On 7 Apr 2022, I added 2 new tables, and my bot was a good boi.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iEzN_c3R--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1649298808363/45lUlgtw1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iEzN_c3R--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1649298808363/45lUlgtw1.png" alt="New definitions added to the src/types/supabase.ts file" width="800" height="761"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since publishing the original article, I also created a GitHub action that does what is listed above. Check it out &lt;a href="https://github.com/lyqht/generate-supabase-db-types-github-action"&gt;here&lt;/a&gt;!&lt;/p&gt;

&lt;h2&gt;
  
  
  That's a wrap folks! 🎉
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6fbv8uJt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://c.tenor.com/eoM1uCVuXtkAAAAM/yay-excited.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6fbv8uJt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://c.tenor.com/eoM1uCVuXtkAAAAM/yay-excited.gif" alt="https://c.tenor.com/eoM1uCVuXtkAAAAM/yay-excited.gif" width="220" height="124"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thank you for reading, hope you enjoyed the article!&lt;/p&gt;

&lt;p&gt;If you find the article awesome, hit the &lt;em&gt;reactions&lt;/em&gt; 🧡 and &lt;em&gt;share&lt;/em&gt; it 🐦~&lt;/p&gt;

&lt;p&gt;To stay updated whenever I post new stuff, follow me on &lt;a href="https://twitter.com/estee_tey"&gt;Twitter&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>opensource</category>
      <category>github</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>A Strange Moment.js — 3 Gotchas for Working &amp; Testing with Datetimes</title>
      <dc:creator>Estee Tey</dc:creator>
      <pubDate>Fri, 25 Mar 2022 16:57:43 +0000</pubDate>
      <link>https://dev.to/lyqht/a-strange-momentjs-3-gotchas-for-working-testing-with-datetimes-5gc0</link>
      <guid>https://dev.to/lyqht/a-strange-momentjs-3-gotchas-for-working-testing-with-datetimes-5gc0</guid>
      <description>&lt;h2&gt;
  
  
  Introduction to Moment.js
&lt;/h2&gt;

&lt;p&gt;Moment.js is the most &lt;em&gt;commonly&lt;/em&gt; used JavaScript library to deal with datetimes, however for many use cases today, it may not be the best library to use given the issues of mutability, file size bundles and that it was designed for the previous era of the JavaScript ecosystem. Even the library maintainers themselves &lt;em&gt;discourage you&lt;/em&gt; to use Moment.js in new projects going forward.&lt;/p&gt;

&lt;p&gt;Nonetheless, there are many developers who still have to work with this library because &lt;strong&gt;many existing production projects use it heavily&lt;/strong&gt;, and it take significant effort to do a code revamp to migrate to use another immutable datetime library. Hence, this article is meant to highlight some strange gotchas for anyone who still has to deal with Moment.js. And for those who don’t have to, this article also serves as an interesting read for those who are curious about problems that can come from Moment.js &amp;amp; working with DateTime. &lt;/p&gt;

&lt;p&gt;Some examples given here are inspired based on what I have done at work. There will also be &lt;em&gt;Jest&lt;/em&gt; tests in the article to illustrate that the functionalities work as intended. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you are not familiar with Jest or find it a bother to set it up, you can also simply console.log the relevant variables to check the values manually. Otherwise you can refer to the source code found here at &lt;a href="https://github.com/lyqht/strange-momentjs"&gt;this GitHub repository&lt;/a&gt; on how Jest can be setup for a simple Node.js project.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Gotcha 1: Mutability of DateTime objects
&lt;/h2&gt;

&lt;p&gt;In many DateTime manipulation test cases, we would want to create DateTime objects in a chronological order. Instead of constantly writing hard coded values like “2022-03-21”, “2022-03-22”, I could create relative moment objects for testing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;today&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;moment&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;oneWeekLater&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;today&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;week&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;twoWeeksLater&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;today&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;week&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;However, when we log the 3 values, it turns out that they all have the same date.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;today&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// "2022-03-17T15:16:52.538Z"&lt;/span&gt;
&lt;span class="nx"&gt;oneWeekLater&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// "2022-03-17T15:16:52.538Z"&lt;/span&gt;
&lt;span class="nx"&gt;twoWeeksLater&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// "2022-03-17T15:16:52.538Z"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is because it turns out that all 3 constants refer to the same moment object, with the same datetime value. When we perform &lt;code&gt;.add&lt;/code&gt;, moment &lt;strong&gt;mutates the original object&lt;/strong&gt; rather than mutating a copy of that object.&lt;/p&gt;

&lt;h3&gt;
  
  
  Solution: Clone the moment object
&lt;/h3&gt;

&lt;p&gt;Hence, the solution to this problem is rather straight forward — we simply have to clone the moment object before performing arithmetic operations.&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;today&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;moment&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;oneWeekLater&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;today&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clone&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;week&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;twoWeeksLater&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;today&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clone&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;week&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;today&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// "2022-03-17T15:16:52.538Z"&lt;/span&gt;
&lt;span class="nx"&gt;oneWeekLater&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// "2022-03-24T15:16:52.538Z"&lt;/span&gt;
&lt;span class="nx"&gt;twoWeeksLater&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// "2022-03-31T15:16:52.538Z"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That way, we will get 3 unique moment objects and their respective datetime values.&lt;/p&gt;




&lt;h2&gt;
  
  
  Gotcha 2: moment().isValid()
&lt;/h2&gt;

&lt;p&gt;For backend applications, it is pretty common to validate that the properties found in the request body are the specified type that we expect them to be. For validating that the requestBody has a property of the DateTime string format, this is a simple way of how we can create a function to check it.&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;isDateTimeValid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;requestBody&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;moment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;requestBody&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dateTime&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;isValid&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 function does work as intended for the &lt;em&gt;happy paths&lt;/em&gt;.&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="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;isDateTimeValid&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;YYYY-MM-DD&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;requestWithDate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;dateTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2020-11-12&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isDateTimeValid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;requestWithDate&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toBe&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="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;YYYY-MM-DDThh:mm:ss&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;requestWithDateTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;dateTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2022-03-17T10:00:00&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isDateTimeValid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;requestWithDateTime&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toBe&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="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ISO string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;requestWithISOString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;dateTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2022-03-17T15:16:52.538Z&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isDateTimeValid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;requestWithISOString&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toBe&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running &lt;em&gt;Jest&lt;/em&gt;, I get the following expected results.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;PASS  ./momentjs.test.js
  isDateTimeValid
    ✓ should &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="nb"&gt;true &lt;/span&gt;&lt;span class="k"&gt;for &lt;/span&gt;YYYY-MM-DD &lt;span class="o"&gt;(&lt;/span&gt;1 ms&lt;span class="o"&gt;)&lt;/span&gt;
    ✓ should &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="nb"&gt;true &lt;/span&gt;&lt;span class="k"&gt;for &lt;/span&gt;YYYY-MM-DDThh:mm:ss &lt;span class="o"&gt;(&lt;/span&gt;1 ms&lt;span class="o"&gt;)&lt;/span&gt;
    ✓ should &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="nb"&gt;true &lt;/span&gt;&lt;span class="k"&gt;for &lt;/span&gt;ISO string &lt;span class="o"&gt;(&lt;/span&gt;1 ms&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let’s write some &lt;em&gt;sad&lt;/em&gt; &lt;em&gt;paths&lt;/em&gt;. I will throw in null and some random values that don’t make sense as DateTime.&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="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;isDateTimeValid&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
              &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="mi"&gt;12312312&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hehe&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;should return false for %p&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dateTime&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;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;dateTime&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isDateTimeValid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running &lt;em&gt;Jest&lt;/em&gt;, I get the following results. It returned false for null and invalid string values, but not for numbers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;FAIL  ./momentjs.test.js
  isDateTimeValid
    ✓ should &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="nb"&gt;true &lt;/span&gt;&lt;span class="k"&gt;for &lt;/span&gt;YYYY-MM-DD &lt;span class="o"&gt;(&lt;/span&gt;2 ms&lt;span class="o"&gt;)&lt;/span&gt;
    ✓ should &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="nb"&gt;true &lt;/span&gt;&lt;span class="k"&gt;for &lt;/span&gt;YYYY-MM-DDThh:mm:ss &lt;span class="o"&gt;(&lt;/span&gt;1 ms&lt;span class="o"&gt;)&lt;/span&gt;
    ✓ should &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="nb"&gt;true &lt;/span&gt;&lt;span class="k"&gt;for &lt;/span&gt;ISO string
    ✓ should &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="nb"&gt;false &lt;/span&gt;&lt;span class="k"&gt;for &lt;/span&gt;null
    ✕ should &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="nb"&gt;false &lt;/span&gt;&lt;span class="k"&gt;for &lt;/span&gt;1 &lt;span class="o"&gt;(&lt;/span&gt;2 ms&lt;span class="o"&gt;)&lt;/span&gt;
    ✕ should &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="nb"&gt;false &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt;
    ✕ should &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="nb"&gt;false &lt;/span&gt;&lt;span class="k"&gt;for &lt;/span&gt;12312312
    ✓ should &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="nb"&gt;false &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="s2"&gt;"hehe"&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;7 ms&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This happens because if we try to create a moment with a generic number, we do get a datetime.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;moment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// 1970-01-01T07:30:00+07:30&lt;/span&gt;
&lt;span class="nx"&gt;moment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// 1970-01-01T07:29:59+07:30&lt;/span&gt;
&lt;span class="nx"&gt;moment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;12312312&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// 1970-01-01T10:55:12+07:30&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that all of these correspond to the &lt;em&gt;same date&lt;/em&gt;, but with &lt;em&gt;a different time&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;This happens because Moment interprets number values that we pass to the constructor as &lt;strong&gt;the number of seconds since the Unix Epoch&lt;/strong&gt;. Therefore, we will need to tweak our datetime validity method to make it work as intended for valid datetime strings.&lt;/p&gt;

&lt;h3&gt;
  
  
  Solution: Enable &lt;code&gt;strictMode&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;A common solution to address these edge cases is to enforce strict mode. How it works is that we have to pass in a format that we are parsing the datetime, and set strict mode boolean as true.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;moment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dateTime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;YYYY-MM-DD&lt;/span&gt;&lt;span class="dl"&gt;"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you just change the validity function to use the above, then all the test cases that should be failing will fail correctly, and only the happy case of YYYY-MM-DD will pass.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;FAIL  ./momentjs.test.js
  isDateTimeValid
    ✓ should &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="nb"&gt;true &lt;/span&gt;&lt;span class="k"&gt;for &lt;/span&gt;YYYY-MM-DD
    ✕ should &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="nb"&gt;true &lt;/span&gt;&lt;span class="k"&gt;for &lt;/span&gt;YYYY-MM-DDThh:mm:ss &lt;span class="o"&gt;(&lt;/span&gt;1 ms&lt;span class="o"&gt;)&lt;/span&gt;
    ✕ should &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="nb"&gt;true &lt;/span&gt;&lt;span class="k"&gt;for &lt;/span&gt;ISO string &lt;span class="o"&gt;(&lt;/span&gt;1 ms&lt;span class="o"&gt;)&lt;/span&gt;
    ✓ should &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="nb"&gt;false &lt;/span&gt;&lt;span class="k"&gt;for &lt;/span&gt;null &lt;span class="o"&gt;(&lt;/span&gt;1 ms&lt;span class="o"&gt;)&lt;/span&gt;
    ✓ should &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="nb"&gt;false &lt;/span&gt;&lt;span class="k"&gt;for &lt;/span&gt;1
    ✓ should &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="nb"&gt;false &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt;
    ✓ should &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="nb"&gt;false &lt;/span&gt;&lt;span class="k"&gt;for &lt;/span&gt;12312312 &lt;span class="o"&gt;(&lt;/span&gt;1 ms&lt;span class="o"&gt;)&lt;/span&gt;
    ✓ should &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="nb"&gt;false &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="s2"&gt;"hehe"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we want the other 2 happy cases to continue passing, the solution is to use the &lt;a href="https://en.wikipedia.org/wiki/ISO_8601"&gt;ISO 8601 format&lt;/a&gt;, which allows for &lt;strong&gt;partial&lt;/strong&gt; timestamps.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isDateTimeValid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;requestBody&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;moment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;requestBody&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dateTime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;moment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ISO_8601&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="nx"&gt;isValid&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;Now, all our tests pass correctly 🎉&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;PASS  ./momentjs.test.js
  isDateTimeValid
    ✓ should &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="nb"&gt;true &lt;/span&gt;&lt;span class="k"&gt;for &lt;/span&gt;YYYY-MM-DD
    ✓ should &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="nb"&gt;true &lt;/span&gt;&lt;span class="k"&gt;for &lt;/span&gt;YYYY-MM-DDThh:mm:ss
    ✓ should &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="nb"&gt;true &lt;/span&gt;&lt;span class="k"&gt;for &lt;/span&gt;ISO string
    ✓ should &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="nb"&gt;false &lt;/span&gt;&lt;span class="k"&gt;for &lt;/span&gt;null
    ✓ should &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="nb"&gt;false &lt;/span&gt;&lt;span class="k"&gt;for &lt;/span&gt;1
    ✓ should &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="nb"&gt;false &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt;
    ✓ should &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="nb"&gt;false &lt;/span&gt;&lt;span class="k"&gt;for &lt;/span&gt;12312312
    ✓ should &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="nb"&gt;false &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="s2"&gt;"hehe"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;However, if we think about the problem we are trying to solve further — to validate the request body containing the datetime format — we should be standardizing the datetime format that we want to accept from our request body rather than allowing a loose list of datetime formats. This depends on the type of consumer that’s calling your API, but generally, &lt;strong&gt;the ISO string format in UTC is the safest to handle&lt;/strong&gt;, since most databases &amp;amp; servers run on UTC by default.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Gotcha 3: Parsing DateTime strings
&lt;/h2&gt;

&lt;p&gt;Many times, developers tend to use open source libraries without fully reading the documentation, and they spend hours fixing problems caused by lack of understanding of the fundamental concepts that the library is built upon. The DateTime string format parsed by Moment.js is one of such fundamental concepts which I’m guilty of just skimming over.&lt;/p&gt;

&lt;p&gt;There was a time where I had to retrieve the weekday of a specific date. To do this in Moment.js, all we have to do is call the moment object’s &lt;code&gt;.isoWeekday()&lt;/code&gt; function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;moment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2022-03-14&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;isoWeekday&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// 1 -&amp;gt; Monday&lt;/span&gt;
&lt;span class="nx"&gt;moment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2022-03-17&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;isoWeekday&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// 4 -&amp;gt; Thursday&lt;/span&gt;
&lt;span class="nx"&gt;moment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2022-03-20&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;isoWeekday&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// 7 -&amp;gt; Sunday&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, it is rather common for many databases to store some properties as DateTime even though the Date type could be sufficient. When we retrieve these date properties from the DB they would usually return in the format of &lt;code&gt;“YYYY-MM-DDThh:m:ss.SSSZ”&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When we try to find the weekday of DateTime strings, you will notice a &lt;em&gt;strange&lt;/em&gt; behavior.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// normal date&lt;/span&gt;
&lt;span class="nx"&gt;moment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2022-03-17&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;isoWeekday&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// 4 -&amp;gt; Thursday&lt;/span&gt;

&lt;span class="c1"&gt;// datetime retrieved from DB&lt;/span&gt;
&lt;span class="nx"&gt;moment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2022-03-17T22:16:52.538Z&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;isoWeekday&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// 5 -&amp;gt; Friday&lt;/span&gt;

&lt;span class="c1"&gt;// failed attempts to get the right date&lt;/span&gt;
&lt;span class="nx"&gt;moment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2022-03-17T22:16:52.538Z&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;startOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;day&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;YYYY-MM-DD&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 2022-03-18&lt;/span&gt;
&lt;span class="nx"&gt;moment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2022-03-17T22:16:52.538Z&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;startOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;day&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;isoWeekday&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// 5 -&amp;gt; Friday&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the examples above, the date portion of the strings are all the same, but the weekday returned is different from expected. For &lt;em&gt;me&lt;/em&gt;, the moment parses the DateTime as a day later than expected. For you, you may end up getting a different DateTime that what I’ve written there 😆 If it is indeed different, it is because of &lt;strong&gt;timezone differences&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://www.worldtimebuddy.com/"&gt;World time buddy&lt;/a&gt; is a nice website to go to if you want to compare DateTime between different countries and regions.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you are not familiar with timezone offsets, meeting this problem can be very frustrating. Here’s an illustration of the string format is parsed by Moment.js.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--K1zEwJnt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1648175059357/J4SXELb6H.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--K1zEwJnt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1648175059357/J4SXELb6H.png" alt="Structure of datetime.png" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Having a ‘Z’ in the DateTime string meant that this DateTime string is given &lt;strong&gt;in UTC.&lt;/strong&gt; This meant that if I am residing in Singapore, a &lt;strong&gt;GMT+8&lt;/strong&gt; timezone, when I use moment to parse this UTC DateTime string, I will get a local DateTime which is &lt;strong&gt;8 hours ahead&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;moment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2022-03-17T22:16:52.538Z&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// 2022-03-18T06:16:52+08:00&lt;/span&gt;
&lt;span class="nx"&gt;moment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2022-03-17T22:16:52.538&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// 2022-03-17T22:16:52+08:00&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If somehow you are saving &lt;code&gt;"2022-03-17T22:16:52.538Z"&lt;/code&gt; in your DB and this refers to DateTime in your local timezone rather than a DateTime in UTC (yes this happens 😂), there are 2 ways you can parse this correctly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Solution 1
&lt;/h3&gt;

&lt;p&gt;The first method is to simply chop off the ‘Z’ part, since it is what causes the timezone offset.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dateTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2022-03-17T22:16:52.538Z&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="nx"&gt;moment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;splice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// 2022-03-17T22:16:52+08:00&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Solution 2
&lt;/h3&gt;

&lt;p&gt;The second method is more readable, which is to tell Moment.js explicitly that this DateTime string is actually not in UTC.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;moment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2022-03-17T22:16:52.538Z&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;utc&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;format&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// 2022-03-17T22:16:52+08:00&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we have a better understanding of how Moment.js parses DateTime strings and why this gotcha happens, remember how I was trying to find out the weekday of a given date?&lt;/p&gt;

&lt;p&gt;Let me introduce you to a fun context for that! The problem will be presented in just dates rather than DateTime for simplicity, but you can certainly replace the dates below with DateTime strings and what you have learnt so far ✨&lt;/p&gt;

&lt;h2&gt;
  
  
  Use case: Find the most recent instance of a given weekday
&lt;/h2&gt;

&lt;p&gt;Imagine that you are a backend developer who has to implement a special weekly check-in reward event for a game. Some requirements are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Admins are able to configure a specific day of the week for players to check-in to this event e.g. Wednesday&lt;/li&gt;
&lt;li&gt;The game has to call the API that you have created to get the most recent instance of Wednesday that the players can check in to the event, to determine whether to show them the most recent checkpoint that they have missed or show them that they can check in today.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now let’s visualize the scenarios we have to address with some actual dates. Let’s start off with just dates.&lt;/p&gt;

&lt;h3&gt;
  
  
  Case 1: Get the given weekday from last week
&lt;/h3&gt;

&lt;p&gt;For example, if today is &lt;strong&gt;Tuesday 15 March 2022,&lt;/strong&gt; the most recent instance of Wednesday from this date will be Wednesday &lt;strong&gt;9 March 2022&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;This scenario can be translated into a test as such.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getRecentInstanceOfWeekday&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should get last wednesday if today is tuesday&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;getRecentInstanceOfWeekday&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2022-03-15&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2022-03-09&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;Unfortunately, &lt;strong&gt;there isn’t such a utility function available in Moment.js&lt;/strong&gt;. There are many use cases like this where we would have to write our own. However, that being said, Moment.js does provide arithmetic operations that could aid us in writing the solution. &lt;/p&gt;

&lt;p&gt;To address the first test case, we can get the Wednesday of the previous week.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getRecentInstanceOfWeekday&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentDate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dayOfTheWeek&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;moment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentDate&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;subtract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;weeks&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;day&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dayOfTheWeek&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;YYYY-MM-DD&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running Jest, the test case will pass as intended.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;getRecentInstanceOfWeekday&lt;/span&gt;
    &lt;span class="err"&gt;✓&lt;/span&gt; &lt;span class="nx"&gt;should&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nx"&gt;last&lt;/span&gt; &lt;span class="nx"&gt;wednesday&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;today&lt;/span&gt; &lt;span class="nx"&gt;is&lt;/span&gt; &lt;span class="nx"&gt;tuesday&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="nx"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let’s move to another scenario, where we should be getting the given weekday from current week instead.&lt;/p&gt;

&lt;h3&gt;
  
  
  Case 2: Get the given weekday from current week
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;If today is &lt;strong&gt;Thursday 17 March 2022,&lt;/strong&gt; we should be getting Wednesday 16 March 2022.&lt;/li&gt;
&lt;li&gt;Likewise if today is &lt;strong&gt;Wednesday 16 March 2022&lt;/strong&gt; itself, we should be getting Wednesday 16 March 2022.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This translates to the 2 new test cases below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getRecentInstanceOfWeekday&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should get last wednesday if today is tuesday&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;getRecentInstanceOfWeekday&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2022-03-15&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2022-03-09&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should return this week's wednesday if today is thursday&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;getRecentInstanceOfWeekday&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2022-03-17&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2022-03-16&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should return today if today is wednesday&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;getRecentInstanceOfWeekday&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2022-03-16&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2022-03-16&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;However, with our previous implementation, when we run this test with &lt;em&gt;Jest&lt;/em&gt;, we will see the following test failure results.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;● getRecentInstanceOfWeekday › should &lt;span class="k"&gt;return &lt;/span&gt;this week&lt;span class="s1"&gt;'s wednesday if today is thursday

    expect(received).toBe(expected) // Object.is equality

    Expected: "2022-03-16"
    Received: "2022-03-09"

      42 |
      43 |     it("should return this week'&lt;/span&gt;s wednesday &lt;span class="k"&gt;if &lt;/span&gt;today is thursday&lt;span class="s2"&gt;", () =&amp;gt; {
    &amp;gt; 44 |         expect(getRecentInstanceOfWeekday("&lt;/span&gt;2022-03-17&lt;span class="s2"&gt;", 3)).toBe("&lt;/span&gt;2022-03-16&lt;span class="s2"&gt;");
         |                                                             ^
      45 |     })
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;● getRecentInstanceOfWeekday › should &lt;span class="k"&gt;return &lt;/span&gt;today &lt;span class="k"&gt;if &lt;/span&gt;today is wednesday

    expect&lt;span class="o"&gt;(&lt;/span&gt;received&lt;span class="o"&gt;)&lt;/span&gt;.toBe&lt;span class="o"&gt;(&lt;/span&gt;expected&lt;span class="o"&gt;)&lt;/span&gt; // Object.is equality

    Expected: &lt;span class="s2"&gt;"2022-03-16"&lt;/span&gt;
    Received: &lt;span class="s2"&gt;"2022-03-09"&lt;/span&gt;

      46 |
      47 |     it&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"should return today if today is wednesday"&lt;/span&gt;, &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 48 |         expect&lt;span class="o"&gt;(&lt;/span&gt;getRecentInstanceOfWeekday&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"2022-03-16"&lt;/span&gt;, 3&lt;span class="o"&gt;))&lt;/span&gt;.toBe&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"2022-03-16"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
         |                                                             ^
      49 |     &lt;span class="o"&gt;})&lt;/span&gt;
      50 | &lt;span class="o"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Solution
&lt;/h3&gt;

&lt;p&gt;With the test cases we have written, we notice a pattern:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;if today is on a weekday &amp;lt; Wednesday, we return the Wednesday of the previous week&lt;/li&gt;
&lt;li&gt;if today is on a weekday ≥ Wednesday, we return the Wednesday of the current week&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Knowing this pattern and how &lt;code&gt;isoWeekday()&lt;/code&gt; works, we can write a solution.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getRecentInstanceOfWeekday&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentDate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dayOfTheWeek&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;moment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentDate&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;isoWeekday&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;dayOfTheWeek&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;moment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentDate&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;subtract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;weeks&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;isoWeekday&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dayOfTheWeek&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;YYYY-MM-DD&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;moment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentDate&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;isoWeekday&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dayOfTheWeek&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;YYYY-MM-DD&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, the previous tests will pass.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;PASS  ./momentjs.test.js
  getRecentInstanceOfWeekday
    ✓ should get last wednesday &lt;span class="k"&gt;if &lt;/span&gt;today is tuesday &lt;span class="o"&gt;(&lt;/span&gt;1 ms&lt;span class="o"&gt;)&lt;/span&gt;
    ✓ should &lt;span class="k"&gt;return &lt;/span&gt;this week&lt;span class="s1"&gt;'s wednesday if today is thursday
    ✓ should return today if today is wednesday (1 ms)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can also write more test cases and generalize them as such in &lt;em&gt;Jest&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getRecentInstanceOfWeekday&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;each&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;given day is Wed, when today is Tue&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;2022-03-15&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2022-03-09&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;given day is Wed, when today is Mon&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;2022-03-14&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2022-03-09&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should return given weekday from last week - %s&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;testCase&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;today&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;givenWeekday&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expectedDate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;getRecentInstanceOfWeekday&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;today&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;givenWeekday&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expectedDate&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;each&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;given day is Wed, when today is Wed (same day)&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;2022-03-16&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2022-03-16&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;given day is Wed, when today is Thu&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;2022-03-17&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2022-03-16&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;given day is Wed, when today is Sun&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;2022-03-20&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2022-03-16&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should return given weekday from current week - %s&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;testCase&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;today&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;givenWeekday&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expectedDate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;getRecentInstanceOfWeekday&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;today&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;givenWeekday&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expectedDate&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And these too will continue to pass 🎉&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;PASS  ./momentjs.test.js
  getRecentInstanceOfWeekday
    ✓ should &lt;span class="k"&gt;return &lt;/span&gt;given weekday from last week - given day is Wed, when today is Tue
    ✓ should &lt;span class="k"&gt;return &lt;/span&gt;given weekday from last week - given day is Wed, when today is Mon
    ✓ should &lt;span class="k"&gt;return &lt;/span&gt;given weekday from current week - given day is Wed, when today is Wed &lt;span class="o"&gt;(&lt;/span&gt;same day&lt;span class="o"&gt;)&lt;/span&gt;
    ✓ should &lt;span class="k"&gt;return &lt;/span&gt;given weekday from current week - given day is Wed, when today is Thu
    ✓ should &lt;span class="k"&gt;return &lt;/span&gt;given weekday from current week - given day is Wed, when today is Sat
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Working with Date and DateTime objects is a &lt;em&gt;pain in the ass&lt;/em&gt; even with libraries that help you to deal with them — but it gets better! Once you know that certain gotchas exist and you have more experience with interesting use cases, you will know the edge cases to look out for. Translating requirements into tests will also help you build more confidence in the functionalities that you are delivering ✨ &lt;/p&gt;

&lt;p&gt;Below are some additional resources if you are keen to explore further.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;For a deeper dive into datetime manipulation before you even consider using a datetime library, Punit Jajodia wrote &lt;a href="https://www.toptal.com/software/definitive-guide-to-datetime-manipulation"&gt;The Definitive Guide to DateTime Manipulation&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Why Moment.js library maintainers recommend you not to use Moment.js for newer projects on their &lt;a href="https://momentjs.com/docs/#/-project-status/"&gt;landing page&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;For those of you who are starting out on new projects and have a choice to use a different datetime library, give Day.js a try! It mimics moment.js API, but better in many ways. This article by Sabesan Sathananthan covers on &lt;a href="https://medium.datadriveninvestor.com/https-medium-com-sabesan96-why-you-should-choose-day-js-instead-of-moment-js-9cf7bb274bbd"&gt;why you should choose day.js instead of moment.js&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Special Considerations for months and years by &lt;a href="https://momentjscom.readthedocs.io/en/latest/moment/03-manipulating/01-add/#special-considerations-for-months-and-years"&gt;Moment.js documentation&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  That's a wrap folks! 🎉
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6fbv8uJt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://c.tenor.com/eoM1uCVuXtkAAAAM/yay-excited.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6fbv8uJt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://c.tenor.com/eoM1uCVuXtkAAAAM/yay-excited.gif" alt="https://c.tenor.com/eoM1uCVuXtkAAAAM/yay-excited.gif" width="220" height="124"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thank you for reading, hope you enjoyed the article! &lt;/p&gt;

&lt;p&gt;✨ Special thanks to &lt;a href="https://www.linkedin.com/in/shaun-chiang-b33a4113b/"&gt;Shaun Chiang&lt;/a&gt; &amp;amp; &lt;a href="https://www.linkedin.com/in/kah-yee-kwa"&gt;Kah Yee Kwa&lt;/a&gt; for pairing with me to work on interesting DateTime scenarios.&lt;/p&gt;

&lt;p&gt;If you find the article awesome, hit the &lt;em&gt;reactions&lt;/em&gt; 🧡 and &lt;em&gt;share&lt;/em&gt; it 🐦~&lt;/p&gt;

&lt;p&gt;To stay updated whenever I post new stuff, follow me on &lt;a href="https://twitter.com/estee_tey"&gt;Twitter&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>tutorial</category>
      <category>testing</category>
      <category>node</category>
    </item>
    <item>
      <title>Nevertheless, Estee Coded</title>
      <dc:creator>Estee Tey</dc:creator>
      <pubDate>Mon, 07 Mar 2022 19:07:20 +0000</pubDate>
      <link>https://dev.to/lyqht/nevertheless-estee-coded-2de6</link>
      <guid>https://dev.to/lyqht/nevertheless-estee-coded-2de6</guid>
      <description>&lt;p&gt;Hi, I'm Estee and you can call me ST. My pronouns are She/He, because I don't really feel strongly about my gender in either ways. If I'm playing games, my friends know that I could be very manly in my playing style and if I see cockroaches, you will see me shrieking like a girl. Yes, you can dispute that either gender can be reacting in the same way, and that's a point that I hope more people are aware about when they meet people. It was only by chance, I was born as a girl.&lt;/p&gt;

&lt;p&gt;I gave a brief glimpse of my coding background in &lt;a href="https://community.codenewbie.org/codenewbie/estee-tey-an-adaptable-bold-and-open-minded-coder-i40"&gt;an interview with the CodeNewbie Community&lt;/a&gt; previously, but here's a more complete digest of my background in tech &amp;amp; tech career.&lt;/p&gt;

&lt;h2&gt;
  
  
  Background in Tech
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;First experience with coding at age 21, nearing the end of 1st year of university due to freshmore curriculum.&lt;/li&gt;
&lt;li&gt;In university, I tried to cover ground in various parts of the tech industry because I didn't know what I really wanted to do in tech, even though I didn't mind the idea of coding for a living.

&lt;ul&gt;
&lt;li&gt;In the 2.5 years of school curriculum under the Infosystems major, I learnt introductory concepts on android app development in Java, algorithms, machine learning, data science, game development with Unity. &lt;/li&gt;
&lt;li&gt;Outside of school, I signed up for courses by myself to learn about Internet of Things (IOT), mixed reality app development for HoloLens 2, Blockchain Development.&lt;/li&gt;
&lt;li&gt;Out of all of these, I only enjoyed the app &amp;amp; game development courses 😆&lt;/li&gt;
&lt;li&gt;I also had 1 internship in IT project management, and 1 internship as a Software Engineer.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Eventually, I graduated in a bad time when COVID hit pretty hard, September 2020. 

&lt;ul&gt;
&lt;li&gt;Because of that, a full-time job offer I attained in March 2020 got pushed back to an indefinite date. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nevertheless&lt;/strong&gt;, life goes on, and bills had to be paid. So I just applied for another job during my finals and I became a Software Engineer trainee for another company in the mean time. &lt;/li&gt;
&lt;li&gt;The former offer did eventually got back for me to start working in Jan 2021.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;I've worked in a global tech consultancy company as a Software Developer since then, and I've gotten to work on 5 different projects since my time there.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Working in tech
&lt;/h2&gt;

&lt;p&gt;I've made a twitter thread previously on my WFH experience based on the 1st 4 projects that I worked on in my current job.&lt;/p&gt;

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

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



&lt;/p&gt;

&lt;p&gt;For my current project (5th project) that I'm in now, it is very technically challenging compared to all the previous projects for me. The tough part is not the tech stack, but rather understanding the complexity of the system design of microservices, and also to perform technical solutioning with a lot of ambiguity under high pressure from the client. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nevertheless&lt;/strong&gt;, I'm excited to learn more in-depth about all of these including the tech stack which comprises of React, React Native, Typescript, Express, Kubernetes, Azure Platform. &lt;/p&gt;

&lt;p&gt;I would also be looking forward to building more side projects to learn stuff that I don't get to do at work, and teaching developers like you on various concepts through &lt;a href="https://blog.esteetey.dev/"&gt;the technical articles that I write&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Some concluding thoughts
&lt;/h2&gt;

&lt;p&gt;I'm lucky enough to be born in Singapore, a place where women are less discriminated albeit the fact that there is still a very low gender ratio for the tech industry. Honestly, if it is a fact that girls are just not as interested in tech, then so be it. But if they are interested and they are denied opportunities just because of being a "She", then that's another story. &lt;/p&gt;

&lt;p&gt;I hope that people will look at individuals via traits more rather than just using the gender label as a convenient way to group people into boxes to be shoved into areas outside their comfort zone. &lt;/p&gt;

&lt;p&gt;People are like onions, with many layers deep. Some when you cut it, it will make you cry. But some when you fry it, it's pretty tasty.&lt;/p&gt;

</description>
      <category>wecoded</category>
      <category>career</category>
      <category>codenewbie</category>
    </item>
    <item>
      <title>How to create a CSS Playground for styling Pseudo-elements with Vanilla JavaScript</title>
      <dc:creator>Estee Tey</dc:creator>
      <pubDate>Mon, 07 Feb 2022 04:18:16 +0000</pubDate>
      <link>https://dev.to/lyqht/how-to-create-a-css-playground-for-styling-pseudo-elements-with-vanilla-javascript-485m</link>
      <guid>https://dev.to/lyqht/how-to-create-a-css-playground-for-styling-pseudo-elements-with-vanilla-javascript-485m</guid>
      <description>&lt;p&gt;In this article, I will show you how to implement 3 features that I thought to be valuable of a CSS playground (here's the why section if you want to learn why before how) using CSS and Vanilla JavaScript.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Live Preview&lt;/li&gt;
&lt;li&gt;Export CSS Functionality&lt;/li&gt;
&lt;li&gt;CSS Validation&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The steps for implementation will be based on the previous playground that I personally made.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 There will be checkpoints across the article where I'll embed a codepen for the progress thus far, so feel free to fork those to continue if you ever get lost. If you ever just want to see the completed product, you can  refer to the source code at the &lt;a href="https://github.com/lyqht/scrollbar-playground"&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The CSS Playground
&lt;/h2&gt;

&lt;p&gt;Scrollbar playground is a project that I made previously for users to &lt;a href="https://blog.esteetey.dev/make-your-website-stand-out-with-a-custom-scrollbar"&gt;learn to create a custom scrollbar&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The project structure is very small and mimics the usual structure of a CodePen (1 file each for HTML, CSS &amp;amp; JavaScript).&lt;/li&gt;
&lt;li&gt;The challenge of creating this particular playground is that the scrollbar can only be styled using &lt;a href="https://blog.esteetey.dev/primer-css-pseudo-elements-and-pseudo-classes"&gt;pseudo-elements&lt;/a&gt;, which are style selectors that you cannot set directly say using &lt;code&gt;element.style.position= 'absolute'&lt;/code&gt;. There's no such thing as &lt;code&gt;element.style.scrollbarProperty&lt;/code&gt;. In this article, you will learn later how to indirectly manipulate the value of the pseudo-element properties.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are the features that have been implemented in the playground:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Live preview&lt;/strong&gt; → Users have multiple &lt;strong&gt;configuration settings&lt;/strong&gt; such as sizes &amp;amp; colors, and are able to see changes on a live preview. Users can also &lt;em&gt;play&lt;/em&gt; with the live preview by resizing the div and scrolling up &amp;amp; down at the div.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Export Button&lt;/strong&gt; → Users can obtain the styles they want on their &lt;strong&gt;clipboard&lt;/strong&gt; and paste it easily over to their own project. They could even paste over to a new CodePen to test and see it work immediately with a very tall div. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WdmhxX0f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1643863921146/XtG4F0Q3l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WdmhxX0f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1643863921146/XtG4F0Q3l.png" alt="Labelling parts of Scrollbar Playground" width="800" height="484"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now let’s dive into an implementation of the most essential feature of playgrounds - live preview. &lt;/p&gt;

&lt;h2&gt;
  
  
  1. Live Preview
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties"&gt;CSS Custom Properties&lt;/a&gt; (also known as &lt;strong&gt;CSS Variables&lt;/strong&gt;) is a concept that I used for implementing the live preview. Here, we will go through 2 types of properties that are available for configuration which you can observe from the previous screenshot - sizes &amp;amp; colors. &lt;/p&gt;

&lt;p&gt;Here’s the HTML for the preview area.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;'preview'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;'inner-content'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        Text generated by Cat Ipsum. 
    &lt;span class="nt"&gt;&amp;lt;/p&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;Most live previews exist as a 2 column layout, so let's do that too.&lt;/p&gt;

&lt;p&gt;2 column layout On HTML.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Remember to add some lorem ipsum text into the inner content so that the scrollbars will show up. Scrollbars only show up if the content exceeds the height/width of a container.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&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;'preview'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;'inner-content'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            Text generated by Cat Ipsum. 
            &lt;span class="c"&gt;&amp;lt;!-- add more text here --&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&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;'form-container'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
       &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;'form'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
       &lt;span class="c"&gt;&amp;lt;!-- for user config settings later --&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;span class="nt"&gt;&amp;lt;/body&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2 column layout via 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;body&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;flex-direction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;row&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="nl"&gt;padding&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;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#000000&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="nf"&gt;#form-container&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;50%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;min-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30%&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="py"&gt;justify-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="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;#form&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="py"&gt;justify-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;flex-direction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;column&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we can style the preview with CSS. The only important CSS for a scrollbar live preview are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;resize: both&lt;/code&gt; -&amp;gt; this allows users to resize however they want to see the effect of the scrollbar extending/shrinking&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;overflow: auto&lt;/code&gt; -&amp;gt; this allows user to see the scrollbar on both horizontal &amp;amp; vertical sides of the div.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#preview {
    margin: auto;
    width: 300px;
    height: 400px;
    resize: both; 
    overflow: auto; 
    display: grid;
    place-items: center;
    border-radius: 6px;
    border: 1px solid white;
    color: black;
    background: rgba(255, 255, 255, 0.8);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you're working on your own playground on another topic, be creative on how you want to the structure the layout and style the preview 🎵&lt;/p&gt;

&lt;p&gt;Now that we got the basic layout of the live preview done, let's begin to create the playground!&lt;/p&gt;

&lt;h3&gt;
  
  
  Size Properties
&lt;/h3&gt;

&lt;p&gt;Let’s start with just 1 size configuration, say the scrollbar’s width.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create a &lt;strong&gt;HTML&lt;/strong&gt; text input for the user to input the scrollbar’s width.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"scrollbar-width"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Scrollbar Width (px)&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"scrollbar-width"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"scrollbar-width"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"number"&lt;/span&gt; &lt;span class="na"&gt;min=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt; &lt;span class="na"&gt;step=&lt;/span&gt;&lt;span class="s"&gt;"10"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"20"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add a &lt;strong&gt;CSS&lt;/strong&gt; pseudo-element value that is set by a &lt;strong&gt;CSS variable&lt;/strong&gt; with fallback default value in the main stylesheet.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nf"&gt;#preview&lt;/span&gt;&lt;span class="nd"&gt;::-webkit-scrollbar&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;--scrollbar-width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;20px&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;p&gt;Once you add this pseudo-element style, you can no longer see the scrollbar 👀 This is an interesting finding I came across while learning about the scrollbar as well. Somehow, by adding 1 scrollbar pseudo-element, you are nullifying the rest of the default scrollbar pseudo-elements as well. I don't know why this CSS is designed this way, but we just have to work around it. For now, to see the scrollbar,  we can add basic colors to the scrollbar thumb and track.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nf"&gt;#preview&lt;/span&gt;&lt;span class="nd"&gt;::-webkit-scrollbar-track&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#A1A1AA&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;#preview&lt;/span&gt;&lt;span class="nd"&gt;::-webkit-scrollbar-thumb&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#3B82F6&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;/li&gt;
&lt;li&gt;
&lt;p&gt;Add &lt;strong&gt;JavaScript&lt;/strong&gt; code to get the user’s input for the scrollbar width setting and set it as the live preview container’s scrollbar width. Notice here that we are not setting the pseudo-element directly; we are updating the pseudo-element indirectly by &lt;strong&gt;updating the CSS variable&lt;/strong&gt; that it is tied to.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;scrollbarDiv&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;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;preview&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;widthInput&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;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;scrollbar-width&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;widthInput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onchange&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="nx"&gt;scrollbarDiv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--scrollbar-width&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;widthInput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;px`&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;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And there we have 1 size property that can be be configured by the user and you can see the preview rendering the changes live! ✨ &lt;/p&gt;

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

&lt;p&gt;You can imagine the flow to be pretty similar for the scrollbar height &amp;amp; border radius as well. With more size properties that can be configured, the code can get a little messy. This is a way you can choose to refactor the JavaScript code so that it’s more readable and obvious how to add new inputs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Refactoring size property configuration&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Declare a size property array that consists of objects that maps the element &amp;amp; the CSS property that they are intended to update. e.g.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;el&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;heightInput&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--scrollbar-height&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;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a common update function that takes an input element &amp;amp; property to set the style appropriately for the live preview.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;setSizeFieldOnChange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onchange&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="nx"&gt;scrollbarDiv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;property&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;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;px`&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;/li&gt;
&lt;li&gt;
&lt;p&gt;Then for each size property, set the onChange function to the common update function.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;sizePropertyArray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;setSizeFieldOnChange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

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

&lt;p&gt;With the addition of 2 other size properties, this leads to the final size property configuration JavaScript code being&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;scrollbarDiv&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;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;preview&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;heightInput&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;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;scrollbar-height&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;widthInput&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;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;scrollbar-width&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;borderRadiusInput&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;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;scrollbar-border-radius&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;sizePropertyArray&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;el&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;heightInput&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;property&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--scrollbar-height&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="na"&gt;el&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;widthInput&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;property&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--scrollbar-width&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="na"&gt;el&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;borderRadiusInput&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;property&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--scrollbar-border-radius&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;setSizeFieldOnChange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onchange&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="nx"&gt;scrollbarDiv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;property&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;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;px`&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;sizePropertyArray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;setSizeFieldOnChange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Now let’s move on to color properties.&lt;/p&gt;

&lt;h3&gt;
  
  
  Color Properties
&lt;/h3&gt;

&lt;p&gt;For the color picker component, we will be using an external library because it isn’t really worth the effort to write one from scratch when it’s not the focus of the playground. For this article, I’m using Pickr, and it has a CDN link for the library &amp;amp; styles which we can import by including the following script in the head element. Since we are adding importing external scripts now, I'll also add a Google font for styling.&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;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.jsdelivr.net/npm/@simonwep/pickr/dist/pickr.min.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://fonts.googleapis.com/css2?family=PT+Mono&amp;amp;display=swap"&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&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;"index.css"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&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;"https://cdn.jsdelivr.net/npm/@simonwep/pickr/dist/themes/nano.min.css"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To use the font in your project, just set the font-family wherever you want.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;body {
  font-family: "PT Mono", monospace;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, let’s begin with just 1 color configuration, say the scrollbar’s thumb.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create a &lt;strong&gt;HTML&lt;/strong&gt; button for the user to bring up the color picker.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"scrollbar-thumb-color"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Thumb&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"thumb-color-picker"&lt;/span&gt; &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;"thumb-color-picker"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add a &lt;strong&gt;CSS&lt;/strong&gt; pseudo-element value that is set by a &lt;strong&gt;CSS variable&lt;/strong&gt; with fallback default value in the main stylesheet.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nf"&gt;#preview&lt;/span&gt;&lt;span class="nd"&gt;::-webkit-scrollbar-thumb&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;--scrollbar-thumb-color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;#3B82F6&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;/li&gt;
&lt;li&gt;
&lt;p&gt;Add JavaScript to create a color picker and bind it to an element. The Pickr creation code looks a little long, but the API is rather intuitive.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;thumbColorElement&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="s2"&gt;#thumb-color-picker&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;pickr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Pickr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;el&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;thumbColorElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#3B82F6&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nano&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;swatches&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rgba(244, 67, 54, 1)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rgba(233, 30, 99, 0.95)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rgba(156, 39, 176, 0.9)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rgba(103, 58, 183, 0.85)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rgba(63, 81, 181, 0.8)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rgba(33, 150, 243, 0.75)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rgba(3, 169, 244, 0.7)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rgba(0, 188, 212, 0.7)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rgba(0, 150, 136, 0.75)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rgba(76, 175, 80, 0.8)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rgba(139, 195, 74, 0.85)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rgba(205, 220, 57, 0.9)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rgba(255, 235, 59, 0.95)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rgba(255, 193, 7, 1)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="na"&gt;comparison&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;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;preview&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="na"&gt;opacity&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="na"&gt;hue&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="na"&gt;interaction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;hex&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="na"&gt;rgba&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="na"&gt;hsla&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="na"&gt;hsva&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="na"&gt;cmyk&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="na"&gt;input&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="na"&gt;clear&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="na"&gt;save&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;/code&gt;&lt;/pre&gt;


&lt;p&gt;If you are keen to configure more settings at the Pickr component, you can refer to the official Pickr docs.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Then, we also need to add a function to update the the live preview container’s scrollbar thumb color when the user selects a color from the color picker. This is again done by updating the &lt;strong&gt;CSS variable&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;pickr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&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="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;scrollbarDiv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--scrollbar-thumb-color&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toHEXA&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;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;With that, you will be able to see the thumb color being changed when the user selects a new color ✨ Likewise, for other colors, we can perform a similar procedure. However, for the color properties, this introduces a lot more &lt;em&gt;bulk code&lt;/em&gt; than the size properties due to how long the Pickr creation &amp;amp; binding code is. Hence, it becomes quite important for us to refactor the code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Refactoring color configuration&lt;/strong&gt; &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create a common method which does the common pickr creation &amp;amp; binding logic. The overlapping parameters are the input element, the CSS variable to be updated and the default color we want the color pickr to display.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bindColorPickr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;defaultColor&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;pickr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Pickr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;defaultColor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="c1"&gt;// the rest of the creation parameters is the same&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="nx"&gt;pickr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&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="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;scrollbarDiv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toHEXA&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;/li&gt;
&lt;li&gt;
&lt;p&gt;Declare an array of objects which resembles the size property array that we had earlier on for the color properties that can be configured.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;colorsPropertyArray&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;el&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#thumb-color-picker&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;property&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--scrollbar-thumb-color&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;defaultColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#3B82F6&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="na"&gt;el&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#track-color-picker&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;property&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--scrollbar-track-color&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;defaultColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#A1A1AA&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="na"&gt;el&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-color-picker&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;property&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--scrollbar-button-color&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;defaultColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#3F3F46&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="na"&gt;el&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#corner-color-picker&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;property&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--scrollbar-corner-color&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;defaultColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#FFFFFF&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="na"&gt;el&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#border-color-picker&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;property&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--scrollbar-border-color&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;defaultColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#FFFFFF&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Then for each color property, we will bind the common function.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;colorsPropertyArray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;defaultColor&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;bindColorPicker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;defaultColor&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

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

&lt;p&gt;This leads to the overall color configuration code being&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bindColorPicker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;defaultColor&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;pickr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Pickr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;defaultColor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nano&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;swatches&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rgba(244, 67, 54, 1)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rgba(233, 30, 99, 0.95)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rgba(156, 39, 176, 0.9)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rgba(103, 58, 183, 0.85)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rgba(63, 81, 181, 0.8)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rgba(33, 150, 243, 0.75)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rgba(3, 169, 244, 0.7)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rgba(0, 188, 212, 0.7)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rgba(0, 150, 136, 0.75)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rgba(76, 175, 80, 0.8)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rgba(139, 195, 74, 0.85)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rgba(205, 220, 57, 0.9)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rgba(255, 235, 59, 0.95)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rgba(255, 193, 7, 1)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="na"&gt;comparison&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;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;preview&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="na"&gt;opacity&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="na"&gt;hue&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="na"&gt;interaction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;hex&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="na"&gt;rgba&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="na"&gt;hsla&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="na"&gt;hsva&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="na"&gt;cmyk&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="na"&gt;input&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="na"&gt;clear&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="na"&gt;save&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;pickr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&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="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;scrollbarDiv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toHEXA&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;save&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;colorsPropertyArray&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;el&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#thumb-color-picker&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;property&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--scrollbar-thumb-color&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;defaultColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#3B82F6&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="na"&gt;el&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#track-color-picker&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;property&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--scrollbar-track-color&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;defaultColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#A1A1AA&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="na"&gt;el&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#corner-color-picker&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;property&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--scrollbar-corner-color&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;defaultColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#FFFFFF&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nx"&gt;colorsPropertyArray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;defaultColor&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;bindColorPicker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;defaultColor&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;We can also structure the layout of how the color inputs are presented to the user since having all of them in a column looks funny too.&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="nf"&gt;#color-inputs&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;padding&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="err"&gt;o&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;grid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;grid-template-columns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="n"&gt;fr&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="n"&gt;fr&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="n"&gt;fr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;#color-inputs&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;div&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;grid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;place-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="py"&gt;margin-block-end&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="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;And finally, the live preview our scrollbar playground is completed!&lt;/p&gt;

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




&lt;p&gt;🎉 Good job, you have learnt how to create a simple CSS playground with vanilla JS using CSS variables for size and color properties! The same concepts and thinking process can be applied to other types of properties that you would like your users to play with in the playground - be it pseudo-elements or other CSS selectors 😊&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The &lt;a href="https://lyqht.github.io/scrollbar-playground/"&gt;actual scrollbar playground&lt;/a&gt; has a few more config settings, they are excluded intentionally since the principles to implement them are the same as what you just learnt.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The next section will be on adding an export CSS button to your app - a good to have feature for playgrounds. &lt;/p&gt;




&lt;h2&gt;
  
  
  2. Export CSS Functionality
&lt;/h2&gt;

&lt;p&gt;This section will be covering the export css functionality. I'll leave it to you to design the export CSS button however you want 🌻&lt;/p&gt;

&lt;p&gt;For us to export CSS, we need a way to access the existing CSS rules in our app. For that, we can use &lt;code&gt;document.styleSheets&lt;/code&gt; to access the array of stylesheets that are used by a website. The order of the stylesheets depends on how you declare the sequence of imports in your app. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Try it out in your browser console on any website ✨&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For my project, these are 3 stylesheets used by the HTML document by order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;imported stylesheet for google fonts in index.css&lt;/li&gt;
&lt;li&gt;index.css&lt;/li&gt;
&lt;li&gt;imported stylesheet for pickr styling &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Hence, for us to access the css rules that are declared locally in &lt;code&gt;index.css&lt;/code&gt;, we can access the &lt;code&gt;cssRules&lt;/code&gt; property in the stylesheet object as such&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;styleSheets&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;cssRules&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you are trying to develop a playground website locally, on Google Chrome, you may notice the following error when you try to access the css rules.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BG3KvO-o--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1643864911886/r_uuUQYKv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BG3KvO-o--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1643864911886/r_uuUQYKv.png" alt="Google Chrome Console Error. index.js:166 Uncaught (in promise) DOMException: Failed to read the 'cssRules' property from 'CSSStyleSheet': Cannot access rules&amp;lt;br&amp;gt;
    at HTMLButtonElement.exportCSSButton.onclick (file:///Users/lyqht/archived/scrollbar-playground/index.js:166:60)&amp;lt;br&amp;gt;
exportCSSButton.onclick @ index.js:166" width="800" height="115"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This happens because accessing css rules violates a CORS policy implemented by Google Chrome, and is further discussed in this &lt;a href="https://stackoverflow.com/questions/48753691/cannot-access-cssrules-from-local-css-file-in-chrome-64"&gt;StackOverflow thread&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Basically, in order to read these rules from the website on your local machine, you have to set up a local testing server. I followed the &lt;a href="https://developer.mozilla.org/en-US/docs/Learn/Common_questions/set_up_a_local_testing_server"&gt;MDN docs on setting up a local testing server&lt;/a&gt;, which just requires to run the below command in bash.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; http.server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we will be able to access our local website at &lt;code&gt;http:localhost:8000&lt;/code&gt;, where we can access &lt;code&gt;stylesheet.cssRules&lt;/code&gt;. However, the &lt;code&gt;index.css&lt;/code&gt; has other playground layout styles aside from just the scrollbar styles. This meant that we have to perform the following actions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We will need to filter them out and get only the scrollbar styles to be exported for the user. Let this be &lt;code&gt;exportedStyle&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;We can also help the user to replace the selector &lt;code&gt;#preview&lt;/code&gt; to something more generic like &lt;code&gt;body&lt;/code&gt; so that they can test it easily on an empty CodePen. So our exported style will look something like
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;exportedStyle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;body { ... }&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This leads to the overall code being&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;scrollbarDiv&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;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;preview&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;customProperties&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;scrollbarDiv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cssText&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;defaultElementForStyling&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;body&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;exportedStyle&lt;/span&gt; &lt;span class="o"&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;defaultElementForStyling&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;customProperties&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; } `&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cssRules&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;styleSheets&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;cssRules&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;scrollbarRules&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cssRules&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rule&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cssText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;::-webkit-scrollbar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="nx"&gt;scrollbarRules&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rule&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;modifiedRule&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cssText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#preview&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;defaultElementForStyling&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nx"&gt;exportedStyle&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;modifiedRule&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After we get the exported styles as a string, we want to put them at the user's clipboard so they can paste them to their own project. To do so, we can use the Navigator clipboard Web API.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clipboard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;writeText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;exportedStyle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;The clipboard API does not work through an iframe usually, but here's a handy monkey-patch by Thai Pangsakulyanont if you really want it to work on a clipboard. You can refer to the codepen to see how this is added in the &lt;code&gt;.js&lt;/code&gt; file.&lt;/p&gt;


&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://cdn.skypack.dev/copy-to-clipboard&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clipboard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;writeText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/blockquote&gt;

&lt;p&gt;In addition to copying to the user's clipboard, we should also show some indicator to tell the user that they did copy the text. For my playground, I added the following text element next to the export button in HTML.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"export-button-container"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;'export-css'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Export CSS&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;'export-css-hint'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/p&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;Add an initial style to not show this text element&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;
&lt;span class="nf"&gt;#export-button-container&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;relative&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;grid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;place-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;margin&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="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;#export-button-container&lt;/span&gt;&lt;span class="o"&gt;&amp;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;padding&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;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;#export-css-hint&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;skyblue&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;60%&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;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.3s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then after we put the exported styles on the user's clipboard, we can show this text element to fade in, and then fade out after a short while.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;exportCSSHint&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;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;export-css-hint&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clipboard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;writeText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;exportedStyle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;exportCSSHint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Copied&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="nx"&gt;exportCSSHint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;opacity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;exportCSSHint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;opacity&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="mi"&gt;1500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This leads to the final overall code for the export CSS functionality.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;exportCSSButton&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;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;export-css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;exportCSSButton&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onclick&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;customProperties&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;scrollbarDiv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cssText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;scrollbarDiv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cssText&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;defaultCSSProperties&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;exportedStyle&lt;/span&gt; &lt;span class="o"&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;defaultElementForStyling&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;customProperties&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; } `&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cssRules&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;styleSheets&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;cssRules&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Google font styles were loaded first before index.css&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;scrollbarRules&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cssRules&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rule&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cssText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;::-webkit-scrollbar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="nx"&gt;scrollbarRules&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rule&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;modifiedRule&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cssText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#preview&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;defaultElementForStyling&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nx"&gt;exportedStyle&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;modifiedRule&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clipboard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;writeText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;exportedStyle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;exportCSSHint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Copied&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="nx"&gt;exportCSSHint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;opacity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;exportCSSHint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;opacity&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="mi"&gt;1500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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




&lt;p&gt;Great! Now your playground also has the quintessential feature of a generator app ✨ What else is left? Well, the next part will be crucial if you want to work on a playground that allows for &lt;strong&gt;raw user input&lt;/strong&gt; for styling 🚨&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Validate CSS
&lt;/h2&gt;

&lt;p&gt;This is the third feature which I didn’t include previously in the table of features for playgrounds and generators, it is not commonly thought up of &amp;amp; actually implemented. I couldn't find any guides out there that teaches you how to even do this.&lt;/p&gt;

&lt;p&gt;Ideally, when it comes to exporting content for users to use, for a better user experience, we try to make sure that our users don’t even get invalid data e.g. unopenable files, wrong syntax etc. This is because we would be causing trouble for our users who have to spend time to troubleshoot whether it's because they used the exported results wrongly or the exported results are actually invalid. &lt;/p&gt;

&lt;p&gt;Hence, it will be great if we could validate our CSS before we give it to the user. For CSS, invalid styles may be exported in a few ways. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Missing edge case handling due to browser support differences.&lt;/li&gt;
&lt;li&gt;If the user is allowed to pass in raw CSS input through a text input, sometimes the users may be unfamiliar with CSS and give an invalid CSS. Some users may even intentionally give trash input just to test the system.&lt;/li&gt;
&lt;li&gt;The CSS variables could even be updated incorrectly by the developer 
&amp;gt; e.g. there was once I accidentally removed an additional semicolon in  creating the string of exported styles 😆 of course it never made it to production.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Won’t it be great if there’s a CSS validation service? Well, the official &lt;a href="https://jigsaw.w3.org/css-validator/"&gt;W3C does offer a CSS validation service&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DqhQWAXK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1643866143789/dww-BTGIw.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DqhQWAXK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1643866143789/dww-BTGIw.gif" alt="w3c validation.gif" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The service works great! However, there are a few problems.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The website doesn’t make the API documentation very obvious. 

&lt;ol&gt;
&lt;li&gt;If you look at developer resources, &lt;a href="https://jigsaw.w3.org/css-validator/api.html"&gt;the page for Web Service API&lt;/a&gt; only states a single example that follow the SOAP (Simple Object Access Protocol) format. &lt;/li&gt;
&lt;li&gt;To be honest... I haven’t worked with SOAP before despite coding for a few years. I don’t plan to learn it unless I really have to in legacy projects, and I think most of the newer devs wouldn’t know of this format too 😅&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://jigsaw.w3.org/css-validator/manual.html#api"&gt;actual generic API documentation&lt;/a&gt; is found under the &lt;em&gt;“Experts only”&lt;/em&gt; section of the User Manual. Come on, I don’t need to be an expert to use an API 😂 &lt;/li&gt;
&lt;li&gt;The actual output by the API is &lt;em&gt;very&lt;/em&gt; verbose. Here’s an example when I tried to call the API via Postman with the default output format of html.
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TRXt2a3o--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1643867069113/-_3qf-s-T.png" alt="Screenshot 2022-02-02 at 3.59.10 PM.png" width="800" height="726"&gt;
Regardless which output format you choose, be it html/xhtml/soap12/text, all of them will give you similar results.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Luckily, I got some tips from the maintainer of css-validation, &lt;a href="https://twitter.com/twolfsn"&gt;Todd Wolfson&lt;/a&gt; on how to parse that verbose result. He introduced me to the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/DOMParser/DOMParser"&gt;DOMParser, a Web API&lt;/a&gt;. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;With this API, we can parse the HTML document we received from the W3C validation service like a normal HTML. &lt;/li&gt;
&lt;li&gt;Inspecting the HTML, we can also see that there will be &lt;code&gt;td.error&lt;/code&gt; elements if there are errors.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With these knowledge, we can write a validate CSS function which makes an API call and parse the result for validation errors.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;validateCSS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cssStylesInText&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;encodedStyles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;encodeURI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cssStylesInText&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;cssValidationResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`https://jigsaw.w3.org/css-validator/validator?profile=css3&amp;amp;text=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;encodedStyles&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cssValidationResponseText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;cssValidationResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;DOMParser&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;validationDoc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parseFromString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cssValidationResponseText&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text/html&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;validationErrors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;validationDoc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementsByClassName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&lt;/span&gt;&lt;span class="dl"&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;validationErrors&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we can simply check whether there are any validation errors before we give the exported results to the user 🔍 You can throw a toast or whatever to let the user know of the validation errors. For my project, this is unlikely to happen since all of my inputs are controlled so I didn't do that part.&lt;/p&gt;

&lt;p&gt;This leads to the overall code for the export CSS functionality + CSS validation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;validateCSS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cssStylesInText&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;encodedStyles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;encodeURI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cssStylesInText&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;cssValidationResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`https://jigsaw.w3.org/css-validator/validator?profile=css3&amp;amp;text=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;encodedStyles&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cssValidationResponseText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;cssValidationResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;DOMParser&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;validationDoc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parseFromString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cssValidationResponseText&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text/html&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;validationErrors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;validationDoc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementsByClassName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&lt;/span&gt;&lt;span class="dl"&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;validationErrors&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;exportCSSButton&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onclick&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;customProperties&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;scrollbarDiv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cssText&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;exportedStyle&lt;/span&gt; &lt;span class="o"&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;defaultElementForStyling&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;customProperties&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; } `&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cssRules&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;styleSheets&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;cssRules&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;scrollbarRules&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cssRules&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rule&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cssText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;::-webkit-scrollbar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="nx"&gt;scrollbarRules&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rule&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;modifiedRule&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cssText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#preview&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;defaultElementForStyling&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nx"&gt;exportedStyle&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;modifiedRule&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;cssValidationErrorsCollection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;validateCSS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;exportedStyle&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="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cssValidationErrorsCollection&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;No CSS validation errors found by W3C&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clipboard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;writeText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;exportedStyle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nx"&gt;exportCSSHint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Copied&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
        &lt;span class="nx"&gt;exportCSSHint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;opacity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;exportCSSHint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;opacity&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="mi"&gt;1500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;cssValidationErrorsCollection&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="c1"&gt;// add your own logic to parse the errors into readable text for your users&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 finished the 3rd feature and now, we are done for realz!  &lt;/p&gt;




&lt;p&gt;If you are keen in CSS validation for testing in web apps that are not pure Vanilla JavaScript, here are 2 libraries you can try out. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/twolfson/css-validator"&gt;css-validation&lt;/a&gt; - a simple library that uses W3C's validation service like what is shown above, and intended to run on NodeJS.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/csstree/validator"&gt;css-tree/validator&lt;/a&gt; - a more advanced library that performs validation via CSSTree parsing, and offers more customization on validation. &lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Generating playgrounds
&lt;/h2&gt;

&lt;p&gt;After all of that, if you don't want to do so much work in the configuration logic &amp;amp; styling of the playground itself, there are a few solutions below.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://css-playground.com/"&gt;CSS Playground&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;CSS Playground is a website that helps you to generate your own CSS playground. Here's an example playground on Clipping Paths with clip-path created by the website maintainer. The theme is heavily Material Design, with designated layouts for configs, documentation, etc.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qXwmsJ7h--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1643868167588/rh4ZkyJAz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qXwmsJ7h--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1643868167588/rh4ZkyJAz.png" alt="CSS playground preview" width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://storybook.js.org/"&gt;Storybook&lt;/a&gt; &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Storybook is a library that offers many features to help you showcase UI components, particularly that of design systems, which can be implemented on different frontend frameworks e.g. React, Vue, etc. Check out &lt;a href="https://storybook.js.org/docs/react/get-started/examples"&gt;the list of storybook examples&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Now that you know how to create your own CSS playground from vanilla JavaScript and also possible ways to generate a CSS playground, below is an optional overview on the concept of playgrounds.&lt;/p&gt;




&lt;h2&gt;
  
  
  The concept of Playgrounds
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;This section is a short opinionated lore on how playgrounds are helpful and how it is different from a generator. Feel free to skip this if you are not particularly keen in semantics.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The first website that I learnt CSS 5 years ago from was W3C, since it has always been the first search result that shows up in Google. I didn’t know of other resources at that time, but I really liked that for every concept, there is always a &lt;strong&gt;“Try it Yourself”&lt;/strong&gt; button so that I could experiment with what I learnt, however I want. Today, there are more established platforms like &lt;a href="https://codepen.io/"&gt;CodePen&lt;/a&gt;, &lt;a href="https://codesandbox.io/"&gt;CodeSandbox&lt;/a&gt;, &lt;a href="https://stackblitz.com/"&gt;StackBlitz&lt;/a&gt;, &lt;a href="https://snack.expo.dev/"&gt;Expo Snack&lt;/a&gt; etc where you get to write code on a browser editor on multiple files and seeing your changes get compiled and rendered on the fly.&lt;/p&gt;

&lt;p&gt;With such technology, playgrounds became a norm to help developers learn technical concepts and frameworks better, as they often help to complement information that is difficult to captured by plain text documentation through &lt;strong&gt;user interactions&lt;/strong&gt;. "Playground" is a term often used interchangeably with a "generator" since they could be quite similar in terms of the &lt;strong&gt;features&lt;/strong&gt; they offer, but in the macro perspective, their objective is different.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---i8V0t6H--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1643863937147/g9kDZ629r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---i8V0t6H--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1643863937147/g9kDZ629r.png" alt="https://cdn.hashnode.com/res/hashnode/image/upload/v1643863937147/g9kDZ629r.png" width="800" height="354"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The two have a different &lt;strong&gt;value proposition&lt;/strong&gt;, as such &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;There will be a difference in the way the user interface is designed between a playground and a generator app.&lt;/li&gt;
&lt;li&gt;The priority of feature implementation by the developer will also be different.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The value of a generator seem to match more with that of &lt;a href="https://zapier.com/blog/what-is-no-code/"&gt;no-code tools&lt;/a&gt;, software where users can create their own software products without knowing how to code, as a compared to a playground where the value is to &lt;em&gt;have fun&lt;/em&gt;. This is also a reason why I named my project as a playground rather than a generator - the export feature was more of an &lt;em&gt;after-thought&lt;/em&gt; rather than planned from the start of the project.&lt;/p&gt;

&lt;p&gt;Anyways, now that you understand better the semantic of a playground vs a generator. Below are some references which may help to inspire you to create your own playground or no-code tool. ✨&lt;/p&gt;




&lt;h2&gt;
  
  
  Idea inspirations
&lt;/h2&gt;

&lt;p&gt;Exploring specific CSS concepts&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://9elements.github.io/fancy-border-radius/"&gt;Fancy Border Radius Generator&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://css-playground.com/view/24/box-shadow-playground"&gt;Box Shadow Playground&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Free &amp;amp; Macro CSS generators&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://webcode.tools/generators/css"&gt;Web Code Tools' Ultimate CSS Generator&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.cssportal.com/css-generators.php"&gt;CSS Portal's CSS generators&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Productised Generators&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.svgbackgrounds.com/"&gt;SVG Backgrounds&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://colorhub.vercel.app/"&gt;ColorHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  That's a wrap folks! 🎉
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6fbv8uJt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://c.tenor.com/eoM1uCVuXtkAAAAM/yay-excited.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6fbv8uJt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://c.tenor.com/eoM1uCVuXtkAAAAM/yay-excited.gif" alt="birds excited" width="220" height="124"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thank you for reading, hope you enjoyed the article! &lt;a href="https://github.com/lyqht/scrollbar-playground"&gt;Scrollbar playground&lt;/a&gt; is open to feature requests btw. Feel free to create issues and star the project if you find it cool 😊 &lt;/p&gt;

&lt;p&gt;If you find the article awesome, hit the &lt;em&gt;reactions&lt;/em&gt; 🧡 and &lt;em&gt;share&lt;/em&gt; it 🐦~&lt;/p&gt;

&lt;p&gt;To stay updated whenever I post new stuff, follow me on &lt;a href="https://twitter.com/estee_tey"&gt;Twitter&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>html</category>
      <category>css</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Introduction to Enterprise Backend Development: Service Design Considerations</title>
      <dc:creator>Estee Tey</dc:creator>
      <pubDate>Thu, 13 Jan 2022 16:54:08 +0000</pubDate>
      <link>https://dev.to/lyqht/introduction-to-enterprise-backend-development-service-design-considerations-hge</link>
      <guid>https://dev.to/lyqht/introduction-to-enterprise-backend-development-service-design-considerations-hge</guid>
      <description>&lt;p&gt;In enterprise projects, you would often be thrown to work on a random spot of a very big system rather than building services one by one from ground up like start-ups. Hence, it could be much more difficult to grasp and piece together the different backend concepts when you are not even sure where or what is the head and tail of the architecture. This article aims to help you to get a better footing of the things to look out for in an enterprise backend development project.&lt;/p&gt;




&lt;h2&gt;
  
  
  Introduction to enterprise backend development
&lt;/h2&gt;

&lt;p&gt;At a high level, Enterprise backend development can be described in terms of 3 concepts - Domain, Framework and Service Types.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Domain&lt;/strong&gt;: 

&lt;ul&gt;
&lt;li&gt;Payments is an often cited domain because transaction-related issues such as concurrency &amp;amp; performance become more salient to be addressed.&lt;/li&gt;
&lt;li&gt;Other domains include health data, vehicles etc. Literally anything can be a domain.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Framework&lt;/strong&gt;: 

&lt;ul&gt;
&lt;li&gt;Spring Boot is a popular choice of Java-based opinionated framework for creating backend applications. &lt;/li&gt;
&lt;li&gt;Frameworks from other languages include Flask, Django, Express, Ruby on Rails, CakePHP. &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Types of Backend Services&lt;/strong&gt; commonly built:

&lt;ol&gt;
&lt;li&gt;Domain Service: this service that is responsible for most business logic related features&lt;/li&gt;
&lt;li&gt;Scheduler Job Service: this could be a midnight job running to send out emails or to update database records to invalidate memberships that have reached their expiration date&lt;/li&gt;
&lt;li&gt;File Ingestion Service: this is a service often found in enterprise projects to support internal operations, such as parsing the excel sheet files generated by the admin department and then helping to save those data into the database.&lt;/li&gt;
&lt;/ol&gt;


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

&lt;p&gt;For an enterprise backend development project that is starting from scratch, even before deciding the framework and types of backend services to build, it is important to evaluate the team compositions available and come up with a proper technical vision for the project. If you're joining an existing enterprise backend development project, then the technical vision would already be determined, but it will still be helpful for you to learn about the process of designing backend services, as it might be your job to enhance existing services or create new services to add to the system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Service Design
&lt;/h2&gt;

&lt;p&gt;Service Design is a phrase usually used in the field of User Experience (UX), but it is also very applicable for backend services. In the article &lt;a href="https://www.nngroup.com/articles/service-design-101/"&gt;Service Design 101 by Nielson Norman Group&lt;/a&gt;, this is the definition that they give Service Design.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Service design is the activity of planning and organizing a business’s resources (people, props, and processes) in order to (1) directly improve the employee’s experience, and (2) indirectly, the customer’s experience.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In the UX field, we identify and gather such business resources to improve the internal company's operations, which thus improve the experience that we deliver to end users. Likewise in enterprise backend, we perform a similar process of identifying components of the system:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Data entities&lt;/li&gt;
&lt;li&gt;Relationships and associations between entities&lt;/li&gt;
&lt;li&gt;Features that are required to perform business logic and deliver value&lt;/li&gt;
&lt;li&gt;Features &amp;amp; responsibilities to be delegated to specific services&lt;/li&gt;
&lt;li&gt;A "code of conduct" to be established between the producer and consumer&lt;/li&gt;
&lt;li&gt;Models required to derive the entities&lt;/li&gt;
&lt;li&gt;Validations required on the entities and models&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here's a diagram to visualize the steps and the type of design pattern/process involved for each step.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BeoCCohL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1642082759365/wVB5STrQs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BeoCCohL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1642082759365/wVB5STrQs.png" alt="service design flow chart" width="613" height="633"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;For the difference between entity and model, you can refer to &lt;a href="https://gauravgahlot.in/blog/entity-model-viewmodel-datamodel/"&gt;this short article&lt;/a&gt; &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The list of steps above &lt;strong&gt;may not be performed in the exact sequence as listed above&lt;/strong&gt; when you are work in enterprise backend development. &lt;/p&gt;

&lt;p&gt;If you work in an Agile enterprise project, it is more common for your business analysts (BA) to introduce the requirements of the system starting from step 3, and then they will leave it to the technical people (&lt;em&gt;us&lt;/em&gt;) to determine how the rest of the service design process are done. In such a scenario, it is no wonder why the &lt;a href="https://microservices.io/patterns/microservices.html"&gt;microservices architecture&lt;/a&gt; approach has become so popular for delivering enterprise backend projects. &lt;/p&gt;

&lt;p&gt;The microservices approach: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Services are designed to be &lt;strong&gt;loosely coupled&lt;/strong&gt;. Examples of loosely coupled architecture include &lt;a href="https://dev.to/abh1navv/hexagonal-architecture-3ocl"&gt;Hexagonal Architecture&lt;/a&gt;, &lt;a href="https://jeffreypalermo.com/2008/07/the-onion-architecture-part-1/"&gt;Onion Architecture&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Services are designed to be &lt;strong&gt;backward compatible&lt;/strong&gt; for services that depend on them to continue using them without a need to upgrade. This can be done with a proper versioning strategy and use of feature toggles.&lt;/li&gt;
&lt;li&gt;This approach hence allows different services to be be designed and worked on independently by different teams in parallel. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At the same time, services do not need to know how their respective producers/consumers are implemented internally in code. For example, a Python Django BFF should be able to consume data from a Java Spring Boot domain service without knowing the tech stack of the upstream service that it is calling. But if the tech stack and code implementation is unknown, how does one requests another service to perform necessary computations? That is where the APIs comes in. &lt;/p&gt;

&lt;h2&gt;
  
  
  API contract
&lt;/h2&gt;

&lt;p&gt;Application programming interfaces (API) allow microservices to interact with one another, and an API contract written by a service dictates the specifications and format of API requests that they accept from the applications that are consuming this API. In some enterprise projects, &lt;a href="https://medium.com/commencis/contract-first-api-development-with-openapi-generator-and-connexion-b21bbf2f9244"&gt;Contract-first API development&lt;/a&gt;  is a practiced concept. In my previous projects, we also perform &lt;a href="https://medium.com/john-lewis-software-engineering/consumer-driven-contract-testing-a-scalable-testing-strategy-for-microservices-3f2b09f99ed1"&gt;Contract Testing&lt;/a&gt; using &lt;a href="https://swagger.io/tools/swaggerhub/"&gt;SwaggerHub&lt;/a&gt; and &lt;a href="https://docs.pact.io/"&gt;Pact&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With an API contract, it helps to distinguish which services are the producer and the consumer. It also declares that these services interact synchronously. For asynchronous interaction, Kafka stream processing is an option and you could also enforce a data contract with a schema registry (I'll include a resource link later for this, Kafka concepts are beyond the scope of the article)&lt;/p&gt;

&lt;p&gt;There are alternative and commonly used phrases to describe the dependency relationship between services aside from consumer &amp;amp; producer - upstream &amp;amp; downstream services.&lt;/p&gt;

&lt;h2&gt;
  
  
  Upstream and Downstream Services
&lt;/h2&gt;

&lt;p&gt;Architectures are generally easier to comprehend after understanding the flow of data across different parts of the system. However, in a data flow architecture context, the meaning of the terms "upstream" and "downstream" are not as explicit. If you simply treat data flows as streams of water, you would think that the source of the stream is considered "upstream" while the destination of the stream would be considered "downstream". However in a microservices context, services could be both a producer and consumer of data. In that case, how do we distinguish which is the upstream or downstream service?&lt;/p&gt;

&lt;p&gt;On a &lt;a href="https://reflectoring.io/upstream-downstream/"&gt;Reflectoring article&lt;/a&gt;, the author deducted 2 rules for determining determine which service is the upstream and downstream service. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Dependency Rule:&lt;/strong&gt; each item depends on all the items upstream from its viewpoint&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Value Rule:&lt;/strong&gt; moving downstream, each step adds more value to the product&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In a microservices context, the value here refers to the value to the end users.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lGgBBJ1---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1642090922643/hkdRSMl27.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lGgBBJ1---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1642090922643/hkdRSMl27.png" alt="example architecture.png" width="733" height="398"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In an example enterprise project architecture as shown above, with the knowledge of the 2 rules, it is much easier to deduce that the BFF is considered a downstream service of the domain service, and the Kafka ingestion service is an upstream service of the domain service.&lt;/p&gt;

&lt;p&gt;So why does knowing the upstream and downstream services matter? As much as we try to make services loosely coupled, when we improve on an existing functionality of a service, there is still a chance that we would accidentally break other services that are dependent on that service. Hence knowing this dependency and value relationship will help us to look out for such potential problems and actively address them before they even happen.&lt;/p&gt;

&lt;h2&gt;
  
  
  Exploring further
&lt;/h2&gt;

&lt;p&gt;With this article, you should be more prepared to face the challenges of enterprise backend development regardless of the tech stack. At the minimum, you now know of some considerations that you should be making when you are working to improve or create a new backend service. &lt;/p&gt;

&lt;p&gt;If you are interested to learn more, these are some topics that I have explicitly excluded because of their complexity and variation from project to project.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Authentication&lt;/li&gt;
&lt;li&gt;Infrastructure e.g. Gateways, Routing&lt;/li&gt;
&lt;li&gt;Stream Processing with Kafka&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here are some resources for topics briefly discussed in this article&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://stackabuse.com/data-transfer-object-pattern-in-java-implementation-and-mapping/"&gt;Data Transfer Object Pattern&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.gentlydownthe.stream/"&gt;A gentle to introduction to Apache Kafka&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pluralsight.com/courses/enforcing-data-contracts-kafka-schema-registry"&gt;Enforcing Data Contracts with Kafka Schema Registry - PluralSight Course&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  That's a wrap folks! 🎉
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6fbv8uJt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://c.tenor.com/eoM1uCVuXtkAAAAM/yay-excited.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6fbv8uJt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://c.tenor.com/eoM1uCVuXtkAAAAM/yay-excited.gif" alt="birds excited" width="220" height="124"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thank you for reading, hope you enjoyed the article! &lt;/p&gt;

&lt;p&gt;If you find the article awesome, hit the &lt;em&gt;reactions&lt;/em&gt; 🧡 and &lt;em&gt;share&lt;/em&gt; it 🐦~&lt;/p&gt;

&lt;p&gt;I'll also be working on a follow up to this article which will include a Spring Boot project  example ✨&lt;/p&gt;

&lt;p&gt;To stay updated whenever I post new stuff, follow me on &lt;a href="https://twitter.com/estee_tey"&gt;Twitter&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>beginners</category>
      <category>microservices</category>
      <category>programming</category>
    </item>
    <item>
      <title>Be a dev detective with Sourcegraph 🔍</title>
      <dc:creator>Estee Tey</dc:creator>
      <pubDate>Mon, 13 Dec 2021 05:22:53 +0000</pubDate>
      <link>https://dev.to/lyqht/enhance-your-dev-detective-powers-with-sourcegraph-265e</link>
      <guid>https://dev.to/lyqht/enhance-your-dev-detective-powers-with-sourcegraph-265e</guid>
      <description>&lt;p&gt;In this article, I will show you a simple scenario where you can learn how to utilize Sourcegraph to: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Figure out the library of an &lt;strong&gt;unfamiliar&lt;/strong&gt; UI component &amp;amp; use it in a project&lt;/li&gt;
&lt;li&gt;Find other projects that use this UI component&lt;/li&gt;
&lt;li&gt;Monitor for new projects &amp;amp; revisit searches&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Figuring out an unfamiliar UI component
&lt;/h2&gt;

&lt;p&gt;This component is something that I have not seen in any of the design system libraries that I've used. I first found it on &lt;a href="https://www.dremio.com/"&gt;Dremio&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dNz1tjoS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1639029491981/be3YOr8RR.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dNz1tjoS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1639029491981/be3YOr8RR.gif" alt="Dremio.gif" width="800" height="385"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is an interactive slider in the centre of the container, and 2 very well designed images are presented in stark contrast next to each other. Notice the light/dark color scheme, and the use of same shapes in both images. This is a really smart way of using this component to showcase the different aspects &amp;amp; features of the services that they offer! &lt;/p&gt;

&lt;p&gt;Putting the images aside, I didn't know of the name of the component so I didn't know how to exactly Google this &lt;em&gt;thing&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;🔍 To get some &lt;strong&gt;clues&lt;/strong&gt;, let's &lt;a href="https://esteetey.dev/navigate-the-frontend-easily-with-the-inspector"&gt;once again use the Browser Inspector&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Inspecting the element
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ssCckIKq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1639028875398/_iLcvsZan.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ssCckIKq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1639028875398/_iLcvsZan.png" alt="Inspecting Dremio component.png" width="800" height="386"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There's a funny repeated class name called &lt;code&gt;twentytwenty&lt;/code&gt; 🤔. Normally developers who make their own custom UI would use &lt;a href="https://ukiahsmith.com/blog/semantic-class-naming/"&gt;Semantic Class Naming&lt;/a&gt; rather than a vague name like this. This gives us grounds to &lt;strong&gt;deduce that...&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;👉 this classname is provided for a &lt;strong&gt;library implementation&lt;/strong&gt;.  &lt;/p&gt;

&lt;p&gt;Now let's use Sourcegraph, a universal code search tool to look for this library, since &lt;em&gt;we don't know whether the library is available on GitHub/ GitLab or etc&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Search for the library with Sourcegraph
&lt;/h2&gt;

&lt;p&gt;Sourcegraph can be accessed &lt;a href="https://sourcegraph.com/search"&gt;here&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;🔍 Let's try to find the library using the query &lt;code&gt;repo: twentytwenty&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gRQ5EuEo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1639111200338/7cxN36QHB.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gRQ5EuEo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1639111200338/7cxN36QHB.png" alt="Sourcegraph demo.png" width="800" height="366"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Bingo! We got a few repositories with the description of "image comparison" which tallies to what the component does. From a glance, we can also see that this component is compatible with the following libraries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;jquery (1.2k ⭐)&lt;/li&gt;
&lt;li&gt;Vue (197 ⭐)&lt;/li&gt;
&lt;li&gt;React (40 ⭐)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once we click on one of the repositories, we can see that we can navigate the whole project within Sourcegraph itself ✨ There's also a handy icon for us to go the original GitHub repository if we wish to watch/star the repository.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wOx3LR_W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1639029099473/0xlaeVdt8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wOx3LR_W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1639029099473/0xlaeVdt8.png" alt="Sourcegraph demo ii.png" width="800" height="387"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Following the setup instructions in the README for the Vue version of the component, I was able to spurn up the component very easily on codepen, using a neko 🐱 and doggo 🐶 image, similar to what we saw on Dremio.&lt;/p&gt;

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

&lt;p&gt;And that is how you can add this image slider UI to your own project too! If you're using React or jquery, you can check out the other 2 repositories instead. Although Dremio already shows a very good use case of the TwentyTwenty component, it will be cool for us to find out other creative ways of using this component too. &lt;/p&gt;

&lt;h2&gt;
  
  
  Search for other projects that used the component
&lt;/h2&gt;

&lt;p&gt;🔍 To search for other projects that use this component, we can construct a different search query that comprise of 3 filters&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;select:repo&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;by default, Sourcegraph gives us search results in terms of the files where they find relevant content. however, we only want to know the &lt;em&gt;projects&lt;/em&gt;, in other words the &lt;em&gt;repositories&lt;/em&gt; that uses the component&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-repo:^github\.com/.*/.*twentytwenty&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;the &lt;code&gt;repo&lt;/code&gt; search filter takes in a regex pattern &lt;/li&gt;
&lt;li&gt;we want to exclude the repositories that provide the UI component such as react-twentytwenty, vue-twentytwenty and so on, so the search filter becomes &lt;code&gt;-repo&lt;/code&gt; &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;content:"twentytwenty-container"&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;we want the projects to include usage of the &lt;code&gt;twentytwenty-container&lt;/code&gt; classname like how Dremio did it.&lt;/li&gt;
&lt;li&gt;you could try with just &lt;code&gt;twentytwenty&lt;/code&gt;, but you will get search results irrelevant to this component as well, because there is a widely used official &lt;a href="https://wordpress.org/themes/twentytwenty/"&gt;Wordpress theme called TwentyTwenty&lt;/a&gt; too.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Combining all three, we get this search query.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;select:repo -repo:^github\.com/.*/.*twentytwenty content:"twentytwenty-container"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you copy and paste it into Sourcegraph, you will get the following search results.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lsb_fL1M--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1639137168155/rZeKTA5pw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lsb_fL1M--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1639137168155/rZeKTA5pw.png" alt="image.png" width="800" height="462"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These repositories are all using the the TwentyTwenty component! However, we can't tell at a glance about the framework of each project that are listed in the search results. Most of these results would be using the jQuery plugin version of the component, since the component was first created as that plugin. But what if your project doesn't use jQuery? &lt;/p&gt;

&lt;h2&gt;
  
  
  Finding a specific framework project
&lt;/h2&gt;

&lt;p&gt;For example, say you are working on a Vue project, it would be more relevant to find projects of the same framework that use this component and check out how they implement it in code. &lt;/p&gt;

&lt;p&gt;🔍 To do this, you can add an extra search filter &lt;code&gt;lang: Vue&lt;/code&gt;, which creates the combined query below.&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="k"&gt;select&lt;/span&gt;:repo &lt;span class="nt"&gt;-repo&lt;/span&gt;:^github&lt;span class="se"&gt;\.&lt;/span&gt;com/.&lt;span class="k"&gt;*&lt;/span&gt;/.&lt;span class="k"&gt;*&lt;/span&gt;twentytwenty content:&lt;span class="s2"&gt;"twentytwenty-container"&lt;/span&gt; lang:Vue
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you run this search query, as of this post, there is only 1 project out of the 72 that we found just now that is Vue. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XaL1nZg1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1639138281224/bp7wm2yi4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XaL1nZg1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1639138281224/bp7wm2yi4.png" alt="image.png" width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this open source font project &lt;a href="https://github.com/rubjo/victor-mono"&gt;Victor Mono&lt;/a&gt;, they created a Vue website app that uses the TwentyTwenty component to showcase the difference between the font they offer and the other common programming fonts. It's a pretty cool use case 😎!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GGBBmlGn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1639145587347/Am0cZd-Gb.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GGBBmlGn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1639145587347/Am0cZd-Gb.gif" alt="VictorMono TwentyTwenty Component Usage Demo.gif" width="800" height="666"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Won't it be nice if you could get notified in the future when there are new open source projects that are using creative usages of Vue projects using this component? &lt;/p&gt;

&lt;p&gt;Well, Sourcegraph comes with this functionality! ✨&lt;/p&gt;

&lt;h2&gt;
  
  
  Code Monitoring
&lt;/h2&gt;

&lt;p&gt;To save your searches and monitor code, you would need to create an account with Sourcegraph. You can sign up easily using the GitHub authentication feature. &lt;/p&gt;

&lt;p&gt;To create a code monitor, you can click on "Monitoring" at the NavBar.&lt;/p&gt;

&lt;p&gt;First, you have to create a new trigger based on a search query. We can copy paste the query we had before, and then add a new filter &lt;code&gt;type:diff&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;&lt;span class="k"&gt;select&lt;/span&gt;:repo &lt;span class="nt"&gt;-repo&lt;/span&gt;:^github&lt;span class="se"&gt;\.&lt;/span&gt;com/.&lt;span class="k"&gt;*&lt;/span&gt;/.&lt;span class="k"&gt;*&lt;/span&gt;twentytwenty content:&lt;span class="s2"&gt;"twentytwenty-container"&lt;/span&gt; lang:Vue &lt;span class="nb"&gt;type&lt;/span&gt;:diff patternType:literal
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;This new filter is required to tell the code monitor when to notify you.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;type:diff&lt;/code&gt; represents you &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Gid_-bMH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1639150064007/pMI_p1U-a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Gid_-bMH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1639150064007/pMI_p1U-a.png" alt="image.png" width="644" height="359"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then we have to choose an action to run. For now, there's only "Send email notifications", so choose that option and continue. You can also click on "Send test email" to see how the notification will be like.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--f8PVHFPD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1639149254445/x6j59NZEm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--f8PVHFPD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1639149254445/x6j59NZEm.png" alt="image.png" width="652" height="412"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is the test email that I got.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZHHEPfOY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1639149336119/kZ8i38wFO.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZHHEPfOY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1639149336119/kZ8i38wFO.png" alt="Test Email by Sourcegraph" width="800" height="532"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After filling all the necessary inputs, the "Create code monitor" button will be clickable.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9Z7ssrkv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1639148657088/roWVlNB-q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9Z7ssrkv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1639148657088/roWVlNB-q.png" alt="image.png" width="772" height="906"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Save a search
&lt;/h2&gt;

&lt;p&gt;To save a search and revisit it manually later, there are 2 ways you can reach the Saved Searches page. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;First way: Click on your profile and then on "Saved Searches"
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rmO1kFqS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1639149633158/Hpe_9JKnz.png" alt="image.png" width="800" height="308"&gt;
&lt;/li&gt;
&lt;li&gt;Second way: Click on the "Save Search" button after you run a query at the search page.
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4XWwATFy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1639149753853/j2UhvzSMI.png" alt="image.png" width="800" height="448"&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Then you will be brought to this page, where you can add a description for the query that you are saving.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FhDBic4W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1639148144409/ag3WEnb47.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FhDBic4W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1639148144409/ag3WEnb47.png" alt="image.png" width="800" height="415"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;For a more exhaustive list of search query syntax to craft more detailed queries, check out the &lt;a href="https://docs.sourcegraph.com/code_search/reference/queries"&gt;Sourcegraph documentation for Search Query Syntax&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  That's a wrap folks! 🎉
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6fbv8uJt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://c.tenor.com/eoM1uCVuXtkAAAAM/yay-excited.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6fbv8uJt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://c.tenor.com/eoM1uCVuXtkAAAAM/yay-excited.gif" alt="birds excited" width="220" height="124"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thank you for reading, hope you enjoyed the article to become a better dev detective with Sourcegraph! This is just a very simple use case of how you can utilize Sourcegraph to learn of new libraries like TwentyTwenty and how other projects have used them. Imagine  &lt;a href="https://about.sourcegraph.com/case-studies/"&gt;other possibilities you could do&lt;/a&gt; with this powerful search tool! ✨&lt;/p&gt;

&lt;p&gt;If you find the article awesome, hit the &lt;em&gt;reactions&lt;/em&gt; 🧡 and &lt;em&gt;share&lt;/em&gt; it 🐦~&lt;/p&gt;

&lt;p&gt;To stay updated whenever I post new stuff, follow me on &lt;a href="https://twitter.com/estee_tey"&gt;Twitter&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>webdev</category>
      <category>vue</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Article Badge Counter (ABC) Workflow</title>
      <dc:creator>Estee Tey</dc:creator>
      <pubDate>Fri, 03 Dec 2021 10:01:38 +0000</pubDate>
      <link>https://dev.to/lyqht/article-badge-counter-abc-workflow-937</link>
      <guid>https://dev.to/lyqht/article-badge-counter-abc-workflow-937</guid>
      <description>&lt;p&gt;&lt;strong&gt;Submission Category&lt;/strong&gt;: Wacky Wildcards&lt;/p&gt;




&lt;h3&gt;
  
  
  Link to Code
&lt;/h3&gt;

&lt;p&gt;Main repository&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--A9-wwsHG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/lyqht"&gt;
        lyqht
      &lt;/a&gt; / &lt;a href="https://github.com/lyqht/article-badge-counter-workflow"&gt;
        article-badge-counter-workflow
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      GitHub Action Workflow that complements blog-post-workflow to generate a Shields.io badge. Intended to be easy like ABC
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;h1&gt;
article-count-workflow&lt;/h1&gt;
&lt;p&gt;This GitHub action workflow aims to help you to create an article badge counter like this to showcase on your profile that you are more than just a developer 😉 Users can click it and be redirected to a website of your choice.&lt;/p&gt;
&lt;p&gt;The default badge looks something like this.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://esteetey.dev/" rel="nofollow"&gt;&lt;img alt="Website" src="https://camo.githubusercontent.com/dd11fe4621d0b811dd695d3cfcd4dbf0b2304934f4b3913afe1f313b467eece2/68747470733a2f2f696d672e736869656c64732e696f2f776562736974653f6c6162656c3d626c6f6720f09f939d2675705f6d6573736167653d31382061727469636c65732675726c3d68747470733a2f2f65737465657465792e6465762f"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
What it does&lt;/h2&gt;
&lt;p&gt;This workflow is a composite action:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The number of articles is retrieved using the &lt;a href="https://github.com/gautamkrishnar/blog-post-workflow"&gt;blog-post-workflow&lt;/a&gt; action.&lt;/li&gt;
&lt;li&gt;The badge is generated using Shields.io, which are not clickable by default.&lt;/li&gt;
&lt;li&gt;This action has a script to take in your inputs to create a customized clickable badge.&lt;/li&gt;
&lt;li&gt;The commit and push github actions to your repo are performed by the &lt;a href="https://github.com/stefanzweifel/git-auto-commit-action"&gt;git-auto-commit workflow&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
How to use&lt;/h2&gt;
&lt;p&gt;If you are new to GitHub Actions, refer to &lt;a href="https://github.com/lyqht/article-badge-counter-workflow#if-you-dont-have-an-existing-github-action-workflow-for-your-repository"&gt;this section&lt;/a&gt;. Otherwise, you can get started by referring to to the example given and the input options available.&lt;/p&gt;
&lt;h3&gt;
Example&lt;/h3&gt;
&lt;p&gt;This is an example…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/lyqht/article-badge-counter-workflow"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Also tried out doing a pre-release on the GitHub Action Marketplace &lt;a href="https://github.com/marketplace/actions/article-count-workflow"&gt;here&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  About the workflow
&lt;/h3&gt;

&lt;p&gt;This workflow is a composite action:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The number of articles is retrieved using the &lt;a href="https://github.com/gautamkrishnar/blog-post-workflow"&gt;blog-post-workflow&lt;/a&gt; action.&lt;/li&gt;
&lt;li&gt;The badge is generated with &lt;a href="https://github.com/badges/shields"&gt;Shields.io&lt;/a&gt;, which is not clickable by default.&lt;/li&gt;
&lt;li&gt;This action has a script to take in your inputs to create a customized clickable badge.&lt;/li&gt;
&lt;li&gt;The commit and push github actions to your repo are performed by the &lt;a href="https://github.com/stefanzweifel/git-auto-commit-action"&gt;git-auto-commit workflow&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Using the GitHub Action
&lt;/h3&gt;

&lt;p&gt;An example for the action yaml, instructions for those who are unfamiliar with GitHub Actions, and the input options available to be used to customize the badge are included in the repository's README.md. &lt;/p&gt;

&lt;p&gt;Users can display their generated badge on their GitHub profile README.md in any way they like. This is a layout example from my &lt;a href="https://github.com/lyqht"&gt;profile&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4083fSjK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/coqv6atgbq8qt8lug3q2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4083fSjK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/coqv6atgbq8qt8lug3q2.png" alt="GitHub Profile with generated article badge counter" width="800" height="700"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Additional Resources / Info
&lt;/h3&gt;

&lt;p&gt;Special thanks to &lt;a href="https://dev.to/gautamkrishnar"&gt;Gautam Krishnar&lt;/a&gt; for helping to fix &lt;a href="https://github.com/gautamkrishnar/blog-post-workflow/issues/110"&gt;the bug that I discovered&lt;/a&gt; while working on this action 🧡 He also submitted the &lt;code&gt;blog-post-workflow&lt;/code&gt; action for the hackathon, and the article can be found &lt;a href="https://dev.to/gautamkrishnar/blog-post-workflow-github-action-1821"&gt;here&lt;/a&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  Room for improvements
&lt;/h3&gt;

&lt;p&gt;There were a lot of troubleshooting along the way, but it was fun to learn about composite action concepts and create this action 😄 Ideally, I would also want to include some form of E2E test on the README as part of the composite action to make sure that the badge does render correctly before the action pushes the commit.&lt;/p&gt;

</description>
      <category>actionshackathon21</category>
      <category>github</category>
      <category>opensource</category>
      <category>writing</category>
    </item>
    <item>
      <title>Make your website stand out with a custom scrollbar 🌟</title>
      <dc:creator>Estee Tey</dc:creator>
      <pubDate>Thu, 25 Nov 2021 16:36:56 +0000</pubDate>
      <link>https://dev.to/lyqht/make-your-website-stand-out-with-a-custom-scrollbar-76m</link>
      <guid>https://dev.to/lyqht/make-your-website-stand-out-with-a-custom-scrollbar-76m</guid>
      <description>&lt;p&gt;The scrollbar is easily one of the most neglected UI components out there that are not leveraged by many websites to enhance their users' experience. By default, on all HTML, when the content of the website exceeds the viewport height, a scrollbar will automatically appear on the right, just like the one you're seeing as you read this article now.&lt;/p&gt;

&lt;p&gt;The default scrollbar looks decent, but &lt;strong&gt;it can be better&lt;/strong&gt;. Let's take a reference from the experts. Here's an example of a unique scrollbar found on &lt;a href="https://css-tricks.com/" rel="noopener noreferrer"&gt;CSSTricks.com&lt;/a&gt;. Put your eyes to the right of the GIF! 👀&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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1637475913512%2FFUgV5lQu_.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1637475913512%2FFUgV5lQu_.gif" alt="https://cdn.hashnode.com/res/hashnode/image/upload/v1637475913512/FUgV5lQu_.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This scrollbar fits in with the dark theme of the website much more than the default scrollbar. It also has a little bevel feel to it.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to re-create the CSS Tricks Scrollbar
&lt;/h2&gt;

&lt;p&gt;As I've taught my previous readers before, you can &lt;a href="https://esteetey.dev/navigate-the-frontend-easily-with-the-inspector" rel="noopener noreferrer"&gt;navigate the frontend simply by using the Browser Inspector&lt;/a&gt;. So let's do that!&lt;/p&gt;

&lt;p&gt;Here are the steps for inspecting the scrollbar:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Inspect the &lt;code&gt;&amp;lt;html /&amp;gt;&lt;/code&gt; element directly. This is because there's no &lt;code&gt;&amp;lt;scrollbar /&amp;gt;&lt;/code&gt; tag. The scrollbar is not a specific HTML element.&lt;/li&gt;
&lt;li&gt;To find how the scrollbar is styled, filter for 'scrollbar' in the styles tab.&lt;/li&gt;
&lt;li&gt;You will see a few pseudo-elements that are used to style the scrollbar.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here's a preview of how your inspector will look like.&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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1637476690450%2FIqzCgdqwz.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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1637476690450%2FIqzCgdqwz.png" alt="https://cdn.hashnode.com/res/hashnode/image/upload/v1637476690450/IqzCgdqwz.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the style tab, these are the relevant rules that you can find for styling the scrollbar.&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;html&lt;/span&gt;&lt;span class="nd"&gt;::-webkit-scrollbar&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;16px&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;16px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;html&lt;/span&gt;&lt;span class="nd"&gt;::-webkit-scrollbar-thumb&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#434343&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;16px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;box-shadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;inset&lt;/span&gt; &lt;span class="m"&gt;2px&lt;/span&gt; &lt;span class="m"&gt;2px&lt;/span&gt; &lt;span class="m"&gt;2px&lt;/span&gt; &lt;span class="n"&gt;hsla&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="m"&gt;0%&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="m"&gt;.25&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="nb"&gt;inset&lt;/span&gt; &lt;span class="m"&gt;-2px&lt;/span&gt; &lt;span class="m"&gt;-2px&lt;/span&gt; &lt;span class="m"&gt;2px&lt;/span&gt; &lt;span class="n"&gt;rgba&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="m"&gt;0&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="m"&gt;.25&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;html&lt;/span&gt;&lt;span class="nd"&gt;::-webkit-scrollbar-track&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;linear-gradient&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="m"&gt;#434343&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;#434343&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;#111&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;#111&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 I simply copy these styles over and use it for a very long HTML page, the scrollbar will look as it is in CSSTricks!&lt;/p&gt;

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

&lt;p&gt;Now that we know the  &lt;a href="https://esteetey.dev/primer-css-pseudo-elements-and-pseudo-classes" rel="noopener noreferrer"&gt;pseudo-elements&lt;/a&gt; do work, let's understand &lt;strong&gt;how they work&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to style a scrollbar
&lt;/h2&gt;

&lt;p&gt;For the CSS Tricks Scrollbar, there were &lt;strong&gt;3 pseudo-elements&lt;/strong&gt; used:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;::-webkit-scrollbar&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;::-webkit-scrollbar-thumb&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;::-webkit-scrollbar-track&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's a simple diagram to depict those 3 parts of the scrollbar.&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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1637505909053%2FPBfovy_H9.jpeg" 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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1637505909053%2FPBfovy_H9.jpeg" alt="https://cdn.hashnode.com/res/hashnode/image/upload/v1637505909053/PBfovy_H9.jpeg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In addition to these 3 pseudo-elements, there are 4 other parts of the scrollbar that you can consider styling. According to  &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/::-webkit-scrollbar" rel="noopener noreferrer"&gt;MDN Web Docs&lt;/a&gt; , these are the &lt;strong&gt;7 pseudo-elements&lt;/strong&gt; that you can utilize to style your scrollbar.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;::-webkit-scrollbar&lt;/code&gt; — the entire scrollbar.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;::-webkit-scrollbar-thumb&lt;/code&gt; — the draggable scrolling handle.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;::-webkit-scrollbar-track&lt;/code&gt; — the track (progress bar) of the scrollbar, where there is a gray bar on top of a white bar.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;::-webkit-scrollbar-button&lt;/code&gt; — the buttons on the scrollbar (arrows pointing upwards and downwards that scroll one line at a time).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;::-webkit-scrollbar-track-piece&lt;/code&gt; — the part of the track (progress bar) not covered by the handle.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;::-webkit-scrollbar-corner&lt;/code&gt; — the bottom corner of the scrollbar, where both horizontal and vertical scrollbars meet This is often the bottom-right corner of the browser window.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;::-webkit-resizer&lt;/code&gt; — the draggable resizing handle that appears at the bottom corner of some elements.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Some of these descriptions may be a little hard to visualize. So here's a diagram to help you with that.&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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1637841466109%2F-R2xLNyGy.jpeg" 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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1637841466109%2F-R2xLNyGy.jpeg" alt="https://cdn.hashnode.com/res/hashnode/image/upload/v1637841466109/-R2xLNyGy.jpeg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For a gentle introduction to how some of these pseudo-elements can be used, I have created a scrollbar playground for you to create a simple custom scrollbar and export its CSS. Take some time to play around with the settings to deduce how the pseudo-elements work✨&lt;/p&gt;

&lt;p&gt;👉 Visit the playground in full page view &lt;a href="https://lyqht.github.io/scrollbar-playground" rel="noopener noreferrer"&gt;here&lt;/a&gt;!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 Important Tip in using Scrollbar Playground&lt;/p&gt;

&lt;p&gt;Clicking the export to CSS button will copy the element's styles to your clipboard. Afterwards, you can open up an empty codepen and just paste the styles directly to the CSS tab to see the magic. &lt;strong&gt;Remember to set the height of the body element to a very big value&lt;/strong&gt; like &lt;code&gt;height: 3000px;&lt;/code&gt; so that there is content overflow for a scrollbar to appear.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Observations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Width of the scrollbar
&lt;/h3&gt;

&lt;p&gt;When you change...&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scrollbar Height -&amp;gt; the width of the &lt;strong&gt;vertical&lt;/strong&gt; scrollbar changes.&lt;/li&gt;
&lt;li&gt;Scrollbar Width -&amp;gt; the width of the &lt;strong&gt;horizontal&lt;/strong&gt; scrollbar changes.&lt;/li&gt;
&lt;/ul&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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1637842383087%2FOFe4cX3BS.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1637842383087%2FOFe4cX3BS.gif" alt="https://cdn.hashnode.com/res/hashnode/image/upload/v1637842383087/OFe4cX3BS.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The style is applied at the following pseudo-element selector.&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="nf"&gt;#fake-window&lt;/span&gt;&lt;span class="nd"&gt;::-webkit-scrollbar&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;16px&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;16px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Scrollbar Buttons
&lt;/h3&gt;

&lt;p&gt;In the playground, you can choose to hide or show them. If you show them, you can choose to show 1 or 2 buttons.&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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1637843269929%2FfYG5Qjvfz.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1637843269929%2FfYG5Qjvfz.gif" alt="https://cdn.hashnode.com/res/hashnode/image/upload/v1637843269929/fYG5Qjvfz.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Although it is much more common to see scrollbar with just the single arrow buttons on both ends of the scrollbar since that's the native behavior, if you apply styles to just the &lt;code&gt;::--webkit-scrollbar-button&lt;/code&gt; element itself, you will see 2 buttons on each side.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This behavior may differ depending on the Chromium version of your browser, and also the element you apply the style to. For example, if you apply the same style on codepen, you don't see the double button, but if you apply it to a simple html page and open it up on your local machine, you will see the double button, like what you see on the playground when you choose the 2 button option.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To enforce that the scrollbar only shows one button on each side, the following style needs to be applied.&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;::-webkit-scrollbar-button:vertical:start:increment&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nd"&gt;::-webkit-scrollbar-button:vertical:end:decrement&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nd"&gt;::-webkit-scrollbar-button:horizontal:start:increment&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nd"&gt;::-webkit-scrollbar-button:horizontal:end:decrement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Coloring parts of the scrollbar
&lt;/h3&gt;

&lt;p&gt;Most of the scrollbar parts can be colored individually.&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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1637843698246%2Flw6_sPHrM.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1637843698246%2Flw6_sPHrM.gif" alt="https://cdn.hashnode.com/res/hashnode/image/upload/v1637843698246/lw6_sPHrM.gif"&gt;&lt;/a&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="nf"&gt;#fake-window&lt;/span&gt;&lt;span class="nd"&gt;::-webkit-scrollbar-thumb&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;--scrollbar-thumb-color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;#3B82F6&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;#fake-window&lt;/span&gt;&lt;span class="nd"&gt;::-webkit-scrollbar-track&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;--scrollbar-track-color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;#A1A1AA&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;#fake-window&lt;/span&gt;&lt;span class="nd"&gt;::-webkit-scrollbar-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;--scrollbar-button-color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;#3F3F46&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;#fake-window&lt;/span&gt;&lt;span class="nd"&gt;::-webkit-scrollbar-corner&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;--scrollbar-corner-color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;#FFFFFF&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Exploring further
&lt;/h2&gt;

&lt;p&gt;Now that you have some basics of setting css properties in terms of sizes and colors for the scrollbar pseudo-elements, consider special scrollbars like this too! ✨&lt;/p&gt;

&lt;h3&gt;
  
  
  Examples
&lt;/h3&gt;

&lt;p&gt;Here's a very simple example that I made which only has a horizontal scrollbar, but the thumb is an animating cat! 😸 The icon is taken from &lt;a href="https://github.com/Gowee/nyancat-svg" rel="noopener noreferrer"&gt;Gowee's nyan cat SVG on Github&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;Another note-worthy scrollbar to be mentioned is the scrollbar on &lt;a href="https://www.swyx.io/react-sfcs-here/" rel="noopener noreferrer"&gt;Shawn's portfolio&lt;/a&gt;. Eyes to the right 👀 It is a candy stick moving on a heart palette track and it feels super delightful to look at it! Literal eye candy😋&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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1637310977859%2FxUqav5j6g.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1637310977859%2FxUqav5j6g.gif" alt="https://cdn.hashnode.com/res/hashnode/image/upload/v1637310977859/xUqav5j6g.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://webkit.org/blog/363/styling-scrollbars/" rel="noopener noreferrer"&gt;The original Webkit's article on styling scrollbars&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;This article talks about even more selectors that you can utilize to style your scrollbar, although they are rarely used&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://css-tricks.com/the-current-state-of-styling-scrollbars/" rel="noopener noreferrer"&gt;CSS Tricks' article on styling scrollbars&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;Great tons of modern looking examples here!&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  Libraries for adding a Custom Scrollbar
&lt;/h2&gt;

&lt;p&gt;Of course, if you're lazy and prefer a fast and pre-made solution, here are 3 public packages that I've found which you can choose to add into your project. The way they implemented them is different, so do check out their docs to see which you prefer.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/idiotWu/smooth-scrollbar" rel="noopener noreferrer"&gt;Smooth Scrollbar&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;A simple customizable, performant Javascript plugin solution&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://github.com/KingSora/OverlayScrollbars" rel="noopener noreferrer"&gt;Overlay Scrollbars&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;comes with themes and framework wrappers for Angular, React and Vue&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://github.com/mdbootstrap/perfect-scrollbar" rel="noopener noreferrer"&gt;Perfect Scrollbar&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;made by the Material Bootstrap team&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  Bonus: Hiding the scrollbar UI
&lt;/h2&gt;

&lt;p&gt;Although this article is about making your website stand out with a custom scrollbar, if for any reason you hate having to see a scrollbar UI take up visual space on your website,  or you want your website to be look alike on both desktop and mobile (websites do not show scrollbars by default on mobile browsers), you can simply set the &lt;code&gt;display: none&lt;/code&gt; to the pseudo-element&lt;code&gt;::-webkit-scrollbar&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;html::-webkit-scrollbar {
  display: none;
}

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

&lt;/div&gt;



&lt;p&gt;You will still be able to scroll on a webpage, just that you can't see the scrollbar UI. For most use cases, I do not recommend you to disable the scrolling entirely 😆&lt;/p&gt;




&lt;h2&gt;
  
  
  That's a wrap folks! 🎉
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fc.tenor.com%2FeoM1uCVuXtkAAAAM%2Fyay-excited.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fc.tenor.com%2FeoM1uCVuXtkAAAAM%2Fyay-excited.gif" alt="https://c.tenor.com/eoM1uCVuXtkAAAAM/yay-excited.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thank you for reading, hope you enjoyed the article! If you have tried out the Scrollbar Playground and created a scrollbar that you like, do share a screenshot below! 😊&lt;/p&gt;

&lt;p&gt;If you find the article awesome, hit the &lt;em&gt;reactions&lt;/em&gt; 🧡 and &lt;em&gt;share&lt;/em&gt; it 🐦~&lt;/p&gt;

&lt;p&gt;To stay updated whenever I post new stuff, follow me on &lt;a href="https://twitter.com/estee_tey" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>css</category>
      <category>tutorial</category>
      <category>codepen</category>
    </item>
    <item>
      <title>Introduction to Scalable Vector Graphics (SVG)</title>
      <dc:creator>Estee Tey</dc:creator>
      <pubDate>Tue, 16 Nov 2021 14:56:12 +0000</pubDate>
      <link>https://dev.to/lyqht/introduction-to-scalable-vector-graphics-svg-734</link>
      <guid>https://dev.to/lyqht/introduction-to-scalable-vector-graphics-svg-734</guid>
      <description>&lt;p&gt;This article is an enhanced version of the lunch &amp;amp; learn session that I presented recently on &lt;strong&gt;Introduction to Scalable Vector Graphics.&lt;/strong&gt; The slides can be found &lt;a href="https://lyqht.github.io/intro-to-svg-slides" rel="noopener noreferrer"&gt;here&lt;/a&gt;, where you can choose to download a PDF version. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;There are some iframes which have been excluded in this article e.g. the embedded presentation slides, because the Forem platform does not support them. But if you're keen to see them, check out the &lt;a href="https://esteetey.dev/introduction-to-svg" rel="noopener noreferrer"&gt;original article&lt;/a&gt; on my tech blog. &lt;/p&gt;
&lt;/blockquote&gt;




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

&lt;p&gt;SVG refers to &lt;strong&gt;S&lt;/strong&gt;calable &lt;strong&gt;V&lt;/strong&gt;ector &lt;strong&gt;G&lt;/strong&gt;raphics, and it is a type of image format that is widely available on websites. Before we describe further on SVG, let's understand the importance of images and other image format types.&lt;/p&gt;

&lt;h3&gt;
  
  
  🖼 Images
&lt;/h3&gt;

&lt;p&gt;On a very high level, we can split images into 2 generic types: &lt;strong&gt;Raster&lt;/strong&gt; and &lt;strong&gt;Vector&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Raster images are drawn pixel by pixel, and may look different depending on the &lt;strong&gt;resolution/ pixel density&lt;/strong&gt; of the device. Vector images look good and crisp regardless of the resolution. Here's a Codepen to illustrate the difference between the two, try &lt;strong&gt;resizing&lt;/strong&gt; the different images! ✨&lt;/p&gt;

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

&lt;p&gt;The raster art was drawn by my boyfriend using Microsoft Paint (what a madlad! 😍), while the vectorized version are traced and coloured by me using Adobe Illustrator. It was a joint project for creating a couple tee design 😉&lt;/p&gt;

&lt;p&gt;I'm sure you're curious about this question - if vector images always look good, then why would we ever consider raster images? Well that's because of 2 practical factors. The first being the fact that the technology to support the &lt;em&gt;creation&lt;/em&gt; &amp;amp; &lt;em&gt;usage&lt;/em&gt; of raster images is much more accessible compared to SVG. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;On the designers' side, there is generally more familiarity to work with tools to create raster art (Photoshop) than vector art (Illustrator).&lt;/li&gt;
&lt;li&gt;On the developer's side, most file uploaders e.g. for uploading avatar images, background banners etc, are implemented to be restricted to raster image formats only.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The second reason for preferring raster images over SVG is the &lt;strong&gt;file size&lt;/strong&gt;. Different image formats of the same visual will result in different file sizes, and thus different pages sizes. According to &lt;em&gt;&lt;a href="https://developers.google.com/speed/webp/docs/compression" rel="noopener noreferrer"&gt;Google Developers Page on Image Compression&lt;/a&gt;,&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Images comprise up to 60%-65% of bytes on most web pages and page size is a major factor in total rendering time. Page size is especially important for mobile devices, where smaller size images will help to save both bandwidth and battery life.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For &lt;strong&gt;different use cases,&lt;/strong&gt; we should be using different image file types. In the context of online images, &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For Raster Image File Types, there's 4 types - GIF, PNG, JPG and WEBP.&lt;/li&gt;
&lt;li&gt;For Vector Image File Types, there's only SVG!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's a table for the description of the name of the file extensions.&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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1637037579358%2FaSzqOFyrq.jpeg" 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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1637037579358%2FaSzqOFyrq.jpeg" alt="Table showing file extensions"&gt;&lt;/a&gt;&lt;br&gt;
And here's a brief context behind how each raster file type came about.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;GIF

&lt;ul&gt;
&lt;li&gt;GIF was &lt;strong&gt;one of the first two graphics formats supported&lt;/strong&gt; by HTML, along with &lt;a href="https://en.wikipedia.org/wiki/X_BitMap" rel="noopener noreferrer"&gt;XBM&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;PNG

&lt;ul&gt;
&lt;li&gt;PNG was created to improve upon and &lt;strong&gt;replace GIF&lt;/strong&gt; (Graphics Interchange Format) as an image-file format.&lt;/li&gt;
&lt;li&gt;It supports a better compression algorithm and also transparency in an image.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;JPG

&lt;ul&gt;
&lt;li&gt;The original file extension for the Joint Photographic Expert Group File Format was &lt;code&gt;.jpeg&lt;/code&gt;. On Mac, this was supported but on Windows, all files required a &lt;strong&gt;3-letter file extension&lt;/strong&gt;. So, the file extension was shortened to &lt;code&gt;.jpg&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Eventually, with upgrades Windows also began to accept &lt;code&gt;.jpeg&lt;/code&gt;. However, many users were already used to ‘.jpg’, so both the 3-letter file extension and the 4-letter were commonly used, and still are.&lt;/li&gt;
&lt;li&gt;Today, the most commonly accepted and used form is the &lt;code&gt;.jpg&lt;/code&gt;, as &lt;strong&gt;many users were Windows users&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;WEBP

&lt;ul&gt;
&lt;li&gt;WEBP is an image format type developed by Google to create files that are smaller for the same quality of JPEG, PNG, and GIF image formats.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now that we know what image formats are commonly used for web images, now we can talk about what the different file types are intended for. Here's a table to illustrate the colour modes, compression algorithm supported, and thus the intended usage for different image file types.&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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1637037980630%2FhNf4rc5cx.jpeg" 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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1637037980630%2FhNf4rc5cx.jpeg" alt="Intro to SVG_Page_14.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To add on to the table, here are reasons why you should use SVG.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why use SVG?
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;SVG is &lt;strong&gt;scalable&lt;/strong&gt;

&lt;ol&gt;
&lt;li&gt;You can stretch it however much, you still won't lose quality because of resolution issues.&lt;/li&gt;
&lt;li&gt;Responsive design is easier to be achieved!&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;SVG can be coded &lt;strong&gt;inline&lt;/strong&gt; 

&lt;ul&gt;
&lt;li&gt;this reduces the HTTP requests required to retrieve media.&lt;/li&gt;
&lt;li&gt;this also meant that the FOUC (Flash of Unstyled Content) problem is less likely to happen from media not being retrieved and styled before rendering in the page.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Developers can work with the individual nodes in the SVG to:

&lt;ol&gt;
&lt;li&gt;animate&lt;/li&gt;
&lt;li&gt;optimize for performance&lt;/li&gt;
&lt;li&gt;optimize for accessibility&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;Animation and optimization are considered a little more advanced so they are not covered in this introductory article. I may conduct more talks/ write articles on these concepts in the future. 😊&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now that we have a clearer picture of image file types and why we should use SVG, we can move on to more hands-on stuff! 🙌&lt;/p&gt;


&lt;h2&gt;
  
  
  How to create SVG?
&lt;/h2&gt;

&lt;p&gt;In this article, I will show you 2 different ways of creating SVG. The first would be using code to create SVG XML and the other would be to use Figma, a design tool to create SVG. &lt;/p&gt;
&lt;h2&gt;
  
  
  Create SVG with Code
&lt;/h2&gt;

&lt;p&gt;For learning to create SVG with code, we will &lt;strong&gt;create 4 simple country flags from SEA&lt;/strong&gt; (Southeast Asia) - &lt;em&gt;Japan, Thailand, Vietnam and Singapore&lt;/em&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Country&lt;/th&gt;
&lt;th&gt;JP&lt;/th&gt;
&lt;th&gt;TH&lt;/th&gt;
&lt;th&gt;VN&lt;/th&gt;
&lt;th&gt;SG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Image&lt;/td&gt;
&lt;td&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fupload.wikimedia.org%2Fwikipedia%2Fcommons%2Fthumb%2F1%2F1b%2FFlag_of_Japan_%25281870%25E2%2580%25931999%2529.svg%2F220px-Flag_of_Japan_%25281870%25E2%2580%25931999%2529.svg.png"&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fupload.wikimedia.org%2Fwikipedia%2Fcommons%2Fa%2Fa9%2FFlag_of_Thailand.svg"&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fupload.wikimedia.org%2Fwikipedia%2Fcommons%2Fthumb%2F2%2F21%2FFlag_of_Vietnam.svg%2F2000px-Flag_of_Vietnam.svg.png"&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fupload.wikimedia.org%2Fwikipedia%2Fcommons%2Fthumb%2F4%2F48%2FFlag_of_Singapore.svg%2F2560px-Flag_of_Singapore.svg.png"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;What you will learn&lt;/td&gt;
&lt;td&gt;Rectangle &amp;amp; Circle&lt;/td&gt;
&lt;td&gt;Positioning&lt;/td&gt;
&lt;td&gt;Polygon, Polyline, Nesting of SVG&lt;/td&gt;
&lt;td&gt;Reusing SVG elements&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  ⛳ 1st Flag: Japan
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1637041966658%2FlopW84pcW.jpeg" 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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1637041966658%2FlopW84pcW.jpeg" alt="Slide on JP Flag"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The flag has 2 colors and 2 shapes. The color of SVG elements are indicated by the &lt;code&gt;fill&lt;/code&gt; property. The shapes are created by the following HTML tags.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;rect /&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;circle /&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the rectangle, the width and height properties are pretty self-explanatory. &lt;/p&gt;

&lt;p&gt;For the circle, &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;(cx, cy)&lt;/code&gt; are x,y coordinates of the centre point of the circle&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;r&lt;/code&gt; represents the radius of the circle&lt;/li&gt;
&lt;li&gt;Hence to make the circle positioned in the center, we set &lt;code&gt;(cx, cy)&lt;/code&gt; to be both a &lt;em&gt;relative&lt;/em&gt; 50%. If you prefer &lt;em&gt;absolute units&lt;/em&gt;, you could set the rect's width=300 and circle's cx=150 instead.
&lt;/li&gt;
&lt;/ul&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;svg&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;rect&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"100%"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"100%"&lt;/span&gt; &lt;span class="na"&gt;fill=&lt;/span&gt;&lt;span class="s"&gt;"white"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;circle&lt;/span&gt; &lt;span class="na"&gt;cx=&lt;/span&gt;&lt;span class="s"&gt;"50%"&lt;/span&gt; &lt;span class="na"&gt;cy=&lt;/span&gt;&lt;span class="s"&gt;"50%"&lt;/span&gt; &lt;span class="na"&gt;r=&lt;/span&gt;&lt;span class="s"&gt;"60"&lt;/span&gt; &lt;span class="na"&gt;fill=&lt;/span&gt;&lt;span class="s"&gt;"#BC002D"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ⛳ 2nd Flag: Thailand
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1637038738073%2FLYxYYiPAP.jpeg" 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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1637038738073%2FLYxYYiPAP.jpeg" alt="Slide on TH Flag"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This simple example has only 5 rectangles, and is meant to introduce you to &lt;strong&gt;positioning&lt;/strong&gt;. Previously we have the circle centered in the middle of the rectangle by setting the &lt;code&gt;(cx, cy)&lt;/code&gt; coordinates. For other elements, usually you can set the &lt;code&gt;(x, y)&lt;/code&gt; coordinates directly. These &lt;code&gt;(x, y)&lt;/code&gt; coordinates always refer to the &lt;strong&gt;top left coordinate&lt;/strong&gt; of the element&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;svg&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;rect&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"100%"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"20%"&lt;/span&gt; &lt;span class="na"&gt;y=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt; &lt;span class="na"&gt;fill=&lt;/span&gt;&lt;span class="s"&gt;"red"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;rect&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"100%"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"20%"&lt;/span&gt; &lt;span class="na"&gt;y=&lt;/span&gt;&lt;span class="s"&gt;"20%"&lt;/span&gt; &lt;span class="na"&gt;fill=&lt;/span&gt;&lt;span class="s"&gt;"white"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;rect&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"100%"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"20%"&lt;/span&gt; &lt;span class="na"&gt;y=&lt;/span&gt;&lt;span class="s"&gt;"40%"&lt;/span&gt; &lt;span class="na"&gt;fill=&lt;/span&gt;&lt;span class="s"&gt;"blue"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;rect&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"100%"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"20%"&lt;/span&gt; &lt;span class="na"&gt;y=&lt;/span&gt;&lt;span class="s"&gt;"60%"&lt;/span&gt; &lt;span class="na"&gt;fill=&lt;/span&gt;&lt;span class="s"&gt;"white"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;rect&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"100%"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"20%"&lt;/span&gt; &lt;span class="na"&gt;y=&lt;/span&gt;&lt;span class="s"&gt;"80%"&lt;/span&gt; &lt;span class="na"&gt;fill=&lt;/span&gt;&lt;span class="s"&gt;"red"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  ⛳ 3rd Flag: Vietnam
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1637038742204%2FQUUv0b2aQ.jpeg" 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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1637038742204%2FQUUv0b2aQ.jpeg" alt="Slide on VN Flag"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are 2 shapes here - a rectangle and a star. There isn't a &lt;code&gt;&amp;lt;star /&amp;gt;&lt;/code&gt; element unfortunately, but there is a generic &lt;code&gt;&amp;lt;polygon /&amp;gt;&lt;/code&gt; element that we can use to create a star. W3C provides &lt;a href="https://www.w3schools.com/graphics/svg_examples.asp" rel="noopener noreferrer"&gt;many examples of shapes&lt;/a&gt;, so I will take their star example and use it here.&lt;/p&gt;

&lt;p&gt;Example of creating a star with the polygon element&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;svg&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;polygon&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"star"&lt;/span&gt; &lt;span class="na"&gt;fill=&lt;/span&gt;&lt;span class="s"&gt;"yellow"&lt;/span&gt;
      &lt;span class="na"&gt;points=&lt;/span&gt;&lt;span class="s"&gt;"100,10 40,198 190,78 10,78 160,198"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, if we try to preview the svg with this star, it will get cut off. &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%2Fraw.githubusercontent.com%2Flyqht%2Fintro-to-svg-slides%2Fmain%2Fpublic%2Fsvg%2Fshapes%2Fstar-cutoff.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%2Fraw.githubusercontent.com%2Flyqht%2Fintro-to-svg-slides%2Fmain%2Fpublic%2Fsvg%2Fshapes%2Fstar-cutoff.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is because the star is bigger than the SVG's &lt;strong&gt;default width &amp;amp; height&lt;/strong&gt; of &lt;strong&gt;300×150&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;To fix this, we could adjust the coordinates one by one but that's painful so let's not do that 😅. Instead, let's change the &lt;code&gt;viewbox&lt;/code&gt; property. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠ The &lt;code&gt;viewbox&lt;/code&gt; property is the most important thing you have to know about SVG.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The &lt;code&gt;viewbox&lt;/code&gt; can be declared by giving it a min-x, min-y, width and height. For us to see the star entirely, the height needs to ≥198, since the &lt;em&gt;biggest y-coordinate&lt;/em&gt; we have in the SVG is 198. Let's also give the star a little padding, so we will increase both the width and height of the SVG viewbox.&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;svg&lt;/span&gt; &lt;span class="na"&gt;viewbox=&lt;/span&gt;&lt;span class="s"&gt;'0 0 300 300'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;polygon&lt;/span&gt; &lt;span class="na"&gt;fill=&lt;/span&gt;&lt;span class="s"&gt;"yellow"&lt;/span&gt; &lt;span class="na"&gt;points=&lt;/span&gt;&lt;span class="s"&gt;"100,10 40,198 190,78 10,78 160,198"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can see the star&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%2Fraw.githubusercontent.com%2Flyqht%2Fintro-to-svg-slides%2Fmain%2Fpublic%2Fsvg%2Fshapes%2Fstar.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%2Fraw.githubusercontent.com%2Flyqht%2Fintro-to-svg-slides%2Fmain%2Fpublic%2Fsvg%2Fshapes%2Fstar.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;For a more in-depth explanation on scaling SVG, you can refer to this nifty &lt;a href="https://css-tricks.com/scale-svg/" rel="noopener noreferrer"&gt;article by CSS-Tricks&lt;/a&gt;. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's move on to create the actual VN flag.&lt;/p&gt;

&lt;p&gt;In the flag, the star actually looks a lot smaller than what we have created just now with the padding. We can modify the &lt;code&gt;viewbox&lt;/code&gt; of this star so that it looks smaller, but at the same time, we want the flag to still look like its original shape of a small rectangle that follows the default &lt;code&gt;viewbox&lt;/code&gt; of 300x150.&lt;/p&gt;

&lt;p&gt;There're different ways of to implement the flag, but here, I will introduce you to a new concept - &lt;strong&gt;Nesting of SVG elements.&lt;/strong&gt; We can enclose the SVG element for the star within another SVG element tag. That way, we can achieve the scaling down of the star element while keeping the flag's size. We will also add some &lt;code&gt;(x, y)&lt;/code&gt; offsets so that the star looks centered.&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;svg&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;rect&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"100%"&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"100%"&lt;/span&gt; &lt;span class="na"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"red"&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;svg&lt;/span&gt; &lt;span class="na"&gt;viewBox&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"100 0 600 600"&lt;/span&gt; &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"25%"&lt;/span&gt; &lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"25%"&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;polygon&lt;/span&gt; &lt;span class="na"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"yellow"&lt;/span&gt;
      &lt;span class="na"&gt;points&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"100,10 40,198 190,78 10,78 160,198"&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;svg&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;svg&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;Now that you understand how the &lt;code&gt;&amp;lt;polygon /&amp;gt;&lt;/code&gt; element works, you can also learn about the &lt;code&gt;&amp;lt;polyline /&amp;gt;&lt;/code&gt; element.&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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1637050449117%2FJpe1Egmfp.jpeg" 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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1637050449117%2FJpe1Egmfp.jpeg" alt="Intro to SVG_Page_21.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The only difference polygon and polyline is that the polygon is a &lt;strong&gt;closed path&lt;/strong&gt; while the polyline is an &lt;strong&gt;open path&lt;/strong&gt; - that's why if you draw the 2 shapes in SVG and give them stroke colors, you can tell the difference. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In &lt;code&gt;&amp;lt;polyline /&amp;gt;&lt;/code&gt;, the last point is not connected to the first point&lt;/li&gt;
&lt;li&gt;In &lt;code&gt;&amp;lt;polygon /&amp;gt;&lt;/code&gt;, the last point is connected to the first point&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  ⛳ 4th Flag: SG
&lt;/h3&gt;

&lt;p&gt;For the 4th country flag, we are creating my home nation, Singapore! 😊&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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1637038744584%2FpOqTH2pCO.jpeg" 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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1637038744584%2FpOqTH2pCO.jpeg" alt="Slide on SG Flag"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The flag comprises of 2 rectangles, 5 stars and 1 moon. To create this flag, it requires the combination of concepts that we have learnt from the previous flags.&lt;/p&gt;

&lt;p&gt;But how do we draw a moon? Well, when it comes to art, we don't always have to follow the semantic way of creating things. We can just create 2 overlapping circles where the top circle is red and the bottom circle is white, so that the result looks like a moon 🌘&lt;/p&gt;

&lt;p&gt;Since we have already created a star before, let's reuse it and just change the fill to white. To reuse an element, we can declare &lt;strong&gt;definitions&lt;/strong&gt; in a &lt;code&gt;&amp;lt;defs&amp;gt;&lt;/code&gt; tag and &lt;strong&gt;use&lt;/strong&gt; them using a &lt;code&gt;&amp;lt;use&amp;gt;&lt;/code&gt; tag. You can see that this becomes helpful as we reuse the same element multiple times.&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;svg&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;defs&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;polygon&lt;/span&gt; 
            &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"white-star"&lt;/span&gt; 
            &lt;span class="na"&gt;points=&lt;/span&gt;&lt;span class="s"&gt;"100,10 40,198 190,78 10,78 160,198"&lt;/span&gt; 
            &lt;span class="na"&gt;fill=&lt;/span&gt;&lt;span class="s"&gt;"white"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/defs&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;rect&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"100%"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"50%"&lt;/span&gt; &lt;span class="na"&gt;fill=&lt;/span&gt;&lt;span class="s"&gt;"red"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;rect&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"100%"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"50%"&lt;/span&gt; &lt;span class="na"&gt;y=&lt;/span&gt;&lt;span class="s"&gt;"50%"&lt;/span&gt; &lt;span class="na"&gt;fill=&lt;/span&gt;&lt;span class="s"&gt;"white"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;circle&lt;/span&gt; &lt;span class="na"&gt;cx=&lt;/span&gt;&lt;span class="s"&gt;'15%'&lt;/span&gt; &lt;span class="na"&gt;cy=&lt;/span&gt;&lt;span class="s"&gt;'25%'&lt;/span&gt; &lt;span class="na"&gt;r=&lt;/span&gt;&lt;span class="s"&gt;'30'&lt;/span&gt; &lt;span class="na"&gt;fill=&lt;/span&gt;&lt;span class="s"&gt;'white'&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;circle&lt;/span&gt; &lt;span class="na"&gt;cx=&lt;/span&gt;&lt;span class="s"&gt;'20%'&lt;/span&gt; &lt;span class="na"&gt;cy=&lt;/span&gt;&lt;span class="s"&gt;'25%'&lt;/span&gt; &lt;span class="na"&gt;r=&lt;/span&gt;&lt;span class="s"&gt;'30'&lt;/span&gt; &lt;span class="na"&gt;fill=&lt;/span&gt;&lt;span class="s"&gt;'red'&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;viewBox=&lt;/span&gt;&lt;span class="s"&gt;"600 -100 1000 1800"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;use&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#white-star"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;use&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#white-star"&lt;/span&gt; &lt;span class="na"&gt;x=&lt;/span&gt;&lt;span class="s"&gt;"-20%"&lt;/span&gt; &lt;span class="na"&gt;y=&lt;/span&gt;&lt;span class="s"&gt;"10%"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;use&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#white-star"&lt;/span&gt; &lt;span class="na"&gt;x=&lt;/span&gt;&lt;span class="s"&gt;"20%"&lt;/span&gt; &lt;span class="na"&gt;y=&lt;/span&gt;&lt;span class="s"&gt;"10%"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;use&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#white-star"&lt;/span&gt; &lt;span class="na"&gt;x=&lt;/span&gt;&lt;span class="s"&gt;"-12.5%"&lt;/span&gt; &lt;span class="na"&gt;y=&lt;/span&gt;&lt;span class="s"&gt;"22%"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;use&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#white-star"&lt;/span&gt; &lt;span class="na"&gt;x=&lt;/span&gt;&lt;span class="s"&gt;"12.5%"&lt;/span&gt; &lt;span class="na"&gt;y=&lt;/span&gt;&lt;span class="s"&gt;"22%"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;🎉 Good job reaching here thus far!&lt;/p&gt;

&lt;p&gt;🥳 We managed to create SVGs of &lt;strong&gt;4 different SEA country flags using code!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;👉 You can find all the &lt;code&gt;.svg&lt;/code&gt; files covered &lt;a href="https://github.com/lyqht/intro-to-svg-slides/tree/main/public/svg/flags" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For the rest of the article, I promise you the content would be much &lt;em&gt;lighter&lt;/em&gt;! 😊 Now, let's move on to creating SVG graphics using &lt;strong&gt;Design Tools&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  🎨 Create SVG with Design Tools
&lt;/h2&gt;

&lt;p&gt;Common Design Tools used to create SVG include&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://www.figma.com" rel="noopener noreferrer"&gt;Figma&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.sketch.com/" rel="noopener noreferrer"&gt;Sketch&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.adobe.com/sg/products/illustrator.html" rel="noopener noreferrer"&gt;Illustrator&lt;/a&gt; &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Out of the 3 options, Figma is free and also accessible on any platform, so I will share with you a very short intro on Figma on creating graphics using &lt;strong&gt;Paths,&lt;/strong&gt; and &lt;strong&gt;exporting them as SVG&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;Duplicate the Figma demo file &lt;a href="https://www.figma.com/community/file/1042361058690524745/intro-to-svg-shapes" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/strong&gt;. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⭐ Pro tip: To edit any path, click on a path and press the 'Enter' key.&lt;br&gt;
Try playing around with the paths that are in the file!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Straight Paths
&lt;/h3&gt;

&lt;p&gt;Using the &lt;strong&gt;Pen Tool (P key) 🖊&lt;/strong&gt;, you can easily create straight paths and form polygons, polylines and shapes like the low-poly bear!&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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1637042456734%2FPPAXvKG_S.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1637042456734%2FPPAXvKG_S.gif" alt="Figma-Pentool-demo.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Curved Paths
&lt;/h3&gt;

&lt;p&gt;After creating straight paths with the Pen Tool, you can use the &lt;strong&gt;Bend Tool (Ctrl Key)&lt;/strong&gt; to create a &lt;strong&gt;Bezier Curve&lt;/strong&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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1637042292395%2F-Lg_hGg97.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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1637042292395%2F-Lg_hGg97.png" alt="Bezier Curve Handles On Figma"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Handles will appear, connected to the start and end coordinate of the path that you have selected. This is how shapes like the bunny's round head &amp;amp; ears are created!&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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1637042606762%2FHtxhCtyTq.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1637042606762%2FHtxhCtyTq.gif" alt="Figma-BendTool-demo.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Another type of curved Path would be the &lt;strong&gt;Arc&lt;/strong&gt;. After creating an Ellipse, you can hover over the ellipse path to use the &lt;strong&gt;Arc Tool&lt;/strong&gt; to create an Arc. &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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1637042177526%2FXmtlqpiaA.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1637042177526%2FXmtlqpiaA.gif" alt="Figma_Arc_Demo.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Exporting the SVG
&lt;/h3&gt;

&lt;p&gt;Once you're done creating your fancy shapes and graphics, you can choose to export them easily as a SVG file.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1637051786561%2FMiG6y2CE4.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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1637051786561%2FMiG6y2CE4.png" alt="Exporting SVG"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;That sums up how you can create SVG from scratch either using Code or Design Tools! Personally I would recommend you to create graphics using Design Tools because having GUI just makes it so much easier to &lt;strong&gt;visualize what you're actually doing&lt;/strong&gt;. The downside of it is that you have less control over the result SVG element nodes inside your graphic. This may pose a problem if you try to do animation or optimization later on, but if you're just doing &lt;strong&gt;static&lt;/strong&gt; SVG, it's much easier to just use a design tool to create SVG. &lt;/p&gt;

&lt;p&gt;Of course, if you don't want to create SVG from scratch at all, there are plenty of online tools to generate SVG of common shapes for us!&lt;/p&gt;




&lt;h2&gt;
  
  
  Generate SVG
&lt;/h2&gt;

&lt;p&gt;🔖 There's an &lt;a href="https://www.smashingmagazine.com/2021/03/svg-generators" rel="noopener noreferrer"&gt;extensive list of SVG generators by Smashing Magazine&lt;/a&gt; that you can check out.&lt;/p&gt;

&lt;p&gt;During the talk, I have shown a demo for just two generators that were listed there because I found them relevant to what I've been reading recently.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://squircley.app/" rel="noopener noreferrer"&gt;Squicley&lt;/a&gt; → provides configuration for you to generate SVG squircles, a shape that is similar to rounded circles but not identical. Thought this was a pretty funny concept of shape to introduce to you all 😄 Apparently, &lt;a href="https://99percentinvisible.org/article/circling-square-designing-squircles-instead-rounded-rectangles/" rel="noopener noreferrer"&gt;this is what makes Apple hardware more distinct than others&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2F99percentinvisible.org%2Fapp%2Fuploads%2F2017%2F10%2Fnot-apple-728x347.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%2F99percentinvisible.org%2Fapp%2Fuploads%2F2017%2F10%2Fnot-apple-728x347.png" alt="https://99percentinvisible.org/app/uploads/2017/10/not-apple-728x347.png"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="http://SVGBackgrounds.com" rel="noopener noreferrer"&gt;SVGBackgrounds&lt;/a&gt; → provides you many SVG backgrounds you can customize to be used for your next project. I also previously made a Tweet thread on how you can use it and why you should use it.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

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



&lt;/p&gt;

&lt;p&gt;Aside from generating SVG, we can also extract SVG that we see on websites using the Inspector ✨&lt;/p&gt;




&lt;h3&gt;
  
  
  Extract SVG from websites
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Important Note&lt;/strong&gt;: Please check if you can use the extracted SVG without attribution.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For extracting SVG from websites, we will use our good old &lt;a href="https://esteetey.dev/navigate-the-frontend-easily-with-the-inspector" rel="noopener noreferrer"&gt;Element Inspector&lt;/a&gt; to do it! Let's try it out on &lt;a href="https://hacktoberfest.digitalocean.com/" rel="noopener noreferrer"&gt;Hacktoberfest's website&lt;/a&gt; since they have really beautiful flowers this year 🌺&lt;/p&gt;

&lt;p&gt;&lt;a href="https://cdn.hashnode.com/res/hashnode/image/upload/v1637061717448/lr_8B12KI.gif" rel="noopener noreferrer"&gt;Hacktoberfest-Inspector-Demo GIF&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Ctrl+F &lt;code&gt;"&amp;lt;svg&amp;gt;"&lt;/code&gt; to find the SVG element that you want. &lt;/li&gt;
&lt;li&gt;Once the highlighted area matches the SVG that you're targeting, right click the element and choose "Copy Element"&lt;/li&gt;
&lt;li&gt;The result will be something like this, which you can paste in a new &lt;code&gt;.svg&lt;/code&gt; file.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Flyqht%2Fintro-to-svg-slides%2Fmain%2Fpublic%2Fsvg%2Fhacktoberfest.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Flyqht%2Fintro-to-svg-slides%2Fmain%2Fpublic%2Fsvg%2Fhacktoberfest.svg" alt="Hacktoberfest flowers svg"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🌟 Bonus Helpful Tools for SVG
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Optimization of SVG file size: &lt;a href="https://jakearchibald.github.io/svgomg/" rel="noopener noreferrer"&gt;SVGOMG&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you use design tools to create SVG or extract SVG from websites, chances are that most of them are not optimized in file sizes.&lt;/li&gt;
&lt;li&gt;There's a handy tool called &lt;a href="https://jakearchibald.github.io/svgomg/" rel="noopener noreferrer"&gt;SVGOMG&lt;/a&gt; which has many features you can toggle to help you optimize your file size, and you don't have to understand how all of them work.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You can also preview the result and the result file size at the circled area ✨&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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1637042863065%2FyHOCa9jBO.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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1637042863065%2FyHOCa9jBO.png" alt="Extract-SVG-From-Hacktoberfest-Website-Demo.png"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Visual Studio Code Extension: &lt;a href="https://marketplace.visualstudio.com/items?itemName=jock.svg" rel="noopener noreferrer"&gt;SVG&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Provides auto-completion, SVG Live Preview and Export PNG&lt;/li&gt;
&lt;li&gt;You can also minify the SVG code and remove unnecessary code
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;For React Devs: &lt;a href="https://svg2jsx.com/" rel="noopener noreferrer"&gt;Convert SVG to JSX&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can paste SVG code here to be converted to either functional or class component syntax.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  Frequently asked questions (FAQ)
&lt;/h2&gt;

&lt;p&gt;Q) I see &lt;code&gt;xmlns&lt;/code&gt; attributes are commonly used on SVG elements, but you didn't use them in your talk at all, are they actually needed?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg"&amp;gt;&amp;lt;/svg&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A) If you are using them as inline SVG, you don't need to include those. You only need those attributes if you are embedding them using the &lt;strong&gt;img element&lt;/strong&gt;. Here's a useful &lt;a href="https://stackoverflow.com/questions/18467982/are-svg-parameters-such-as-xmlns-and-version-needed" rel="noopener noreferrer"&gt;StackOverflow thread&lt;/a&gt; that answers this.&lt;/p&gt;




&lt;h2&gt;
  
  
  Exploring further
&lt;/h2&gt;

&lt;p&gt;As you try to experiment with SVG in code further by using JavaScript and so on, you will start to realize that creating graphics in code requires &lt;strong&gt;a lot of math&lt;/strong&gt; 😂&lt;/p&gt;

&lt;p&gt;Here are some nice tutorials for you to try out to create more complex SVG that include good explanations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.aleksandrhovhannisyan.com/blog/svg-tutorial-how-to-code-svg-icons-by-hand/" rel="noopener noreferrer"&gt;SVG Tutorial: How to Code SVG Icons by Hand&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/thormeier/how-to-simply-create-beautiful-rosette-patterns-with-javascript-and-svg-3h4i"&gt;Create beautiful rosette patterns with JavaScript 🖌️🌼&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://uxdesign.cc/star-rating-make-svg-great-again-d4ce4731347e" rel="noopener noreferrer"&gt;Star Rating — Make SVG Great Again&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  That's a wrap folks! 🎉
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fc.tenor.com%2FeoM1uCVuXtkAAAAM%2Fyay-excited.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fc.tenor.com%2FeoM1uCVuXtkAAAAM%2Fyay-excited.gif" alt="birds excited"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thank you for reading, hope you enjoyed the article! If you have any questions or feedback for me, please leave them below, I'll attend to them shortly 😊&lt;/p&gt;

&lt;p&gt;If you find the article awesome, hit the &lt;em&gt;reactions&lt;/em&gt; 🧡 and &lt;em&gt;share&lt;/em&gt; it 🐦~&lt;/p&gt;

&lt;p&gt;To stay updated whenever I post new stuff, follow me on &lt;a href="https://twitter.com/estee_tey" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>tutorial</category>
      <category>svg</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
