<?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>Forem: Joshua Tzucker</title>
    <description>The latest articles on Forem by Joshua Tzucker (@joshuatz).</description>
    <link>https://forem.com/joshuatz</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%2F208595%2Fbef0af2e-3441-4ca5-8745-ad26eae5c625.png</url>
      <title>Forem: Joshua Tzucker</title>
      <link>https://forem.com/joshuatz</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/joshuatz"/>
    <language>en</language>
    <item>
      <title>Git SHA Badges - Know Which Commit is Live</title>
      <dc:creator>Joshua Tzucker</dc:creator>
      <pubDate>Mon, 14 Dec 2020 17:47:58 +0000</pubDate>
      <link>https://forem.com/joshuatz/git-sha-badges-which-commit-is-live-5ada</link>
      <guid>https://forem.com/joshuatz/git-sha-badges-which-commit-is-live-5ada</guid>
      <description>&lt;h2&gt;
  
  
  Demo Repo
&lt;/h2&gt;

&lt;p&gt;I've taken the approaches covered in this post and created a fully-functional demo repo to demonstrate them in action:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;💾 &lt;a href="https://github.com/joshuatz/git-sha-badges"&gt;github.com/joshuatz/git-sha-badges&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;When building a project that lives in a Git repository and auto-deploys elsewhere, a common concern is how to be 100% absolutely sure that the live "deployed" version is up-to-date. If I go to the live, production application, how can I trust that what I am seeing is the latest pushed code in my repository?&lt;/p&gt;

&lt;p&gt;Just because my CI/CD pipeline passed does not &lt;em&gt;necessarily&lt;/em&gt; mean that the latest commit is live yet. Furthermore, if something goes bad, I want to be able to know at a glance &lt;strong&gt;&lt;em&gt;exactly&lt;/em&gt;&lt;/strong&gt; what code is live in production. A simple "pass / fail" badge cannot tell me that.&lt;/p&gt;

&lt;h3&gt;
  
  
  Close, but Not Good Enough
&lt;/h3&gt;

&lt;p&gt;Many automated deploy systems provide a build status badge, but this doesn't tell us much:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Standard Netlify Status Badge:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ADIHwbn6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://api.netlify.com/api/v1/badges/4b298306-7d69-4214-b790-b37a56e59ec4/deploy-status" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ADIHwbn6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://api.netlify.com/api/v1/badges/4b298306-7d69-4214-b790-b37a56e59ec4/deploy-status" alt="Netlify Status"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Many apps also display in the deployed UI the current version string of the app (e.g. &lt;code&gt;1.8.2-beta.1.10&lt;/code&gt;), but this actually &lt;strong&gt;&lt;em&gt;still&lt;/em&gt;&lt;/strong&gt; leaves a lot of room for ambiguity; what if a developer used &lt;code&gt;--force&lt;/code&gt; to override the head of the main branch? What if there have been commits added to the main branch, but no new release authored? Etc.&lt;/p&gt;

&lt;p&gt;Instead of a version string, a nice alternative (or better yet, an addition) is to use the actual SHA of the latest commit as a status indicator. This makes it very easy to compare against the latest commit in version control, and verify that the correct set of changes are live.&lt;/p&gt;

&lt;p&gt;Furthermore, no two commits can have the same SHA, even if &lt;code&gt;--force&lt;/code&gt; has been used, so if I know the SHA that is "live", I know &lt;strong&gt;&lt;em&gt;exactly&lt;/em&gt;&lt;/strong&gt; what code is running, and can even quickly "checkout" that exact version of code in my local environment.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 Similar to how you can use &lt;code&gt;git checkout {branch_name}&lt;/code&gt;, you can also use &lt;code&gt;git checkout {SHA}&lt;/code&gt; to checkout your entire repo at that commit. It kind of feels like time travelling!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Methods for Creating SHA Badges
&lt;/h2&gt;

&lt;p&gt;The general concept that we are going to employ is that our build step, which runs on every deploy, is now going to save the current SHA of the branch (aka the &lt;em&gt;tip&lt;/em&gt;) in an accessible format that can be used for visual indicators.&lt;/p&gt;

&lt;p&gt;Two easy approaches that build off this concept, and which I'll explore here, are JSON files and raw SVG files.&lt;/p&gt;

&lt;h3&gt;
  
  
  JSON File
&lt;/h3&gt;

&lt;p&gt;The basic idea with the JSON file approach is that, at build time, we stash the value of the latest SHA in a shared JSON file. This makes it easy to reuse across our codebase, possibly even at runtime, and in a widely compatible format.&lt;/p&gt;

&lt;p&gt;The JSON file approach is my favorite, for a couple of reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It is easier than dealing with SVG string building, but can be combined with that approach easily (more details further below)&lt;/li&gt;
&lt;li&gt;The JSON file can be re-used, and even imported into runtime code (assuming they share access)!&lt;/li&gt;
&lt;li&gt;Usable by APIs and 3rd party services (including Shields.io for badges)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  JSON File - Getting and Saving the SHA
&lt;/h4&gt;

&lt;p&gt;The implementation of this task could be accomplished in an endless number of ways, and the best option probably depends on the specifics of your build environment, project type, and operating system.&lt;/p&gt;

&lt;p&gt;For a generic example, here is how I how I have integrated this approach into a NodeJS based project, within a &lt;code&gt;package.json&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"build-badge"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"$NETLIFY &amp;amp;&amp;amp; git rev-parse --short HEAD | xargs -I % printf '{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;sha&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;%&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}' &amp;gt; ./static/build-info.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"(yarn run build-badge || true) &amp;amp;&amp;amp; gatsby build"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The most important thing about the above command is that it modifies our build command, which used to be just &lt;code&gt;gatsby build&lt;/code&gt;, to include a step that saves the current SHA to a JSON file (&lt;code&gt;./static/build-info.json&lt;/code&gt;). If you wanted to, you could replace a lot of the bash stuff with a NodeJS or python script that does the same thing.&lt;/p&gt;

&lt;p&gt;If you want a breakdown of the above command, you can view it below&lt;/p&gt;

&lt;p&gt;
  Show / Hide Details
  &lt;p&gt;To break this down further, the &lt;code&gt;build-badge&lt;/code&gt; command is composed of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;$NETLIFY&lt;/code&gt; is an environmental variable that &lt;em&gt;only&lt;/em&gt; exists within my build environment (&lt;a href="https://docs.netlify.com/configure-builds/environment-variables/"&gt;Netlify servers&lt;/a&gt;). 

&lt;ul&gt;
&lt;li&gt;You could use any value that evaluates to true in your build system&lt;/li&gt;
&lt;li&gt;Or, if you always want this file generated regardless of local vs production, you could omit the whole &lt;code&gt;$VARIABLE &amp;amp;&amp;amp;&lt;/code&gt; part&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git rev-parse --short HEAD&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;This is a git command to get the &lt;em&gt;short&lt;/em&gt; version of the last commit SHA&lt;/li&gt;
&lt;li&gt;You could use the full SHA instead; just omit &lt;code&gt;--short&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;If you are looking for a lot of nifty git commands like this one, here is a shameless plug to check out &lt;a href="https://docs.joshuatz.com/cheatsheets/git/"&gt;my Git Cheat Sheet&lt;/a&gt; (this command is on it!)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;xargs -I % printf '{\"sha\":\"%\"}'&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;Not going to get too much into this, but it is a fancy way of passing the SHA from the last section into a stringified JSON representation&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;gt; ./static/build-info.json&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;Save the generated JSON string to a file. This could be any file, and it doesn't even necessarily need the &lt;code&gt;.json&lt;/code&gt; extension&lt;/li&gt;
&lt;li&gt;You should make sure that whatever directory you save it to is going to be accessible after the build is complete, either internally and/or externally (hosted)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And I've changed my build command from &lt;code&gt;gatsby build&lt;/code&gt; to &lt;code&gt;(yarn run build-badge || true) &amp;amp;&amp;amp; gatsby build&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This is an easy way to make sure that &lt;code&gt;gatsby build&lt;/code&gt;, the true build command, always runs regardless of the success or failure of the &lt;code&gt;build-badge&lt;/code&gt; command.&lt;/li&gt;
&lt;/ul&gt;



&lt;/p&gt;

&lt;p&gt;Again, as an alternative to a long bash command you could alternatively extract and save the SHA entirely in your favorite scripting language of choice (NodeJS, Python, etc.).&lt;/p&gt;

&lt;h4&gt;
  
  
  JSON File - Using the Stored SHA
&lt;/h4&gt;

&lt;p&gt;As-is, once we have our JSON file, that alone is enough to be able to add badges or pull the SHA value into front-end code. For example, we can use &lt;a href="https://shields.io/#dynamic-badge"&gt;the dynamic badge feature of Shields.io&lt;/a&gt; to easily get a SVG URL that displays our SHA. Something like:&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;img src="https://img.shields.io/badge/dynamic/json?color=blue&amp;amp;label=Deployed%20SHA&amp;amp;query=sha&amp;amp;url=https%3A%2F%2Fgit-sha-badges.netlify.app%2Fbuild-info.json" alt="Deployed SHA" /&amp;gt;

Notice the importance of:
 - query=sha
 - url=HOSTED_SITE/build-info.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jRhIzQlr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://img.shields.io/badge/dynamic/json%3Fcolor%3Dblue%26label%3DDeployed%2520SHA%26query%3Dsha%26url%3Dhttps%253A%252F%252Fgit-sha-badges.netlify.app%252Fbuild-info.json" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jRhIzQlr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://img.shields.io/badge/dynamic/json%3Fcolor%3Dblue%26label%3DDeployed%2520SHA%26query%3Dsha%26url%3Dhttps%253A%252F%252Fgit-sha-badges.netlify.app%252Fbuild-info.json" alt="Deployed SHA"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you wanted to pull it into your code, in CommonJS, that is as easy as:&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;buildInfo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;BUILD_DIR&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/build-info.json`&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;buildInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sha&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  SVG File
&lt;/h3&gt;

&lt;p&gt;Rather than displaying your SHA in front-end code by pulling it in via AJAX or inside framework components (e.g. a React component), you could also pre-generate SVG badges at build time, with just a little bit of code.&lt;/p&gt;

&lt;p&gt;The easiest approach is to have 99% of the SVG badge ready-to-go, stored as a string. You can use whatever method you prefer for designing your badge. The main thing to do is leave room for the SHA to be inserted at build time, as a &lt;code&gt;&amp;lt;text&amp;gt;&amp;lt;/text&amp;gt;&lt;/code&gt; element inside the SVG.&lt;/p&gt;

&lt;p&gt;At build time, you combine your SVG template string with the latest SHA (this is where it is handy to have that &lt;code&gt;build-info.json&lt;/code&gt; already generated), and then write out the resulting string to an actual &lt;code&gt;.svg&lt;/code&gt; file, which can be served.&lt;/p&gt;

&lt;p&gt;You can checkout how I put this all together &lt;a href="https://github.com/joshuatz/git-sha-badges/blob/main/build.js"&gt;in &lt;code&gt;build.js&lt;/code&gt; in my demo repo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In pseudo code, this approach might look something like:&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;// Compose full SVG string&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;svgStr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;svg&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;svgTemplateStr&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;text&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;sha&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;/text&amp;gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// Save to SVG&lt;/span&gt;
&lt;span class="nx"&gt;saveFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;svgStr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-badge.svg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And don't forget, you can get as fancy as you want with your generated SVGs 😄&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mcm5UH3F--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://git-sha-badges.netlify.app/badge-fancy.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mcm5UH3F--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://git-sha-badges.netlify.app/badge-fancy.svg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus: Github Commit Badges
&lt;/h2&gt;

&lt;p&gt;If all you want is a badge that shows the current HEAD of a particular branch in Github (i.e. the most recent &lt;em&gt;pushed&lt;/em&gt; commit) - you can actually do this entirely with just Shields.io and the public Github API (no credentials required).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Caveat: this requires that your repo is published as public&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;First, you need get the API endpoint that returns HEAD info, for your specific repo and branch combo. The syntax follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://api.github.com/repos/{USER}/{REPO}/git/refs/heads/{BRANCH}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, for example, to get the currently pushed tip for my &lt;code&gt;main&lt;/code&gt; branch in my demo repo for this post (&lt;code&gt;joshuatz/git-sha-badges&lt;/code&gt;), I can use &lt;a href="https://api.github.com/repos/joshuatz/git-sha-badges/git/refs/heads/main"&gt;api.github.com/repos/joshuatz/git-sha-badges/git/refs/heads/main&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you try that URL, you will get a JSON response, including:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Other&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;stuff&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"object"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"sha"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"____"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"commit"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we can plug that endpoint directly into Shield's amazing &lt;a href="https://shields.io/#dynamic-badge"&gt;dynamic badge&lt;/a&gt; generator, which accepts a JSON endpoint and lets us control badge settings through query string parameters. By tweaking the parameters, we can get an orange badge that displays the &lt;code&gt;object.sha&lt;/code&gt; value in the endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;API Endpoint:
  https://api.github.com/repos/joshuatz/git-sha-badges/git/refs/heads/main

Shield Badge Base Endpoint
  https://img.shields.io/badge/dynamic/json

Shield Badge URL (raw)
  https://img.shields.io/badge/dynamic/json?color=orange&amp;amp;label=Github SHA&amp;amp;query=object.sha&amp;amp;url=https://api.github.com/repos/joshuatz/git-sha-badges/git/refs/heads/main

Same URL, encoded:
  https://img.shields.io/badge/dynamic/json?color=orange&amp;amp;label=Github%20SHA&amp;amp;query=object.sha&amp;amp;url=https%3A%2F%2Fapi.github.com%2Frepos%2Fjoshuatz%2Fgit-sha-badges%2Fgit%2Frefs%2Fheads%2Fmain

---

Final badge code

---

Markdown:
  ![Github SHA](https://img.shields.io/badge/dynamic/json?color=orange&amp;amp;label=Github%20SHA&amp;amp;query=object.sha&amp;amp;url=https%3A%2F%2Fapi.github.com%2Frepos%2Fjoshuatz%2Fgit-sha-badges%2Fgit%2Frefs%2Fheads%2Fmain)

HTML
  &amp;lt;img src="https://img.shields.io/badge/dynamic/json?color=orange&amp;amp;label=Github%20SHA&amp;amp;query=object.sha&amp;amp;url=https%3A%2F%2Fapi.github.com%2Frepos%2Fjoshuatz%2Fgit-sha-badges%2Fgit%2Frefs%2Fheads%2Fmain" alt="Github SHA" /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And, here it is (this is a live badge!):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ey3mEVyW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://img.shields.io/badge/dynamic/json%3Fcolor%3Dorange%26label%3DGithub%2520SHA%26query%3Dobject.sha%26url%3Dhttps%253A%252F%252Fapi.github.com%252Frepos%252Fjoshuatz%252Fgit-sha-badges%252Fgit%252Frefs%252Fheads%252Fmain" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ey3mEVyW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://img.shields.io/badge/dynamic/json%3Fcolor%3Dorange%26label%3DGithub%2520SHA%26query%3Dobject.sha%26url%3Dhttps%253A%252F%252Fapi.github.com%252Frepos%252Fjoshuatz%252Fgit-sha-badges%252Fgit%252Frefs%252Fheads%252Fmain" alt="Github SHA"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap Up
&lt;/h2&gt;

&lt;p&gt;I want to wrap up this post by pointing out that, although I gave some specific examples and code samples, the approaches throughout this post are fairly generic in nature and could be extended to solutions beyond just adding Git SHA badges.&lt;/p&gt;

&lt;p&gt;For example, if you maintain several build and deploy targets (e.g. &lt;code&gt;dev&lt;/code&gt;, &lt;code&gt;test&lt;/code&gt;, &lt;code&gt;staging-alpha&lt;/code&gt;, &lt;code&gt;production&lt;/code&gt;, etc.), you could re-use most of the solutions through this post to add visual indicators to each environment that makes it clear what system you are looking at.&lt;/p&gt;

&lt;p&gt;Hope this post helps someone out there! (And kudos to anyone that understands and appreciates what my fancy SVG example is referencing).&lt;/p&gt;

&lt;h2&gt;
  
  
  More About Me (Joshua Tzucker):
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;🔗&lt;a href="https://joshuatz.com/" rel="noopener"&gt;joshuatz.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;💬&lt;a href="https://twitter.com/1joshuatz" rel="noopener"&gt;@1joshuatz&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📘&lt;a href="https://docs.joshuatz.com/" rel="noopener"&gt;docs.joshuatz.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;💾&lt;a href="https://github.com/joshuatz" rel="noopener"&gt;github.com/joshuatz&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devops</category>
      <category>webdev</category>
      <category>github</category>
      <category>git</category>
    </item>
    <item>
      <title>Generate a Perfectly-Sized Cover Image with Cloudinary</title>
      <dc:creator>Joshua Tzucker</dc:creator>
      <pubDate>Thu, 14 May 2020 18:49:38 +0000</pubDate>
      <link>https://forem.com/joshuatz/generate-a-perfectly-sized-cover-image-with-cloudinary-5h35</link>
      <guid>https://forem.com/joshuatz/generate-a-perfectly-sized-cover-image-with-cloudinary-5h35</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;For those unfamiliar with Cloudinary, it is a cloud service that is focused on image serving, with unique features like transformations, responsive generation, and a speedy CDN for delivery. I have no affiliation with Cloudinary, other than being a fan of their service.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;One of the big changes in web development over the years has been a shift from "fixed" designs to "responsive". For example, where we used to define layouts or the display of assets with fixed units, we now try to use percentages or other ratio based approaches to adapt to any display size. This is something to be celebrated, and Cloudinary has &lt;a href="https://cloudinary.com/features/responsive_images" rel="noopener"&gt;an entire feature set built around this&lt;/a&gt;, but &lt;strong&gt;today I want to talk about when the &lt;em&gt;opposite&lt;/em&gt; approach is needed.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We often have "cover images", "hero images", and/or images that we need for social media sharing sites, and these frequently have specific desired pixel sizes or aspect ratios. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dev.to cover image: 1000 x 420 (see &lt;a href="https://dev.to/p/editor_guide" rel="noopener"&gt;&lt;em&gt;Editor Guide&lt;/em&gt;&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Twitter: (depends) 16:9 (1.77:1), 2:1, 1200x628 (1.91:1)&lt;/li&gt;
&lt;li&gt;Facebook feed: 1.91:1&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finding images that perfectly fit these aspect ratios is often difficult and time-consuming, and manually cropping and resizing existing images to fit, equally so.&lt;/p&gt;

&lt;p&gt;What if there was a better way to generate the perfectly sized hero image, with &lt;em&gt;any&lt;/em&gt; image as the input?...&lt;/p&gt;

&lt;h2&gt;
  
  
  Our Goal
&lt;/h2&gt;

&lt;p&gt;So, here is our example goal. We want to produce a 1000 x 420 pixel (2.38:1) cover image for Dev.to, but do so automatically, with a method that accepts any image as the input. If the input aspect ratio does not match the desired output, then the input will be centered, and a blurred background based on the same input shown behind it.&lt;/p&gt;

&lt;p&gt;Sample Input:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RmzYsCHv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/demo/image/upload/sample" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RmzYsCHv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/demo/image/upload/sample" alt="Sample Input"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sample &lt;strong&gt;&lt;em&gt;goal&lt;/em&gt;&lt;/strong&gt; output:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wUXfxa-2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/demo/image/upload/c_fill%2Cw_1000%2Ch_420%2Ce_blur:1500%2Co_75/l_sample%2Cc_limit%2Cw_1000%2Ch_420%2Ce_outline:outer:2:100%2Cfl_no_overflow/sample" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wUXfxa-2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/demo/image/upload/c_fill%2Cw_1000%2Ch_420%2Ce_blur:1500%2Co_75/l_sample%2Cc_limit%2Cw_1000%2Ch_420%2Ce_outline:outer:2:100%2Cfl_no_overflow/sample" alt="Sample Output"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The cover image for this very post was created with the method outlined below! But, you'll see that later.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Building It With Cloudinary
&lt;/h2&gt;

&lt;p&gt;I'm going to walk through how to build this with &lt;a href="https://cloudinary.com/documentation/image_transformations" rel="noopener"&gt;Cloudinary image transformations&lt;/a&gt;, using URL parameters alone. This makes it very easy to use anywhere, with any backend or frontend, since all we need is a URL.&lt;/p&gt;

&lt;p&gt;Base Image: &lt;a href="https://res.cloudinary.com/demo/image/upload/sample"&gt;https://res.cloudinary.com/demo/image/upload/sample&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When you see &lt;strong&gt;&lt;em&gt;Bold and Italic&lt;/em&gt;&lt;/strong&gt; text in the URL, that indicates what was added in the step.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Steps:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Start with the "Base Image": &lt;code&gt;https://res.cloudinary.com/demo/image/upload/&lt;/code&gt;&lt;strong&gt;&lt;em&gt;&lt;code&gt;sample&lt;/code&gt;&lt;/em&gt;&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Added: &lt;strong&gt;&lt;em&gt;&lt;code&gt;sample&lt;/code&gt;&lt;/em&gt;&lt;/strong&gt;
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RmzYsCHv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/demo/image/upload/sample"&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Use image to &lt;em&gt;fill&lt;/em&gt; exact desired size

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;https://res.cloudinary.com/demo/image/upload/&lt;/code&gt;&lt;strong&gt;&lt;em&gt;&lt;code&gt;c_fill,w_1000,h_420/&lt;/code&gt;&lt;/em&gt;&lt;/strong&gt;&lt;code&gt;sample&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Added: &lt;strong&gt;&lt;em&gt;&lt;code&gt;/c_fill,w_1000,h_420/&lt;/code&gt;&lt;/em&gt;&lt;/strong&gt;
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qgyGu_AW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/demo/image/upload/c_fill%2Cw_1000%2Ch_420/sample"&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Since this is actually going to be the background, apply a blur effect and reduce opacity

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;https://res.cloudinary.com/demo/image/upload/c_fill,w_1000,h_420&lt;/code&gt;&lt;strong&gt;&lt;em&gt;&lt;code&gt;,e_blur:1500,o_75&lt;/code&gt;&lt;/em&gt;&lt;/strong&gt;&lt;code&gt;/sample&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Added: &lt;strong&gt;&lt;em&gt;&lt;code&gt;e_blur:1500,o_75&lt;/code&gt;&lt;/em&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Blur &lt;a href="https://cloudinary.com/documentation/image_transformation_reference#effect_parameter" rel="noopener"&gt;is on a scale from 1 to 2000&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Opacity is on a scale from 0 to 100 (100 being completely opaque)
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Knim1TsX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/demo/image/upload/c_fill%2Cw_1000%2Ch_420%2Ce_blur:1500%2Co_75/sample"&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Overlay the same image, but &lt;strong&gt;&lt;em&gt;limit&lt;/em&gt;&lt;/strong&gt; its size to the "canvas"

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;https://res.cloudinary.com/demo/image/upload/c_fill,w_1000,h_420,e_blur:1500,o_75&lt;/code&gt;&lt;strong&gt;&lt;em&gt;&lt;code&gt;/l_sample,c_limit,w_1000,h_420/&lt;/code&gt;&lt;/em&gt;&lt;/strong&gt;&lt;code&gt;sample&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Added: &lt;strong&gt;&lt;em&gt;&lt;code&gt;/l_sample,c_limit,w_1000,h_420/&lt;/code&gt;&lt;/em&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Notice how &lt;code&gt;l_{uploadedId}&lt;/code&gt; is used to generate an &lt;a href="https://cloudinary.com/documentation/image_transformations#adding_image_overlays" rel="noopener"&gt;&lt;em&gt;image overlay&lt;/em&gt; layer&lt;/a&gt;.
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--a6-aU7aD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/demo/image/upload/c_fill%2Cw_1000%2Ch_420%2Ce_blur:1500%2Co_75/l_sample%2Cc_limit%2Cw_1000%2Ch_420/sample"&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Add a nice blurred border to our overlay

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;https://res.cloudinary.com/demo/image/upload/c_fill,w_1000,h_420,e_blur:1500,o_75/l_sample,c_limit,w_1000,h_420&lt;/code&gt;&lt;strong&gt;&lt;em&gt;&lt;code&gt;,e_outline:outer:2:100&lt;/code&gt;&lt;/em&gt;&lt;/strong&gt;&lt;code&gt;/sample&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Added: &lt;strong&gt;&lt;em&gt;&lt;code&gt;e_outline:outer:2:100&lt;/code&gt;&lt;/em&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;You might notice that the border now pushes the overlay outside the &lt;em&gt;canvas&lt;/em&gt;...
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Phb-JBLN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/demo/image/upload/c_fill%2Cw_1000%2Ch_420%2Ce_blur:1500%2Co_75/l_sample%2Cc_limit%2Cw_1000%2Ch_420%2Ce_outline:outer:2:100/sample"&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Prevent "overflow" caused by new border

&lt;ul&gt;
&lt;li&gt;Adding a border to the overlay actually made it larger than the boundaries of the background (base image). We have two options to remedy this:&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Option A:&lt;/strong&gt; Re-crop: Since adding a border to the overlay made it larger than the boundaries of the background, we can simply re-crop the output

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;https://res.cloudinary.com/demo/image/upload/c_fill,w_1000,h_420,e_blur:1500,o_75/l_sample,c_limit,w_1000,h_420,e_outline:outer:2:100&lt;/code&gt;&lt;strong&gt;&lt;em&gt;&lt;code&gt;/c_crop,w_1000,h_420/&lt;/code&gt;&lt;/em&gt;&lt;/strong&gt;&lt;code&gt;sample&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Added: &lt;strong&gt;&lt;em&gt;&lt;code&gt;/c_crop,w_1000,h_420/&lt;/code&gt;&lt;/em&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;If we wanted to re-crop just the overlay, we could use &lt;code&gt;fl_layer_apply&lt;/code&gt; to prevent crop action from applying to the entire chain&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Option B:&lt;/strong&gt; Use the &lt;code&gt;no_overflow_flag&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;This is a super helpful flag to remember! From &lt;a href="https://cloudinary.com/documentation/transformation_flags" rel="noopener"&gt;the docs&lt;/a&gt;:

&lt;ul&gt;
&lt;li&gt;&amp;gt; Prevents Cloudinary from extending the image canvas beyond the original dimensions when overlaying text and other images.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;https://res.cloudinary.com/demo/image/upload/c_fill,w_1000,h_420,e_blur:1500,o_75/l_sample,c_limit,w_1000,h_420,e_outline:outer:2:100&lt;/code&gt;&lt;strong&gt;&lt;em&gt;&lt;code&gt;,fl_no_overflow&lt;/code&gt;&lt;/em&gt;&lt;/strong&gt;&lt;code&gt;/sample&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Added: &lt;strong&gt;&lt;em&gt;&lt;code&gt;fl_no_overflow&lt;/code&gt;&lt;/em&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Final output, using option B:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wUXfxa-2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/demo/image/upload/c_fill%2Cw_1000%2Ch_420%2Ce_blur:1500%2Co_75/l_sample%2Cc_limit%2Cw_1000%2Ch_420%2Ce_outline:outer:2:100%2Cfl_no_overflow/sample" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wUXfxa-2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/demo/image/upload/c_fill%2Cw_1000%2Ch_420%2Ce_blur:1500%2Co_75/l_sample%2Cc_limit%2Cw_1000%2Ch_420%2Ce_outline:outer:2:100%2Cfl_no_overflow/sample"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Extras:
&lt;/h3&gt;

&lt;p&gt;There is still more we can do with this!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add compression

&lt;ul&gt;
&lt;li&gt;To save some bytes, we can tell Cloudinary to serve as a compressed JPG with a quality of 80%&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;https://res.cloudinary.com/demo/image/upload/c_fill,w_1000,h_420,e_blur:1500,o_75/l_sample,c_limit,w_1000,h_420,e_outline:outer:2:100/c_crop,w_1000,h_420&lt;/code&gt;&lt;strong&gt;&lt;em&gt;&lt;code&gt;,q_80&lt;/code&gt;&lt;/em&gt;&lt;/strong&gt;&lt;code&gt;/sample&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;For our sample image, this gives us a saving of approximately 50%!!!&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Add overlay text

&lt;ul&gt;
&lt;li&gt;We can add &lt;a href="https://cloudinary.com/documentation/image_transformations#adding_text_captions" rel="noopener"&gt;overlay text&lt;/a&gt;, such as the title of the post that our cover image is for&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;https://res.cloudinary.com/demo/image/upload/c_fill,w_1000,h_420,e_blur:1500,o_75/l_sample,c_limit,w_1000,h_420,e_outline:outer:2:100,fl_no_overflow&lt;/code&gt;&lt;strong&gt;&lt;em&gt;&lt;code&gt;/l_text:Roboto_100_normal_stroke_:Hero%20Image,co_rgb:FFFFFF,bo_8px_solid_black,g_south,y_30/&lt;/code&gt;&lt;/em&gt;&lt;/strong&gt;&lt;code&gt;sample&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;stroke&lt;/em&gt;, and &lt;em&gt;border&lt;/em&gt; (&lt;code&gt;bo_&lt;/code&gt;) are used to provide an outline around the text
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--oWH88VNJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/demo/image/upload/c_fill%2Cw_1000%2Ch_420%2Ce_blur:1500%2Co_75/l_sample%2Cc_limit%2Cw_1000%2Ch_420%2Ce_outline:outer:2:100%2Cfl_no_overflow/l_text:Roboto_100_normal_stroke_:My%2520Image%2Cco_rgb:FFFFFF%2Cbo_8px_solid_black%2Cg_south%2Cy_30/sample"&gt;
&lt;/li&gt;
&lt;/ul&gt;


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

&lt;h2&gt;
  
  
  Wrap-Up
&lt;/h2&gt;

&lt;p&gt;This might have seemed like a bunch of work, but don't forget; now that we have the transformation chain built, we can reuse it across an unlimited number of images, simply replacing &lt;code&gt;{uploadedId}&lt;/code&gt; with our desired input:&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;myCloudId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;acb123&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// pretend `myImages` is an array of hundreds of image IDs&lt;/span&gt;
&lt;span class="nx"&gt;myImages&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;uploadedId&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;heroImage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://res.cloudinary.com/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;myCloudId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/image/upload/c_fill,w_1000,h_420,e_blur:1500,o_75/l_&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;uploadedId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;,c_limit,w_1000,h_420,e_outline:outer:2:100,fl_no_overflow/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;uploadedId&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;saveImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;heroImage&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;In fact, here it is in action, working its magic across any input I give it (large GIF, give a second to load...):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--a29cz9OO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://i.imgur.com/UIi99Do.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--a29cz9OO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://i.imgur.com/UIi99Do.gif" alt="Cloudinary Dynamic Cover Image - Demo With Multiple Inputs"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nice, right!?&lt;/p&gt;

&lt;p&gt;We could also...&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Save this as a &lt;a href="https://cloudinary.com/documentation/chained_and_named_transformations#named_transformations" rel="noopener"&gt;&lt;em&gt;Named Transformation&lt;/em&gt;&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;Once we do this, reusing it becomes as easy as &lt;code&gt;/image/upload/{transformationName}/{uploadedId}&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Implement this via Cloudinary API&lt;/li&gt;
&lt;li&gt;Integrate into any backend (WordPress, Gatsby, etc.) for dynamic &lt;code&gt;meta&lt;/code&gt; image tags to serve to Twitter, FB / OpenGraph, etc.&lt;/li&gt;
&lt;li&gt;... and many more things!&lt;/li&gt;
&lt;/ul&gt;




&lt;blockquote&gt;
&lt;p&gt;If this was interesting to you, I have a few other Cloudinary related projects that might be worth checking out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://cheatsheets.joshuatz.com/cheatsheets/apis/cloudinary/" rel="noopener"&gt;Cloudinary Cheatsheet&lt;/a&gt; - WIP quick cheatsheet&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://joshuatz.com/projects/applications/desktop-cloud-transform-dct/" rel="noopener"&gt;Desktop Cloud Transform&lt;/a&gt; - Transform local images with Cloudinary, with easy drag-and-drop GUI&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://joshuatz.com/projects/web-stuff/cloudinary-wysiwyg-visual-editor-for-transformations/" rel="noopener"&gt;Cloudinary WYSIWYG&lt;/a&gt; - Visually build Cloudinary transformations with interactive canvas&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;




&lt;p&gt;More About Me:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🔗&lt;a href="https://joshuatz.com/" rel="noopener"&gt;joshuatz.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;👨‍💻&lt;a href="https://dev.to/joshuatz" rel="noopener"&gt;dev.to/joshuatz&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;💬&lt;a href="https://twitter.com/1joshuatz" rel="noopener"&gt;@1joshuatz&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;💾&lt;a href="https://github.com/joshuatz" rel="noopener"&gt;github.com/joshuatz&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>tutorial</category>
      <category>cloudinary</category>
      <category>image</category>
    </item>
    <item>
      <title>Using Windows Task Scheduler to Automate NodeJS Scripts</title>
      <dc:creator>Joshua Tzucker</dc:creator>
      <pubDate>Fri, 21 Feb 2020 21:28:37 +0000</pubDate>
      <link>https://forem.com/joshuatz/using-windows-task-scheduler-to-automate-nodejs-scripts-1lm3</link>
      <guid>https://forem.com/joshuatz/using-windows-task-scheduler-to-automate-nodejs-scripts-1lm3</guid>
      <description>&lt;p&gt;This is a post about using Windows Task Scheduler to automate the execution of NodeJS scripts, and other NPM / Yarn based tasks. &lt;strong&gt;If you don't use Windows, this post is probably not for you&lt;/strong&gt; (but feel free to read anyways 🤷‍♀️)&lt;/p&gt;

&lt;h1&gt;
  
  
  Why? 🤔
&lt;/h1&gt;

&lt;p&gt;Tons of reasons! Maybe you are trying to emulate a production environment that has NodeJS scripts as scheduled CRON tasks. Or, for your own productivity or fun you want to script things to happen based on Window events.&lt;/p&gt;

&lt;p&gt;For example, you could write a NodeJS script that talks to your project tracker of choice via API and stops any running timers when you lock your computer to take a break.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why not just use &lt;code&gt;crontab&lt;/code&gt; under WSL?
&lt;/h2&gt;

&lt;p&gt;Good question! If you have WSL (&lt;em&gt;Windows Subsystem for Linux&lt;/em&gt;) installed, and you &lt;strong&gt;only&lt;/strong&gt; &lt;em&gt;want to trigger actions based on time&lt;/em&gt;, then you should totally give crontab under WSL a shot!&lt;/p&gt;

&lt;p&gt;Although there used to be issues with it (in past versions, WSL used to kill background tasks when you closed the console), I just gave it a shot, and had success. If there is interest, I might make a separate post about how to setup crontab under WSL.&lt;/p&gt;

&lt;p&gt;However, Task Scheduler still has value as a separate tool, since &lt;strong&gt;more than just time can be used as a trigger&lt;/strong&gt;; you can execute tasks based on computer unlocks, power events, and more. You can't do that with crontab.&lt;/p&gt;

&lt;h1&gt;
  
  
  How? 🤓
&lt;/h1&gt;

&lt;p&gt;Steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Find where the binary / application you need to run is stored

&lt;ul&gt;
&lt;li&gt;You can use &lt;code&gt;where npm&lt;/code&gt; or &lt;code&gt;where yarn&lt;/code&gt; from the command line to find the path

&lt;ul&gt;
&lt;li&gt;Example: My yarn path is &lt;code&gt;C:\Program Files (x86)\Yarn\bin\yarn.cmd&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Open &lt;code&gt;Task Scheduler&lt;/code&gt; (search in programs, or &lt;code&gt;WIN+R, taskschd.msc&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Start the task creation process by clicking on "Create Basic Task" or "Create Task" in the sidebar

&lt;ul&gt;
&lt;li&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vAo2m0pS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/hcs5idqr2lfzu9l7ssso.png" alt="Windows Task Scheduler - Create Task Buttons"&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pick a trigger&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;"On a Schedule" (like CRON)&lt;/li&gt;
&lt;li&gt;"At log on"&lt;/li&gt;
&lt;li&gt;Etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add your Action&lt;/strong&gt;: Action -&amp;gt; &lt;code&gt;Start a Program&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;"Program/script":

&lt;ul&gt;
&lt;li&gt;Here is where you plug in the path to the application you found in step 1&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;"Add arguments" - You should put whatever you would put after &lt;code&gt;npm&lt;/code&gt; or &lt;code&gt;yarn&lt;/code&gt; normally.

&lt;ul&gt;
&lt;li&gt;If normally execute &lt;code&gt;npm run myScheduledTask&lt;/code&gt;, you would want arguments to be &lt;code&gt;run myScheduledTask&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;If you are calling a &lt;code&gt;scripts&lt;/code&gt; entry in a &lt;code&gt;package.json&lt;/code&gt; file, you need to tell scheduler to run this where your &lt;code&gt;package.json&lt;/code&gt; file is located.

&lt;ul&gt;
&lt;li&gt;If using Yarn, you can pass the working directory through args, &lt;a href="https://stackoverflow.com/q/46891622/11447682"&gt;with &lt;code&gt;cwd&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Otherwise, use the &lt;code&gt;start in (optional)&lt;/code&gt; field to specify the directory&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

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

&lt;blockquote&gt;
&lt;p&gt;If you are concerned about keeping track of the results of what ran, you can also capture the result of anything spit out to the console by using &lt;code&gt;&amp;gt;&amp;gt; task_log.txt&lt;/code&gt; or something like that.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  👩‍🍳 - You can combine actions and triggers
&lt;/h2&gt;

&lt;p&gt;One nice feature of task scheduler that I did not notice immediately is that it does not have to be a 1:1 mapping of task-trigger-action.&lt;/p&gt;

&lt;p&gt;For example, you can group ten different actions under a single task with a shared trigger. &lt;/p&gt;

&lt;h2&gt;
  
  
  ✨ - You can use Git Bash for more advanced scripting
&lt;/h2&gt;

&lt;p&gt;Instead of targeting NPM, Yarn, or Windows CMD, if you have Git Bash (comes packaged with &lt;em&gt;Git for Windows&lt;/em&gt;), you can it as the target "Program/Script", and then execute a more advanced command that uses some bash tools. For example, a sample task that performs some backups for a project might look like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Program/Script": &lt;code&gt;C:\Program Files\Git\git-bash.exe&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;"Add Arguments": &lt;code&gt;cd C:/projects/my-proj &amp;amp;&amp;amp; node prep-dirs.js &amp;amp;&amp;amp; npm run backup &amp;gt;&amp;gt; backup_log.txt&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  💥 - &lt;code&gt;%1 is not a valid Win32 application&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;If you see this error, you have probably selected the wrong application as the &lt;code&gt;Program/Script&lt;/code&gt; to execute. For example, using &lt;code&gt;/yarn&lt;/code&gt; instead of &lt;code&gt;yarn.cmd&lt;/code&gt; will result in this error.&lt;/p&gt;

&lt;h2&gt;
  
  
  ⚙ - Stop the Cmd window from popping up
&lt;/h2&gt;

&lt;p&gt;If the black windows Command prompt window keeps popping up whenever your task runs, you need to change one of the basic settings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Change security options to: &lt;code&gt;Run whether user is logged on or not&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;🔐You will probably also want to check &lt;code&gt;Do not store password&lt;/code&gt; &lt;/li&gt;
&lt;/ul&gt;


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

&lt;p&gt;There is no harm in having it show up; but it might get annoying if your task is scheduled to run frequently.&lt;/p&gt;

&lt;h2&gt;
  
  
  ⏰ - How to schedule more frequently than every 5 minutes
&lt;/h2&gt;

&lt;p&gt;You might have already noticed that the smallest interval that shows up in the &lt;code&gt;repeat task every&lt;/code&gt; duration picker, under trigger settings, is &lt;code&gt;5 minutes&lt;/code&gt;. Uh-oh!&lt;/p&gt;

&lt;p&gt;Actually, this is an easy fix - you can actually type a custom interval in that box! So if you wanted an entry that was equivalent to a CRON of &lt;code&gt;* * * * *&lt;/code&gt; (every minute), just type in the box &lt;code&gt;1 minute&lt;/code&gt; and set &lt;code&gt;for a duration of&lt;/code&gt; to &lt;code&gt;Indefinitely&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here is what that looks like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mjEhcvig--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/eb47j4ggcvil4hncxm36.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mjEhcvig--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/eb47j4ggcvil4hncxm36.png" alt="Windows Task Scheduler - Creating a time based entry that repeats every minute, like CRON of * * * * *"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Comparison with CRON
&lt;/h1&gt;

&lt;p&gt;Since this is likely to come up in the comments (I can already hear the annoying response;  &lt;em&gt;''why don't you use a real operating system? lol!"&lt;/em&gt;) - yes, Task Scheduler is not a perfect replacement for CRON on windows. But it is not really meant to be, and this post is not advocating as such either.&lt;/p&gt;

&lt;p&gt;Plus, you &lt;em&gt;can&lt;/em&gt; use crontab under WSL now (see my note under "why?").&lt;/p&gt;

&lt;h1&gt;
  
  
  Wrap Up
&lt;/h1&gt;

&lt;p&gt;I hope this was helpful! This is a bit different from what I normally write about, but felt compelled to publish it as I had trouble finding existing resources on the topic.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Pnm2CAXZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/1quk2nax86o8zd6tblli.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Pnm2CAXZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/1quk2nax86o8zd6tblli.png" alt="Success!"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>node</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>5 Underrated *Built-In* VSCode Features</title>
      <dc:creator>Joshua Tzucker</dc:creator>
      <pubDate>Thu, 02 Jan 2020 20:21:11 +0000</pubDate>
      <link>https://forem.com/joshuatz/5-underrated-built-in-vscode-features-3koc</link>
      <guid>https://forem.com/joshuatz/5-underrated-built-in-vscode-features-3koc</guid>
      <description>&lt;h1&gt;
  
  
  Intro
&lt;/h1&gt;

&lt;p&gt;First, to get this out the way, this post is &lt;strong&gt;not&lt;/strong&gt; a list of "top extensions you need to install". Although those lists are helpful (albeit a little "clickbaity"), and I love that VSCode has an extremely "healthy" ecosystem of extensions and plugins, I noticed few posts about the amazing stuff that is &lt;strong&gt;built-in to VSCode from the get-go&lt;/strong&gt;, and that is what I want to talk about.&lt;/p&gt;

&lt;p&gt;These are features of VSCode that are baked into the editor (no extensions or tooling to install), but I personally don't think are getting enough attention and could use a PSA.&lt;/p&gt;

&lt;h2&gt;
  
  
  #1: JavaScript Type Safety
&lt;/h2&gt;

&lt;p&gt;Although the rest of the list is not really ordered, this &lt;strong&gt;&lt;em&gt;definitely&lt;/em&gt;&lt;/strong&gt; deserves a #1 spot, by a large margin. I'm not going to shove TypeScript or type-safe JS (what?!) down your throat, but I'd like to just point out how VSCode has a feature that allows you to get some of the benefits of type-safety, without needing to switch to TS files, transpiling / compiling, or requiring that other devs use TypeScript in order to use your codebase. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This is a wonderful half-way solution for those looking to introduce some type-safety into their JS code immediately, with minimal changes and setup.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I don't want to go too in-depth on this, as there are multiple guides on this out there, but I'll cover some "quickstart" basics:&lt;/p&gt;

&lt;h3&gt;
  
  
  Enabling JS Type Checking
&lt;/h3&gt;

&lt;p&gt;There are multiple ways to enable type checking in JS files, but the easiest if you are looking to just try it out, is to add &lt;code&gt;// @ts-check&lt;/code&gt; to the top of your file.&lt;/p&gt;

&lt;p&gt;See the &lt;a href="https://code.visualstudio.com/docs/nodejs/working-with-javascript#_type-checking-javascript" rel="noopener"&gt;"Type Checking JavaScript" section&lt;/a&gt; of VSCode's "Working with JavaScript" docs for more details.&lt;/p&gt;

&lt;h3&gt;
  
  
  What does this get you?
&lt;/h3&gt;

&lt;p&gt;If you don't want to do any extra work (such as annotating types or setting up configs), simply adding &lt;code&gt;// @ts-check&lt;/code&gt; is a way to instantly get TypeScript powered type checking in VSCode.&lt;/p&gt;

&lt;p&gt;For example, checkout this screenshot comparing a JS file without and with TS-checking on:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KaiL-x6p--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/cd068t0rq00216sdmj6l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KaiL-x6p--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/cd068t0rq00216sdmj6l.png" alt="Visual Studio Code - Regular JS vs TS-Check On for Type Checking"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the above example, the developer has forgotten that &lt;code&gt;querySelectorAll&lt;/code&gt; does not return an &lt;code&gt;array&lt;/code&gt;, it returns something special - a &lt;code&gt;NodeList&lt;/code&gt; - which does not have the built in &lt;code&gt;map()&lt;/code&gt; method that arrays have. With type checking on, the fatal error triggered by the use of &lt;code&gt;map&lt;/code&gt; is caught in VSCode and flagged.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;For those wondering, an easy way to convert from a NodeList to an array is to use &lt;code&gt;Array.from&lt;/code&gt;: &lt;code&gt;Array.from(document.querySelectorAll('a[href]'))&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Annotating Types with JSDoc and Advanced Usage
&lt;/h3&gt;

&lt;p&gt;The "power-user" part of this feature is the ability to use JSDoc comments to tell VSCode's TypeScript powered intellisense system about advanced types that are within your JS code. This TypeScript doc page - &lt;a href="https://www.typescriptlang.org/docs/handbook/type-checking-javascript-files.html" rel="noopener"&gt;"Type Checking JavaScript Files"&lt;/a&gt; goes into depth on it, but for brevity, I'll just paste a cool example:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zxTo_dDm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/uz1rpermuke4fkrv3xrf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zxTo_dDm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/uz1rpermuke4fkrv3xrf.png" alt="VSCode - Advanced TS-Check JS Types with JSDoc Annotations"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For even more advanced usage, there are some great resources out there, starting with &lt;a href="https://cheatsheets.joshuatz.com/settings/vscode/" rel="noopener"&gt;my VSCode cheatsheet&lt;/a&gt; which has a whole section devoted to JSDoc powered type checking and intellisense 🙂. It also has a list of links to other helpful related resources.&lt;/p&gt;




&lt;h2&gt;
  
  
  #2: Making things easier for contributors
&lt;/h2&gt;

&lt;p&gt;Spending time to streamline the process for other people to contribute code to your codebase might feel like a chore, but in the long-run it makes things better for everyone. The easier and more "mistake-proof" the process is for devs to contribute, the less likely it is that you will have to field duplicate questions, reject PRs for violating guidelines, and deal with unnecessary back and forth.&lt;/p&gt;

&lt;p&gt;What you might be surprised to learn is that VSCode has some built-in features that help with this goal.&lt;/p&gt;

&lt;h3&gt;
  
  
  Recommended Extensions
&lt;/h3&gt;

&lt;p&gt;One of them I actually learned about when I poked through &lt;a href="https://github.com/thepracticaldev/dev.to/blob/master/.vscode/extensions.json"&gt;Dev.to's own source code&lt;/a&gt;, as it was the first time I had seen it used - &lt;em&gt;"Recommended Extensions"&lt;/em&gt;. The way it works is that you add a special file - &lt;code&gt;.vscode/extensions.json&lt;/code&gt; - to your project root, with a list of extensions that you want all contributors to be using (for example, Prettier), and VSCode will pick up on that and popup a recommendation to Devs to install them when they clone and open your repo.&lt;/p&gt;

&lt;p&gt;You can find more details on how to use this in VSCode's &lt;a href="https://code.visualstudio.com/docs/editor/extension-gallery#_workspace-recommended-extensions" rel="noopener"&gt;workspace recommended extensions&lt;/a&gt; documentation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Workspace tools and settings
&lt;/h3&gt;

&lt;p&gt;Built-in to VSCode are multiple places where you can override user settings and craft per project configurations, both of which can make it easier for contributors to work in your codebase with VSCode.&lt;/p&gt;

&lt;p&gt;For example, if you manage a mono-repo with distinct parts, you might want to take a look at &lt;a href="https://code.visualstudio.com/docs/editor/multi-root-workspaces" rel="noopener"&gt;"multi-root workspaces"&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For overriding settings, such as default indent and spacing options, you can commit &lt;code&gt;.vscode/settings.json&lt;/code&gt; to the root of your repo with the overrides, but be respectful of your contributors and don't override anything that you want them to be able to change.&lt;/p&gt;




&lt;h2&gt;
  
  
  #3: User-Defined Snippets
&lt;/h2&gt;

&lt;p&gt;Although VSCode does not yet support advanced macros (without the use of an extension that is), did you know that it &lt;em&gt;does&lt;/em&gt; already support advanced snippets? At first glance, these might seem like they can only insert basic blocks of text, but you can actually do some advanced things with them.&lt;/p&gt;

&lt;p&gt;For example, here is a snippet that I quickly whipped up to convert a markdown style bullet list into Github styled checkbox list:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"MD List to GH Checkbox"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"scope"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"markdown"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"prefix"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"list-to-checkbox"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"body"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"${TM_SELECTED_TEXT/^([&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s2"&gt; ]*-) ?(.*)$/$1 [ ] $2/gm}"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Transform MD list to MD checkbox list"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here it is working in action:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--a3qj3pKW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/3lt6chidupf52t59w9eu.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--a3qj3pKW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/3lt6chidupf52t59w9eu.gif" alt="Animated GIF showing how a VSCode snippet can turn a markdown bullet list into Github styled markdown checkboxes"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The main docs page for creating custom snippets is &lt;a href="https://code.visualstudio.com/docs/editor/userdefinedsnippets#_create-your-own-snippets" rel="noopener"&gt;here&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  #4: Reordering Imports Automatically
&lt;/h2&gt;

&lt;p&gt;First, I would like to thank Ryan Chenkie, for this tweet that brought this to my attention:&lt;/p&gt;


&lt;blockquote class="ltag__twitter-tweet"&gt;
      &lt;div class="ltag__twitter-tweet__media ltag__twitter-tweet__media__video-wrapper"&gt;
        &lt;div class="ltag__twitter-tweet__media--video-preview"&gt;
          &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fZ2jdSCs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/tweet_video_thumb/EK3ycwiXUAAqH32.jpg" alt="unknown tweet media content"&gt;
          &lt;img src="/assets/play-butt.svg" class="ltag__twitter-tweet__play-butt" alt="Play butt"&gt;
        &lt;/div&gt;
        &lt;div class="ltag__twitter-tweet__video"&gt;
          
            
          
        &lt;/div&gt;
      &lt;/div&gt;

  &lt;div class="ltag__twitter-tweet__main"&gt;
    &lt;div class="ltag__twitter-tweet__header"&gt;
      &lt;img class="ltag__twitter-tweet__profile-image" src="https://res.cloudinary.com/practicaldev/image/fetch/s--5kkCgNXQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/profile_images/875498876261859329/7iV5CwVY_normal.jpg" alt="Ryan Chenkie profile image"&gt;
      &lt;div class="ltag__twitter-tweet__full-name"&gt;
        Ryan Chenkie
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__username"&gt;
        &lt;a class="comment-mentioned-user" href="https://dev.to/ryanchenkie"&gt;@ryanchenkie&lt;/a&gt;

      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__twitter-logo"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--P4t6ys1m--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-f95605061196010f91e64806688390eb1a4dbc9e913682e043eb8b1e06ca484f.svg" alt="twitter logo"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__body"&gt;
      Just learned about the quick key in VS Code to remove unused imports and reorder used imports: Shift + Option + O.&lt;br&gt;&lt;br&gt;Save yourself some time when you want to clean up imports 🙌 
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__date"&gt;
      15:17 PM - 03 Dec 2019
    &lt;/div&gt;


    &lt;div class="ltag__twitter-tweet__actions"&gt;
      &lt;a href="https://twitter.com/intent/tweet?in_reply_to=1201883268527927301" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WwRENZp4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-reply-action-238fe0a37991706a6880ed13941c3efd6b371e4aefe288fe8e0db85250708bc4.svg" alt="Twitter reply action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/retweet?tweet_id=1201883268527927301" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PFD0MJBa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-retweet-action-632c83532a4e7de573c5c08dbb090ee18b348b13e2793175fea914827bc42046.svg" alt="Twitter retweet action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/like?tweet_id=1201883268527927301" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6wx1BHu3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-like-action-1ea89f4b87c7d37465b0eb78d51fcb7fe6c03a089805d7ea014ba71365be5171.svg" alt="Twitter like action"&gt;
      &lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/blockquote&gt;


&lt;p&gt;For Windows users, that key combo is SHIFT + ALT + O, by default.&lt;/p&gt;

&lt;p&gt;As a reply to that tweet pointed out, you can also have VSCode automatically do this, by configuring it in your &lt;code&gt;settings.json&lt;/code&gt; file, like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"editor.codeActionsOnSave"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"source.organizeImports"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  #5: Ability to build a custom extension for yourself or your company
&lt;/h2&gt;

&lt;p&gt;One of the best things about VSCode is how extensible it is, and how easy it is to build and test new extensions. Often when this is discussed, the focus is on really flashy and/or impressive extensions, but I want to remind everyone that extensions that solve a singular niche problem can be just as important, especially within certain organizations.&lt;/p&gt;

&lt;p&gt;Within the discussion of "tooling", you should think if there is a custom extension that could be built for your company (or just yourself) that could improve your developer workflow, help convert legacy code, and/or interop between processes that are specific to your work. It might be easier than you think to build one, and as a bonus, is sure to impress others.&lt;/p&gt;




&lt;h1&gt;
  
  
  Wrap Up
&lt;/h1&gt;

&lt;p&gt;I hope you found at least one of these items helpful! Thank you to the VSCode team and open-source contributors for building a revolutionary code editor!&lt;/p&gt;

</description>
      <category>vscode</category>
      <category>productivity</category>
      <category>tips</category>
    </item>
    <item>
      <title>Fixed URL Reverse-Proxy with Serverless (GCP) Functions and Ngrok</title>
      <dc:creator>Joshua Tzucker</dc:creator>
      <pubDate>Tue, 03 Dec 2019 23:39:23 +0000</pubDate>
      <link>https://forem.com/joshuatz/fixed-url-reverse-proxy-with-serverless-gcp-functions-and-ngrok-3aed</link>
      <guid>https://forem.com/joshuatz/fixed-url-reverse-proxy-with-serverless-gcp-functions-and-ngrok-3aed</guid>
      <description>&lt;h1&gt;
  
  
  Background - Why might you need a reverse proxy?
&lt;/h1&gt;

&lt;p&gt;Web development tooling has come a long way, and most frameworks and SDKs have made previewing your app as you work a breeze; often it is as simple as running something like &lt;code&gt;npm start&lt;/code&gt; from your CLI. But an unexpected hurdle that many web developers hit, and can seem &lt;em&gt;much&lt;/em&gt; more complicated, is "how do I use this &lt;strong&gt;outside&lt;/strong&gt; of my local network?". Things like screen-sharing, screen recording, and VSCode's Live Share can solve this issue if all you need to do is show your localhost demo to your remote coworker. But if you need to actually &lt;strong&gt;expose&lt;/strong&gt; your code to the outside world, those tools fall short. &lt;/p&gt;

&lt;p&gt;For example, a very common scenario is trying to test a third-party service (Slack, Github, Twilio, etc.) that talks to your codebase, maybe through a webhook. In this scenario, you can't run the third party code locally (e.g. running all of Github's platform on your computer), and if you put something like "localhost:3001/webhook" as the webhook, that third party is going to be unable to reach the code running inside your local network (for good reason!).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3ZTp7qlX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/obcwji0auqo1l7heia3m.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3ZTp7qlX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/obcwji0auqo1l7heia3m.jpg" alt="blocked entry"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;How do we solve this?&lt;/p&gt;

&lt;h1&gt;
  
  
  Reverse-Proxies
&lt;/h1&gt;

&lt;p&gt;The common solution to this issue is a &lt;a href="https://en.wikipedia.org/wiki/Reverse_proxy" rel="noopener"&gt;&lt;em&gt;reverse proxy&lt;/em&gt;&lt;/a&gt; combined with &lt;em&gt;ssh tunneling&lt;/em&gt;. You have a public URL, which points to a public server, which uses a reverse proxy to hand-off the request to a different port and/or server, which in turn gets proxied / tunneled via a SSH tunnel between the server and our local development computer. This is much easier to show visually, so here is a simplistic diagram showing this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1FMsTdI2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/o8606dvwj8nc7rgzpr6y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1FMsTdI2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/o8606dvwj8nc7rgzpr6y.png" alt="Reverse proxy over SSH"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is a lot that goes into this solution. You need to run a public server, set up SSH support on it, have a domain name, DNS entries, SSL certificates; the list goes on and on. It's not all that complicated (&lt;a href="https://alexle.net/free-alternative-to-ngrok-with-permanent-subdomain-using-nginx-ssl-and-reverse-ssh-using-your-own-server/" rel="noopener"&gt;here is a simple guide&lt;/a&gt;), but it is also not free or quick.&lt;/p&gt;

&lt;h1&gt;
  
  
  Quick, Simple, and *Free - Ngrok
&lt;/h1&gt;

&lt;p&gt;Given the complexity of setting up a full reverse-proxy through a SSH pipeline, services have sprung up to simplify this for developers.&lt;/p&gt;

&lt;p&gt;One of the most popular and easiest to use is &lt;a href="https://ngrok.com/" rel="noopener"&gt;Ngrok&lt;/a&gt;. With a single command, you can instantly get a public URL that you can use to access your localhost from anywhere - no public server or complicated setup necessary!&lt;/p&gt;

&lt;p&gt;For example, to relay HTTP requests from &lt;em&gt;outside&lt;/em&gt; your home network to your localhost on port 3001, you could use &lt;code&gt;ngrok http 3001&lt;/code&gt; and be up and running in seconds!&lt;/p&gt;

&lt;p&gt;Ngrok basically takes the place of both the SSH tunnel setup (with its CLI client), and the public server with a reverse proxy - the second and third blocks in my diagram up above. It even comes with built-in tools, such as a dashboard, request inspector, and replay functionality.&lt;/p&gt;

&lt;p&gt;Ngrok is also recommended by several well-known third-party platforms (&lt;a href="https://www.twilio.com/blog/2015/09/6-awesome-reasons-to-use-ngrok-when-testing-webhooks.html"&gt;Twilio&lt;/a&gt;, &lt;a href="https://developer.github.com/webhooks/configuring/"&gt;Github&lt;/a&gt;).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I am not affiliated with Ngrok in any way; I just think they have an impressive service.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The downside to Ngrok
&lt;/h2&gt;

&lt;p&gt;Although Ngrok has a very generous free tier, one major restriction is that the public URLs that are generated are both random and not assigned - meaning that each time you start it up, you will get a different random subdomain, like &lt;code&gt;https://df12f52a.ngrok.io&lt;/code&gt;. If you are trying to develop webhooks or something similar, this means that each time you need to restart Ngrok, you would have to login into your third-party platform, find the admin dashboard, and copy and paste your new Ngrok URL into the GUI. Or, if you are sharing URLs with coworkers, you would need to keep sending them updated URLs.&lt;/p&gt;

&lt;p&gt;Ngrok does offer custom subdomains or personalized domains, but these come under the paid option, which starts at $5 a month. Not much, but this is basically the same price as running Ngrok or Nginx reverse proxy yourself on a paid server, like Digital Ocean.&lt;/p&gt;

&lt;h1&gt;
  
  
  My Solution - Add Another Proxy with Cloud Functions!
&lt;/h1&gt;

&lt;p&gt;This is the kind of solution I am simultaneously both proud and ashamed of. Because I am cheap, and didn't feel like buying a paid Ngrok plan just to test webhooks a few times a year, or setting up a dedicated server for it, I started thinking of how I might use free, or very low cost workarounds. What I came up with is kind of silly, but it works - just add another proxy, with a fixed URL, in front of the changing Ngrok URL!&lt;/p&gt;

&lt;p&gt;Let me elaborate. As I researched options, I noticed that cloud functions (Google Cloud Functions or AWS Lambda) were often the cheapest to use, in that they start at a scale of zero &lt;em&gt;by design&lt;/em&gt; (they only run when requested). At first I was thinking of replacing Ngrok with them entirely, but this quickly was squashed; they are designed to execute quickly and exit, and would not work for maintaining a SSH tunnel connnection.&lt;/p&gt;

&lt;p&gt;However, I realized two important things; cloud functions often get a semi-permanent PUBLIC url to invoke them, and they can handle both incoming and outbound requests. With this in mind, I realized I could setup a cloud function with a public URL, that simply proxies inbound requests to a re-configurable Ngrok address. Here is what that looks like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vMWINcyE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/sbfogovncgeyrcpl171b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vMWINcyE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/sbfogovncgeyrcpl171b.png" alt="gcp-proxy-func"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this scenario, &lt;strong&gt;my Cloud Function public URL stays static, even as the Ngrok address it points to changes&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;The code required to build this is very simple too; it can be assembled with just a little NodeJS, Express, body-parser middleware, and, most important, &lt;a href="https://github.com/chimurai/http-proxy-middleware" rel="noopener"&gt;&lt;code&gt;http-proxy-middleware&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The only caveat here is that the cloud function has to be redeployed each time that the Ngrok URL changes. However, this is very easy to automate; in fact, I already have a command set up in my &lt;code&gt;package.json&lt;/code&gt;, so if my Ngrok URL has changed, all I need to do to redeploy with the update is type &lt;code&gt;npm run ngrok-deploy&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  GCP
&lt;/h2&gt;

&lt;p&gt;I ended up going with Google Cloud Platform to provide the serverless function that proxies requests. Their &lt;a href="https://cloud.google.com/free/" rel="noopener"&gt;free tier&lt;/a&gt; includes 2 million (!!!) executions per month, plus 5GB of network traffic. This is WAY more than enough for some simple webhook testing.&lt;/p&gt;

&lt;p&gt;You can find all my code here, which proxies inbound requests to a configurable destination: &lt;a href="https://github.com/joshuatz/gcp-proxy-func" rel="noopener"&gt;github.com/joshuatz/gcp-proxy-func&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Another cheap alternative
&lt;/h2&gt;

&lt;p&gt;Besides the obvious routes of paying for a pro Ngrok plan or paying for a server to run your own reverse-proxy, there are some other alternatives I have yet to explore.&lt;/p&gt;

&lt;p&gt;One option could be to wrap a reverse proxy in a container, and deploy it on a platform that supports "scale to zero" container scaling, and have it "wake up" on an SSH connection. Then, you would only have to pay for it while you are actively connected via the SSH tunnel and using it for developing.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;This post first appeared on: &lt;a href="https://joshuatz.com/posts/2019/using-google-cloud-functions-permanent-url-to-proxy-ngrok-requests/"&gt;https://joshuatz.com/posts/2019/using-google-cloud-functions-permanent-url-to-proxy-ngrok-requests/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>devops</category>
      <category>ngrok</category>
      <category>webdev</category>
      <category>serverless</category>
    </item>
    <item>
      <title>Simple Intro to JWT Basics</title>
      <dc:creator>Joshua Tzucker</dc:creator>
      <pubDate>Sat, 28 Sep 2019 17:30:11 +0000</pubDate>
      <link>https://forem.com/joshuatz/simple-intro-to-jwt-basics-2kd8</link>
      <guid>https://forem.com/joshuatz/simple-intro-to-jwt-basics-2kd8</guid>
      <description>&lt;p&gt;If you have been put off learning about JWT (JSON Web Tokens) because they sound scary or complicated, I have good news; &lt;strong&gt;they really aren't&lt;/strong&gt;. In this short simplistic guide, I'll walk through the basics of JWT, what makes them unique, and their strengths and shortcomings compared to other forms of authentication.&lt;/p&gt;

&lt;h1&gt;
  
  
  Intro - What &lt;strong&gt;&lt;em&gt;is&lt;/em&gt;&lt;/strong&gt; a JWT?
&lt;/h1&gt;

&lt;p&gt;JSON Web Tokens, or JWTs, are a 'stateless' (more on this later) approach to authentication and session management, where the information about the auth or session (is user admin? Permissions? Etc.) can be stored &lt;strong&gt;&lt;em&gt;within&lt;/em&gt;&lt;/strong&gt; the token itself, rather than on the server.&lt;/p&gt;

&lt;p&gt;Essentially, you can think of JWT as a &lt;strong&gt;container&lt;/strong&gt; full of &lt;strong&gt;truths&lt;/strong&gt; that the server has sent (these are called &lt;em&gt;claims&lt;/em&gt; in JWT terms), and metadata about how these "truths" are stored (the algorithm and type).&lt;/p&gt;

&lt;p&gt;In the most practical terms, a JWT can be represented as a single string, which you will see later.&lt;/p&gt;

&lt;h1&gt;
  
  
  Stateless? What does that mean?
&lt;/h1&gt;

&lt;p&gt;JWTs are not &lt;em&gt;always&lt;/em&gt; part of a stateless system, but the way they are composed lends them to that sort of setup.&lt;/p&gt;

&lt;p&gt;With a typical non-JWT system, if you want to handle authentication and different permissions for different people, you usually use a combination of sessions and server-side storage - this would be your &lt;strong&gt;&lt;em&gt;state&lt;/em&gt;&lt;/strong&gt;. When a user logs in, you create a new &lt;code&gt;session&lt;/code&gt; for them, with a unique ID, and to check permissions, you lookup their user ID against a permissions table and/or roles table. Each page that you load might require multiple DB lookups, to check for a valid session, then get roles, then get permissions, etc.&lt;/p&gt;

&lt;p&gt;By contrast, with JWT, all the information about both the validity of the user (being logged in) and their role and/or permissions, is contained &lt;strong&gt;&lt;em&gt;directly within the JWT string itself&lt;/em&gt;&lt;/strong&gt;. The server, upon receiving an incoming JWT, simply has to verify that it is valid, but doesn't necessarily have to do any database lookups. This can make it stateless.&lt;/p&gt;

&lt;p&gt;There are downsides to the this approach however, which will be discussed later.&lt;/p&gt;

&lt;h1&gt;
  
  
  Breaking it down further - the parts of a JWT
&lt;/h1&gt;

&lt;p&gt;JWTs can seem overwhelming, so let's break them down part by part:&lt;/p&gt;

&lt;h2&gt;
  
  
  What do they actually look like?
&lt;/h2&gt;

&lt;p&gt;First, you might be wondering what a JWT actually looks like. Well, the final string that is sent from the server to the client, to be stored in &lt;code&gt;localStorage&lt;/code&gt;, &lt;code&gt;sessionStorage&lt;/code&gt;, or a cookie, can look like this (real example):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiQm9iYnkgVGFibGVzIiwiaWF0IjoxNTE2MjM5MDIyLCJpc0FkbWluIjp0cnVlLCJwZXJtaXNzaW9ucyI6eyJ1c2Vyc01ha2UiOnRydWUsInVzZXJzQmFuIjp0cnVlLCJ1c2Vyc0RlbGV0ZSI6ZmFsc2V9fQ.HFRcI4qU2lyvDnXhO-cSTkhvhrTCyXv6f6wXSJKGbFk
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Enlightening, huh? Well, it's a lot less scary once you find out this is all it is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;base64Url(header) + '.' + base64Url(payload) + '.' + base64Url(signature)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Let's break it down further...&lt;/p&gt;

&lt;h2&gt;
  
  
  The pieces
&lt;/h2&gt;

&lt;p&gt;As you saw above, the JWT is made up of three major components, each of which is base64 encoded (with the &lt;a href="https://en.wikipedia.org/wiki/Base64#RFC_4648"&gt;web-safe variant&lt;/a&gt;, to avoid characters that could screw up a URL) before being joined together. Each of these components, except the signature, is also a JSON object before encoding, but more on that below:&lt;/p&gt;

&lt;h4&gt;
  
  
  Header:
&lt;/h4&gt;

&lt;p&gt;The header is the part that contains the &lt;em&gt;metadata&lt;/em&gt; about the JWT itself. At the bare minimum, this will contain the type of algorithm used to encrypt the signature, but sometimes also contains extra metadata, like the type of token itself (&lt;code&gt;JWT&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"alg"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"HS256"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"typ"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"JWT"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In this example, the header is saying "Hey, I'm a token of type JWT, with an encryption algorithm of HS256!"&lt;/p&gt;

&lt;h4&gt;
  
  
  Payload:
&lt;/h4&gt;

&lt;p&gt;The payload is really the most important part in terms of functionality, not security. The payload contains the &lt;code&gt;claims&lt;/code&gt; (or &lt;em&gt;truths&lt;/em&gt; as I previously called them), that the server is saying belong to whomever holds the JWT and can send it back to the server.&lt;/p&gt;

&lt;p&gt;When the user sends the JWT &lt;em&gt;back&lt;/em&gt; to the server, via a header, URL, cookie, etc., the server decodes the payload and can use it in thousands of ways; for example, by allowing the user to make a "delete" action because one of the claims is that they are an admin.&lt;/p&gt;

&lt;p&gt;Example payload:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bobby Tables"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"iat"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1516239022&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"isAdmin"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"permissions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="nl"&gt;"usersMake"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="nl"&gt;"usersBan"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="nl"&gt;"usersDelete"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;There are no mandatory key-pairs that your payload must have, although there are some that are standardized and common. For example, &lt;code&gt;iat&lt;/code&gt; is a registered claim for &lt;code&gt;issued at&lt;/code&gt;, and if included, must be a number representing the timestamp the JWT was issued. You should avoid conflicting with reserved/registered claim names. You can read more about them in the original spec, &lt;a href="https://tools.ietf.org/html/rfc7519#section-4"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Reminder: You should try to keep both keys and values short, since JWT is designed to be small.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Signature:
&lt;/h4&gt;

&lt;p&gt;The signature of a JWT is the most important part when it comes to security. Essentially it is the value of the header + payload, put through a one-way encryption hashing function that uses a &lt;em&gt;secret&lt;/em&gt; that &lt;strong&gt;&lt;em&gt;ONLY&lt;/em&gt;&lt;/strong&gt; the server or other trusted entities know.&lt;/p&gt;

&lt;p&gt;If you base64 decode the value, it looks like gibberish, because it is a secure hash:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.T\#..Ú\¯.uá;ç.NHo.´ÂÉ{ú.¬.H..lY
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The pseudo code to generate this hash, on the server, looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HMACSHA256(
  base64Url(header) + "." +
  base64Url(payload),
  SECRET_KEY
);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The signature is a way for the server to, using its secret key**, validate that the JWT a user sends it is both valid, and created by itself or trusted creator.&lt;/p&gt;

&lt;p&gt;(PS: The secret key I used throughout my examples here was &lt;code&gt;krusty-krab-krabby-patty-secret-formula&lt;/code&gt;).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;** = If using &lt;em&gt;asymmetric encryption&lt;/em&gt;, you can use public key to validate. See "Signing Algorithms" subsection under "How is it secure?" section. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Breakdown summary - putting it back together
&lt;/h2&gt;

&lt;p&gt;So, to summarize and show how these parts fit together once again, we are taking:&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;header&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;alg&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;HS256&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;typ&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;JWT&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;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&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;Bobby Tables&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;iat&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1516239022&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;isAdmin&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;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;permissions&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;usersMake&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;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;usersBan&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;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;usersDelete&lt;/span&gt;&lt;span class="dl"&gt;"&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;...making a signature by using a one-way encryption algo...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Pseudo code&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;signature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;hashHS256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;withSecret&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;SECRET_KEY&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 joining the parts together into a single string, after base64'ing each part:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Pseudo code&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;jwtString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;base64Url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;base64Url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;base64Url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h1&gt;
  
  
  How is it secure?
&lt;/h1&gt;

&lt;p&gt;The security mechanism of JWT rests in its signature component (outlined above). To break it down further, when a user sends a JWT back to the server, the way the server checks to make sure that the claims should be trusted, is by validating the signature through:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Take incoming header and payload&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;SECRET_KEY&lt;/code&gt; that only the server and validator(s) know**&lt;/li&gt;
&lt;li&gt;Put header, payload, and secret, through one-way encryption hash

&lt;ul&gt;
&lt;li&gt;This creates a server-generated signature&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Check that the newly created signature matches the one sent in the JWT by the user&lt;/li&gt;
&lt;li&gt;If they match, that proves that the JWT the user sent was indeed created with the same &lt;em&gt;secret&lt;/em&gt; owned by the server!&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Please note that the strength of the security is strongly linked to your secret key - use a suitably long key (to &lt;a href="https://auth0.com/blog/brute-forcing-hs256-is-possible-the-importance-of-using-strong-keys-to-sign-jwts/"&gt;avoid brute force attempts&lt;/a&gt;) that is never shared.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Side note: In &lt;em&gt;addition&lt;/em&gt; to validating signatures, servers often also validate JWTs by checking that they conform to the expected structure and standards. See &lt;a href="https://auth0.com/docs/tokens/guides/jwt/validate-jwt"&gt;this auth0 guide&lt;/a&gt; for details.&lt;/p&gt;

&lt;p&gt;** = or public key, if using &lt;em&gt;asymmetric&lt;/em&gt; encryption, see below.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Signing Algorithms
&lt;/h2&gt;

&lt;p&gt;In these examples, I'm using HMAC, which is a &lt;em&gt;symmetric algorithm&lt;/em&gt;, meaning that there is only one private key and no public key. In order for more than one party to be able to create &lt;em&gt;or&lt;/em&gt; validate a JWT, they must have the secret key.&lt;/p&gt;

&lt;p&gt;With an &lt;em&gt;asymmetric algorithm&lt;/em&gt;, a secret key is still used to &lt;em&gt;create&lt;/em&gt; tokens, but a public key can also be used to confirm validity. This means that another server could create the JWT, and your server could still validate it by checking against the public key, without needing to share the private/secret key. This method is often preferred because it is a way for multiple parties to be able to validate, while only those with the private key can actually create tokens.&lt;/p&gt;

&lt;p&gt;I won't get any further into the details here, since this is supposed to be an intro, but you can read more &lt;a href="https://auth0.com/docs/tokens/concepts/signing-algorithms"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Downside to JWT: Logging users out / invalidating tokens
&lt;/h1&gt;

&lt;p&gt;So far, JWTs seem like they have numerous benefits over stateful authentication methods, but something we have not yet discussed is logging users out, deleting users, or otherwise invalidating or revoking tokens.&lt;/p&gt;

&lt;p&gt;The truth is, this is where JTWs fall short. In order to invalidate a JWT, you &lt;em&gt;need&lt;/em&gt; to have some sort of database / stateful system, because what you end up doing is maintaining either a blacklist or a whitelist.&lt;/p&gt;

&lt;p&gt;With a blacklist, whenever you want to invalidate a token, you would add it to the blacklist table, and whenever a user tries to use a JWT, you would always need to check that it is not on the blacklist.&lt;/p&gt;

&lt;p&gt;With a whitelist, it is basically the same thing, but add every token to the whitelist as it is created, and remove it from the whitelist when invalidating. And every incoming JWT would only be accepted if it &lt;em&gt;is&lt;/em&gt; on the whitelist.&lt;/p&gt;

&lt;p&gt;If you have to use either of these approaches, in my opinion that is symptomatic of your needs not aligning with what JWTs can provide, and it might be time to rethink your websites architecture to see if there is not a better alternative.&lt;/p&gt;

&lt;h2&gt;
  
  
  Avoiding state: Auto-expiring sessions
&lt;/h2&gt;

&lt;p&gt;A pretty common workaround for sites avoiding having to implement a stateful JWT system for logging out users is to just auto-expire tokens quickly.&lt;/p&gt;

&lt;p&gt;For example, you could either put the creation time of the JWT inside of its own payload, and/or the planned expiration time. Then, when validating incoming JWTs, simply check if the expiration time is earlier or equal to the current time - if so, reject the JWT.&lt;/p&gt;

&lt;p&gt;The problem with this is that it is still a bit of a stop-gap solution and can end up being &lt;em&gt;more&lt;/em&gt; complicated than just using sessions in the first place. In order to not have users getting constantly logged out while actively using the site (annoying!), you would need to use auto-refreshing JWTs, probably through a service like &lt;a href="https://auth0.com/docs/tokens/refresh-token/current"&gt;Auth0's Refresh Tokens&lt;/a&gt;. This still doesn't allow for a manual log-out flow, unless you also introduce revoking as part of the refresh pattern.&lt;/p&gt;

&lt;h2&gt;
  
  
  Avoiding state: Lazy invalidation
&lt;/h2&gt;

&lt;p&gt;Imagine that you need to substantially upgrade your user management system, and force everyone to switch. A workaround that is unfortunately suggested by a lot of devs online, but is less than ideal, is to... change your private key secret. If you do that, you &lt;em&gt;immediately&lt;/em&gt; invalidate &lt;strong&gt;&lt;em&gt;all&lt;/em&gt;&lt;/strong&gt; JWTs in circulation and force everyone to login again, since the signature portion of everyones' JWTs will no longer match.&lt;/p&gt;

&lt;h1&gt;
  
  
  Note on complexity, vulnerabilities, and more:
&lt;/h1&gt;

&lt;p&gt;There is still lots to JWT that I have not covered fully in this post (it's supposed to be an intro, in my excuse). The reality is that many developers use a third party service (aka a &lt;em&gt;Federated Identity Management&lt;/em&gt; service), such as &lt;a href="https://auth0.com/blog/why-identity-federation-matters/"&gt;Auth0&lt;/a&gt;, to manage the majority of the JWT stack. This won't magically solve many of the issues I've outlined with JWT, but it will make working with them easier and abstract some of the complexity and security concerns.&lt;/p&gt;




&lt;h1&gt;
  
  
  Additional resources:
&lt;/h1&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Link&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Wikipedia page - surprisingly dev focused&lt;/td&gt;
&lt;td&gt;Wiki&lt;/td&gt;
&lt;td&gt;&lt;a href="https://en.wikipedia.org/wiki/JSON_Web_Token"&gt;Wiki En&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RFC spec from IETF&lt;/td&gt;
&lt;td&gt;Spec&lt;/td&gt;
&lt;td&gt;&lt;a href="https://tools.ietf.org/html/rfc7519"&gt;RFC #7519&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JWT Debugger, Libraries, &amp;amp; More&lt;/td&gt;
&lt;td&gt;Playground &amp;amp; Quick Ref&lt;/td&gt;
&lt;td&gt;&lt;a href="https://jwt.io/"&gt;jwt.io&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;p&gt;PS: If you enjoyed how this guide was written, you might like some of my other cheatsheets and quick reference guides. I've been working on collecting and showcasing them &lt;a href="https://cheatsheets.joshuatz.com/"&gt;here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>jwt</category>
      <category>security</category>
      <category>webdev</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Coding a CSS Theme Switcher - So Many Options!</title>
      <dc:creator>Joshua Tzucker</dc:creator>
      <pubDate>Wed, 04 Sep 2019 16:16:45 +0000</pubDate>
      <link>https://forem.com/joshuatz/coding-a-css-theme-switcher-so-many-options-1cmn</link>
      <guid>https://forem.com/joshuatz/coding-a-css-theme-switcher-so-many-options-1cmn</guid>
      <description>&lt;h1&gt;
  
  
  Table of Contents:
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Intro and "What is theming?"&lt;/li&gt;
&lt;li&gt;Options:

&lt;ul&gt;
&lt;li&gt;
Method A: CSS Custom Properties

&lt;ul&gt;
&lt;li&gt;Method A-1: Using specificity to modify variable inheritance&lt;/li&gt;
&lt;li&gt;Method A-2: Reassigning variable values with Javascript&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
Method B: Using SASS/SCSS

&lt;ul&gt;
&lt;li&gt;Method B-1: Themes as nested maps&lt;/li&gt;
&lt;li&gt;Method B-2: Mixing dumping&lt;/li&gt;
&lt;li&gt;Method B-3: Pulling CSS variables into SASS&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Method C: Mutating State (React, Vue, etc.)&lt;/li&gt;
&lt;li&gt;Method D: Crazy Old-School&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Wrap up / Final Notes&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Introduction:
&lt;/h1&gt;

&lt;p&gt;Front-end web theming seems to be a current hot topic, and I have to admit that I felt the itch to jump on the bandwagon and add a dark theme to a Single-Page Application I’m working on. But where to get started? There are so many guides out there, and yet I couldn’t find many that I felt adequately talked about CSS theming in a generic way that covers many different approaches, rather than a tutorial that is essentially “copy and paste this into your code”.&lt;/p&gt;

&lt;p&gt;So I set out to make one (this post), a guide that briefly discusses each of the many available options in a broad manner, with some short snippets of example code.&lt;/p&gt;

&lt;p&gt;First though…&lt;/p&gt;

&lt;h1&gt;
  
  
  What is theming?
&lt;/h1&gt;

&lt;p&gt;In this context, CSS theming or CSS theme switching refers to a set of shared styles (colors, etc) that are grouped as a theme, and being able to switch between themes instantly on a webpage or within a SPA, without refreshing the page.&lt;/p&gt;

&lt;p&gt;A very simple example of a theme, that I use throughout this post, is as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dark theme:

&lt;ul&gt;
&lt;li&gt;Primary Color: Black&lt;/li&gt;
&lt;li&gt;Secondary Color: White&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Light Theme:

&lt;ul&gt;
&lt;li&gt;Primary Color White&lt;/li&gt;
&lt;li&gt;Secondary Color: Black&lt;/li&gt;
&lt;/ul&gt;


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

&lt;p&gt;Ok, now lets look at different options for implementation:&lt;/p&gt;

&lt;h1&gt;
  
  
  Method A: Using CSS custom properties / variables
&lt;/h1&gt;

&lt;p&gt;This is fast becoming one of the most common current approach to implementing switchable themes, and uses a newer CSS feature called "&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties"&gt;Custom Properties&lt;/a&gt;", which are often referred to as "CSS variables". In short, CSS custom properties allow you to define a variable, such as "primaryColor", which can then be referenced in hundreds of other spots throughout your CSS.&lt;/p&gt;

&lt;p&gt;This is crucial for theming, because it means that you can share a property among thousands of elements, and to change it, you just need to update that one variable value, rather than needing to individually update each element's CSS.&lt;/p&gt;

&lt;p&gt;To get into the details of how to use CSS variables to deliver switchable CSS themes, let's break this method into a few different approaches:&lt;/p&gt;

&lt;h2&gt;
  
  
  Method A-1: Using specificity to change variable inheritance
&lt;/h2&gt;

&lt;p&gt;Ok, long title, but I promise you that this method is actually pretty simple. In fact, this is one of the most common methods, and can be seen in current use across several &lt;em&gt;major&lt;/em&gt; websites. Basically, you define the default theme as a set of variables, and then override the exact same variables through another block of CSS. The override will work because we will add something specific (class or attribute) to a top level node (like &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt;) to trigger the more specific rule.&lt;/p&gt;

&lt;p&gt;Here is some example CSS, without using custom properties:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="nc"&gt;.myElement&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="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="nc"&gt;.dark&lt;/span&gt; &lt;span class="nc"&gt;.myElement&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;data-theme&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"dark"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="nc"&gt;.myElement&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="no"&gt;black&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 here is the same output, but using CSS variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;--primaryColor&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="py"&gt;--secondaryColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;black&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="nc"&gt;.myElement&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="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--primaryColor&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="nc"&gt;.dark&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;data-theme&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"dark"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;--primaryColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;black&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;--secondaryColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then you can simply toggle the class or attribute with JS, either directly with something like &lt;code&gt;.setAttribute(‘data-theme’,’dark’)&lt;/code&gt;, or with a reactive binding within something like React or Vue.&lt;/p&gt;

&lt;p&gt;Here is a quick demo using setAttribute on &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; to switch themes:&lt;br&gt;
&lt;iframe height="600" src="https://codepen.io/joshuatz/embed/xxKOZVr?height=600&amp;amp;default-tab=css,result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;You could technically do this without using CSS custom variables, but you end up with a lot of handcoding and repeating rules. For every rule/element that has different styling for a specific theme, you only have to write that block once if you are using variables, but if not, you have to write it 1 x as many themes as you have. Example below, where you can see that with three elements and three themes, the minimum number of CSS blocks without custom properties is 9 (3×3), whereas with variables, we only need 3 (one for each element):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VIgNyu1_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/8mh80wjt5kyejy095n0d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VIgNyu1_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/8mh80wjt5kyejy095n0d.png" alt="Showing handcoding custom themed elements vs using variables"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Method A-2 Re-Assign variable values with JS
&lt;/h2&gt;

&lt;p&gt;This method usually is used when you aren’t just theme switching, but also allowing for live theme property changing by the user, through something like a color picker. Let’s pretend that the user has just picked a new custom primary color for their theme, and they want to preview how it will look. Here is the initial CSS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;--primaryColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;red&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;--secondaryColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;With just a little bit of JavaScript, we can easy modify any of these values. Here we go changing the primary color:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// This will target :root variables&lt;/span&gt;
&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;:root&lt;/span&gt;&lt;span class="dl"&gt;'&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="s1"&gt;--primaryColor&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;newColor&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Or, same thing...&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;documentElement&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="s1"&gt;--primaryColor&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;newColor&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You can take this a step further and easily implement theming logic to swap out entire themes and switch between multiple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;themes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;light&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;--primaryColor&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;white&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;--secondaryColor&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;black&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;dark&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;--primaryColor&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;black&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;--secondaryColor&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;white&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;red&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;--primaryColor&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;red&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;--secondaryColor&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;white&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;function&lt;/span&gt; &lt;span class="nx"&gt;activateTheme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;prop&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;theme&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;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;:root&lt;/span&gt;&lt;span class="dl"&gt;'&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;prop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// Switch to the dark theme:&lt;/span&gt;
&lt;span class="nx"&gt;activateTheme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;themes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Demo:&lt;/p&gt;

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

&lt;p&gt;By default, when you change a CSS property via Javascript, the change is not persisted when the user reloads the page. To save their changes to a theme, you have a bunch of different options, which is beyond the scope of this post. However, here are some generic options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;On value change, also AJAX to server and save with User data

&lt;ul&gt;
&lt;li&gt;Then, when user logs in, send back theme data either as on-the-fly generated CSS, or JSON which gets parsed and mapped back to CSS custom vars&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Persist theme to localStorage and map back on page load

&lt;ul&gt;
&lt;li&gt;If you are using a state management system and storing the user's theme config there, this should be very easy to do with something like &lt;a href="https://github.com/rt2zz/redux-persist"&gt;"redux-persist"&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;You can also just inject a &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; tag into the page with overriding variable values; this is how dev.to is currently handling theming -- on page load, it injects a different set of variable values through a &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; tag, depending on the value of a localStorage value&lt;/li&gt;
&lt;/ul&gt;


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

&lt;h1&gt;
  
  
  Method B: Using SASS (Sass/Scss)
&lt;/h1&gt;

&lt;p&gt;Technically, you could use Sass to accomplish both methods A &amp;amp; B, since Sass transpiles to CSS, and you could also just stick both methods, as CSS, into Sass, and it should be valid.&lt;/p&gt;

&lt;p&gt;However, there are some Sass specific approaches for theme switching, which I’ve outlined below.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Sidenote: Throughout this post, my demos all use the SCSS style of Sass, but all of this should be possible with indented SASS; you would just need to reformat.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Method B-1: Themes as Nested Maps
&lt;/h2&gt;

&lt;p&gt;One way to approach this in Sass is with nested maps. The inspiration for this code, and one of the best guides on this method is &lt;a href="https://medium.com/@dmitriy.borodiy/easy-color-theming-with-scss-bc38fd5734d1"&gt;this post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Nested maps in Sass resemble JSON, and allow nesting of key-pair maps that can hold any valid Sass value. This makes them a great way to hold multiple named themes, and sub-properties for each theme. Here are my two example themes as a nested map saved to $themes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="nv"&gt;$themes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s1"&gt;'dark'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s1"&gt;'primary'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="no"&gt;black&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'secondary'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'light'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s1"&gt;'primary'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'secondary'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="no"&gt;black&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This by itself does not generate any CSS, so you have to write your own function/code to loop through your themes and generate the CSS you want. It is best to define re-usable helper functions to handle this, that use mixins:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
* Mixin to use to generate blocks for each theme
* Automatically takes @content
*/&lt;/span&gt;
&lt;span class="nv"&gt;$scopedTheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;@mixin&lt;/span&gt; &lt;span class="nf"&gt;themeGen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$allThemesMap&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$themes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;@each&lt;/span&gt; &lt;span class="nv"&gt;$themeName&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$themeMap&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;$allThemesMap&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;.theme-&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nv"&gt;$themeName&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="k"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Creating a map that contains values specific to theme.&lt;/span&gt;
            &lt;span class="c1"&gt;// Global is necessary since in mixin&lt;/span&gt;
            &lt;span class="nv"&gt;$scopedTheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;global&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;@each&lt;/span&gt; &lt;span class="nv"&gt;$variableName&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$variableValue&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;$themeMap&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;// Merge each key-value pair into the theme specific map&lt;/span&gt;
                &lt;span class="nv"&gt;$scopedTheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;map-merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$scopedTheme&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$variableName&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$variableValue&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;global&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="c1"&gt;// The original content passed&lt;/span&gt;
            &lt;span class="k"&gt;@content&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="c1"&gt;// Unset&lt;/span&gt;
            &lt;span class="nv"&gt;$scopedTheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;null&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;global&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="cm"&gt;/**
* Function to call within themeGen mixin, to get value from the current theme in the iterator
*/&lt;/span&gt;
&lt;span class="k"&gt;@function&lt;/span&gt; &lt;span class="nf"&gt;getThemeVal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$themeVar&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="k"&gt;@return&lt;/span&gt; &lt;span class="nf"&gt;map-get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$scopedTheme&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$themeVar&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;Finally, using this with an element:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
* Actually using theme values to generate CSS
*/&lt;/span&gt;
&lt;span class="nc"&gt;.myComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;@include&lt;/span&gt; &lt;span class="nd"&gt;themeGen&lt;/span&gt;&lt;span class="p"&gt;()&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="nf"&gt;getThemeVal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'primary'&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="nf"&gt;getThemeVal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'secondary'&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;Here is a demo showing the final CSS this can generate:&lt;br&gt;
&lt;iframe height="600" src="https://codepen.io/joshuatz/embed/aboNPLV?height=600&amp;amp;default-tab=css,result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Technically, you could hand code the CSS that this generates (and some old-school devs do), but there is not a good reason to do so, and if anything, you are more likely to write error-ridden CSS.&lt;/p&gt;

&lt;p&gt;A related, but slightly different approach (avoids global vars) can be found &lt;a href="https://www.sitepoint.com/sass-theming-neverending-story/"&gt;here&lt;/a&gt;. And &lt;a href="https://stackoverflow.com/a/47873911/11447682"&gt;here's&lt;/a&gt; another variation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Method B-2: Simple Mixin "Dump"
&lt;/h2&gt;

&lt;p&gt;This method is not as elegant as C-1, but less complicated. Essentially you dump a huge block of CSS into a mixin, which then repeats it, but with a named parent theme class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="k"&gt;@mixin&lt;/span&gt; &lt;span class="nf"&gt;scopeToTheme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$themeName&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$prop&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$val&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="nc"&gt;.&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nv"&gt;$themeName&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="k"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nv"&gt;$prop&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="nd"&gt;:&lt;/span&gt; &lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nv"&gt;$val&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;button&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="no"&gt;red&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;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;margin-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4px&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;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;@include&lt;/span&gt; &lt;span class="nd"&gt;scopeToTheme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s1"&gt;'dark-theme'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'background-color'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'black'&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;@include&lt;/span&gt; &lt;span class="nd"&gt;scopeToTheme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s1"&gt;'light-theme'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'background-color'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'white'&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And here is the CSS that this generated:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;button&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="no"&gt;red&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;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;margin-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4px&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;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.dark-theme&lt;/span&gt; &lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;black&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.light-theme&lt;/span&gt; &lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background-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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Method B-3: CSS Custom Properties to Sass
&lt;/h2&gt;

&lt;p&gt;I’m not sure I’ve seen this done anywhere, but an interesting approach could be to combine Method B with Sass by &lt;em&gt;pulling&lt;/em&gt; the CSS variables into Sass. Like so – CSS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;--primaryColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;red&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;--secondaryColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Sass:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="nv"&gt;$primaryColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;primaryColor&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$secondaryColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;secondaryColor&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h1&gt;
  
  
  Method C: Mutating State
&lt;/h1&gt;

&lt;p&gt;I won't get into the specific of how to code this out, but this is pretty easy to think about conceptually. In a reactive framework, you can hold CSS properties, such as colors, as part of &lt;code&gt;state&lt;/code&gt;. Then, using a CSS-in-JS framework, or even just sticking state in inline css, you can plug the value from state into your scoped CSS.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;React Demos:

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://codepen.io/elainehuang/pen/yPWxRX"&gt;Simple inline style binding&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://codesandbox.io/s/x26q7l9vyq"&gt;Styled components + ThemeProvider&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;React Native Web

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/necolas/react-native-web/blob/master/docs/guides/style.md"&gt;Inline style binding with "atomic" CSS classes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Vue Demos:

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://codesandbox.io/s/3no663nv6"&gt;Simple inline style binding&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


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

&lt;p&gt;It looks like Twitter uses this method, with React Native Web.&lt;/p&gt;

&lt;h1&gt;
  
  
  Method D: Crazy Old-School
&lt;/h1&gt;

&lt;p&gt;There are so many reasons not to do it this way, but I feel obligated to share this hackish idea anyways. If you really wanted to insist on using CSS, but not using any variables or touching of the &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; class or attributes, you could hand code different themes as completely separate CSS files. Like &lt;code&gt;dark-theme.css&lt;/code&gt; and &lt;code&gt;light-theme.css&lt;/code&gt;. Then, to switch the theme, you could use AJAX to pull in the new theme file. Or, if you wanted to support browsers without Javascript, you could even use server side code to change which theme gets included and force a page refresh.&lt;/p&gt;

&lt;p&gt;As a side note, an effect of this approach is actually better utilization of bandwidth, since you are only sending what theme is needed.&lt;/p&gt;

&lt;h1&gt;
  
  
  Wrap-Up / Final Thoughts:
&lt;/h1&gt;

&lt;p&gt;First, although this was an extensive look at theming options, it should not be considered fully comprehensive; there is an endless number of ways you can implement custom theming.&lt;/p&gt;

&lt;p&gt;Second, if you have made it all the way through the post, you might still be wondering "well, which option is the best one?". The truth is that &lt;strong&gt;it really matters what your objective is&lt;/strong&gt;. If your goal is to support as many browsers as possible and/or have functions within styling (like converting hex to RGBA and then darkening), then SASS is probably the way forward (Method B). Or, if you want to have the smallest CSS filesize and/or binding of CSS values to JS values, then CSS custom properties are likely the best fit (Method A and Method C, or Method B with special setup).&lt;/p&gt;

&lt;p&gt;The short answer is use what fits best with the framework or existing structure of your site/SPA/App. And have fun theming!&lt;/p&gt;




&lt;p&gt;This writeup was first published at: &lt;a href="https://joshuatz.com/posts/2019/coding-a-css-theme-switcher-a-multitude-of-web-dev-options/"&gt;https://joshuatz.com/posts/2019/coding-a-css-theme-switcher-a-multitude-of-web-dev-options/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>css</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Github Language Stats Bar for a Pure Markdown Repo</title>
      <dc:creator>Joshua Tzucker</dc:creator>
      <pubDate>Mon, 12 Aug 2019 18:38:40 +0000</pubDate>
      <link>https://forem.com/joshuatz/github-language-stats-bar-for-a-pure-markdown-repo-2ea</link>
      <guid>https://forem.com/joshuatz/github-language-stats-bar-for-a-pure-markdown-repo-2ea</guid>
      <description>&lt;p&gt;If you have ever used Github, you have probably noticed the language details statistics bar (or “language breakdown bar”):&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F3jhzlolxgpxtnqn0xq1y.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F3jhzlolxgpxtnqn0xq1y.png" alt="The Github Language Statistics Bar"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It doesn’t really have an impact on anything, but it can tell other devs at a glance what kind of tech is likely in your repo, and it does influence the search results when searching for a repo across Github.&lt;/p&gt;

&lt;p&gt;Unfortunately, if you create a repo and fill it with nothing but markdown files (.md), the language breakdown bar will either not appear at all, or if you introduce some non-markdown files, it will only show those. What gives?&lt;/p&gt;

&lt;p&gt;Both the problem and the solution are revealed with a little bit of background knowledge.&lt;/p&gt;

&lt;h1&gt;
  
  
  How is the language stats bar calculated?
&lt;/h1&gt;

&lt;p&gt;Internally, Github uses a tool called &lt;a href="https://github.com/github/linguist" rel="noopener noreferrer"&gt;Linguist&lt;/a&gt; to detect and classify code files. What you might not know though, is that Linguist also has a lot of rules for preventing a file from contributing towards statistics. A file is excluded from stats if it is classified as any of these:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Vendored-code&lt;/li&gt;
&lt;li&gt;Generated-code&lt;/li&gt;
&lt;li&gt;Documentation&lt;/li&gt;
&lt;li&gt;Data&lt;/li&gt;
&lt;li&gt;Prose&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For details, see &lt;a href="https://github.com/github/linguist#my-repository-isnt-showing-my-language" rel="noopener noreferrer"&gt;this section&lt;/a&gt; of the readme.&lt;/p&gt;

&lt;h1&gt;
  
  
  Why does Markdown not show up?
&lt;/h1&gt;

&lt;p&gt;The first reason why Markdown does not show up is because it is immediately classified as “Prose”, which we know from above, is ignored when it comes to repo language statistics.&lt;/p&gt;

&lt;p&gt;The second reason might or might not apply to you; filename and file structure. For example, if all your markdown files are named “readme.md” or are just in a folder called “Documentation”, they would be classified as “Documentation” and excluded. You can see this from the Regular Expressions patterns in the “&lt;a href="https://github.com/github/linguist/blob/master/lib/linguist/documentation.yml" rel="noopener noreferrer"&gt;documentation.yml&lt;/a&gt;” file.&lt;/p&gt;

&lt;h1&gt;
  
  
  Solution
&lt;/h1&gt;

&lt;p&gt;It used to be that there was not a solution to this issue. However, over time, Github added "overrides" to Linguist, so now users can modify the default classification of files by overriding with git attributes (see &lt;a href="https://github.com/github/linguist/issues/3964" rel="noopener noreferrer"&gt;issue #3964&lt;/a&gt;, which was solved by &lt;a href="https://github.com/github/linguist/pull/3807" rel="noopener noreferrer"&gt;PR #3807&lt;/a&gt;, and example given in &lt;a href="https://github.com/github/linguist/pull/3806" rel="noopener noreferrer"&gt;#3806&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;How do we implement overrides? It is actually surprisingly pain-free.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First&lt;/strong&gt;, create a ".gitattributes" file in the root of your repo. Then, use &lt;a href="https://git-scm.com/docs/gitattributes" rel="noopener noreferrer"&gt;the standard git attributes syntax&lt;/a&gt; within that file to modify file attributes based on filename or pattern. Git Attributes are a way to assign specific, custom, and arbitrary attributes to specific files.&lt;/p&gt;

&lt;p&gt;The linguist attributes you can override are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  linguist-documentation&lt;/li&gt;
&lt;li&gt;  linguist-langauge&lt;/li&gt;
&lt;li&gt;  linguist-vendored&lt;/li&gt;
&lt;li&gt;  linguist-generated&lt;/li&gt;
&lt;li&gt;  linguist-detectable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, here is a .gitattributes file where I have forced Github to count all Markdown files towards my language stats, &lt;em&gt;except&lt;/em&gt; for readme.md:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Linguist overrides
*.md linguist-detectable=true
README.md linguist-detectable=false
readme.md linguist-detectable=false
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Technically, I believe toggling “linguist-detectable” should be all it takes to change whether or not a file is ignored, since it acts as a final override. If you wanted to be 100% sure that markdown is not excluded, you could also override any attribute that might prevent it from being recognized, like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;*.md linguist-vendored=false
*.md linguist-generated=false
*.md linguist-documentation=false
*.md linguist-detectable=true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Final Notes:
&lt;/h1&gt;

&lt;p&gt;Keep in mind that the solution found here is applicable towards many issues with Github stats; you can easily override any Linguist classifications with Git attributes.&lt;/p&gt;

&lt;p&gt;Also, &lt;strong&gt;Pro Tip:&lt;/strong&gt; You can easily check if your file patterns are correct in your .gitattributes file by using the "git check-attr" command. For example, to check the attributes of a markdown file with the filename "web-dev.md", in my folder "cheatsheets", I would use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git check-attr -a cheatsheets/web-dev.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>github</category>
      <category>linguist</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
