<?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: Samson Zhang</title>
    <description>The latest articles on Forem by Samson Zhang (@wwsalmon).</description>
    <link>https://forem.com/wwsalmon</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%2F385167%2F1a31829b-00a3-4c75-8d83-348f41fe1a7d.jpg</url>
      <title>Forem: Samson Zhang</title>
      <link>https://forem.com/wwsalmon</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/wwsalmon"/>
    <language>en</language>
    <item>
      <title>How To Set Up Tailwind and Typescript in Next.js IN ONE STEP</title>
      <dc:creator>Samson Zhang</dc:creator>
      <pubDate>Thu, 28 Jan 2021 23:30:43 +0000</pubDate>
      <link>https://forem.com/wwsalmon/how-to-set-up-tailwind-and-typescript-in-next-js-in-one-step-nab</link>
      <guid>https://forem.com/wwsalmon/how-to-set-up-tailwind-and-typescript-in-next-js-in-one-step-nab</guid>
      <description>&lt;p&gt;&lt;strong&gt;Run &lt;code&gt;npx create-next-app [your-project-name] -e https://github.com/wwsalmon/next-tailwind-typescript-example&lt;/code&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That's it. &lt;code&gt;cd&lt;/code&gt; into your project and get to work with Typescript and Tailwind fully set up, no extra frills included.&lt;/p&gt;

&lt;p&gt;I made &lt;a href="https://github.com/wwsalmon/next-tailwind-typescript-example"&gt;&lt;code&gt;next-tailwind-typescript-example&lt;/code&gt;&lt;/a&gt; after manually setting up Typescript and Tailwind a few too many times. &lt;code&gt;create-next-app&lt;/code&gt;, Tailwind, and Next make manual set up easier than ever, but it's still tedious to do it over and over again.&lt;/p&gt;

&lt;p&gt;If you're curious, though, here's what the six-step manual setup looks like:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Run &lt;code&gt;npx create-next-app [your-project-name]&lt;/code&gt; to get a Next project created and packages installed and such.&lt;/li&gt;
&lt;li&gt;Install Typescript with &lt;code&gt;npm i typescript @types/react @types/node&lt;/code&gt;. Rename all your &lt;code&gt;js&lt;/code&gt; files to &lt;code&gt;tsx&lt;/code&gt; ones. &lt;code&gt;tsconfig.json&lt;/code&gt; and &lt;code&gt;next-env.d.ts&lt;/code&gt; files will be created when you first run &lt;code&gt;npm run dev&lt;/code&gt; or &lt;code&gt;next dev&lt;/code&gt;, so you don't have to worry about any manual config.&lt;/li&gt;
&lt;li&gt;Install Tailwind with &lt;code&gt;npm i tailwindcss postcss-preset-env -D&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a &lt;code&gt;postcss.config.js&lt;/code&gt; with the following contents:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module.exports = {
    plugins: ['tailwindcss', 'postcss-preset-env'],
} 
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a &lt;code&gt;tailwind.config.js&lt;/code&gt; with the following contents:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module.exports = {
  purge: [
    './**/*.html',
    './**/*.tsx',
  ],
  theme: {},
  variants: {},
  plugins: [],
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the following to the top of your &lt;code&gt;global.css&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@tailwind base;
@tailwind components;
@tailwind utilities;
&lt;/code&gt;&lt;/pre&gt;

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

&lt;p&gt;Now get cracking away at whatever you're building!&lt;/p&gt;

&lt;p&gt;I like to strip out &lt;code&gt;create-next-app&lt;/code&gt;'s default contents as well, i.e. deleting &lt;code&gt;Home.module.css&lt;/code&gt;, deleting the contents of &lt;code&gt;public&lt;/code&gt; and &lt;code&gt;pages/api&lt;/code&gt;, and clearing the contents of &lt;code&gt;index.js&lt;/code&gt; and &lt;code&gt;global.css&lt;/code&gt;. I also add a &lt;code&gt;_document.tsx&lt;/code&gt; file to make it easy to link Google Fonts, for example. All of these configurations are made in my example repo.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published &lt;a href=""&gt;on my blog&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>typescript</category>
      <category>tutorial</category>
      <category>react</category>
    </item>
    <item>
      <title>Rebuilding my personal website, Jekyll =&gt; Gatsby: Day 1
</title>
      <dc:creator>Samson Zhang</dc:creator>
      <pubDate>Mon, 17 Aug 2020 17:00:25 +0000</pubDate>
      <link>https://forem.com/wwsalmon/rebuilding-my-personal-website-jekyll-gatsby-day-1-pol</link>
      <guid>https://forem.com/wwsalmon/rebuilding-my-personal-website-jekyll-gatsby-day-1-pol</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8rXiRTvZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/rxw884ud2u3ipqxkzoi0.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8rXiRTvZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/rxw884ud2u3ipqxkzoi0.jpg" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I've been using Jekyll for my personal website since &lt;a href="https://github.com/wwsalmon/webtest/commit/f608147b45880aac217902f19c89b411253465a5"&gt;at least two years ago&lt;/a&gt;, when I used it to build a clean little &lt;a href="https://wwsalmon.netlify.app/"&gt;video portfolio&lt;/a&gt;. It was a huge step forward for me then, having only built raw HTML/CSS websites before, with a sprinkle of JQuery-flavored JS. Instead of hard-coding each page and item, getting really good at copy-pasting, I could auto-generate and populate pages with markdown and CSV files! Jekyll was perfect for me then, remained so when I rebuilt my website and added a blog, and is still immensely attractive today. Its blogging and micro-database (collections) features work effortlessly right out of the box; tools like Jekyll admin make managing posts as easy as using a full CMS; Liquid templating is intuitive and nonintrusive, so my raw HTML/CSS/JS skills carried over and continued to grow. Plus, it works seamlessly with GitHub pages, letting me gradually ease my way into more of the real web dev world (Netlify, Vercel, AWS, VPS's...).&lt;/p&gt;

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

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

&lt;p&gt;Once again, though, I've embarked on a quest to rebuild my website, and in the process I'm leaving Jekyll behind for Gatsby. I've never used Gatsby before, and I've only worked with React for a few months, so I'll be learning a lot in the process. In this post and several more to come, I want to document this learning and building process, &lt;a href="https://www.swyx.io/writing/learn-in-public/"&gt;"learn in public"&lt;/a&gt; as &lt;a class="comment-mentioned-user" href="https://dev.to/swyx"&gt;@swyx&lt;/a&gt;
 puts it. The goal of these posts, again borrowing &lt;a class="comment-mentioned-user" href="https://dev.to/swyx"&gt;@swyx&lt;/a&gt;
's wisdom, is to "make the thing you wish you had found when you were learning." I'll document my process, resources that I used, etc., helping me solidify my learning, and hopefully helping you gain some insight, too, whether you're just starting out with Gatsby like me or a veteran curious about a newbie's approach.&lt;/p&gt;

&lt;p&gt;(Side note: I have a few reasons and ideas for this redesign — namely: 1. I want to get hired as an engineer, 2. I want to collect and showcase my writing, and 3. this site was designed as a film/photo portfolio and consequently not optimized for either of these things — but in this sequence of blog posts, I'll just be talking through the technical side of the process).&lt;/p&gt;

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

&lt;p&gt;There are a couple of reasons why I want to use Gatsby for this rebuild:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;React and the workflow/ecosystem around it are the industry standard right now. When searching for jobs or working on a team project, there's a very good chance that React experience will be very important, so the more comfortable I can get with it, the better.&lt;/li&gt;
&lt;li&gt;Industry aside, this is a well-timed learning project for me. I only started learning React in May. I've been making rapid progress, launching a web app with a vanilla React frontend in July and building a Next.js-based frontend for a startup that hired me in August (Next.js, with its static optimization and out-of-box routing, is &lt;em&gt;so&lt;/em&gt; much nicer and easier to work with than vanilla React...). Gatsby is one more super popular React framework that I want to build at least some familiarity with, its integration with GraphQL a further learning/solidifying opportunity.&lt;/li&gt;
&lt;li&gt;As much as I love Jekyll, I'm a bit tired of Liquid templating. Working with variables, arrays, strings, any sort of data outside of nicely-structured collections and frontmatter is a pain, as anybody familiar with &lt;code&gt;{% assign %}&lt;/code&gt; and &lt;code&gt;{% capture %}&lt;/code&gt; would likely agree. I had a taste of something better with ES6 array functions and React state management, and I wanted more.&lt;/li&gt;
&lt;li&gt;Gatsby is perhaps the best React framework for a blog/personal website. It's a popular and well-supported use case, with a huge ecosystem of third-party plugins, CMS integrations, and other tools. Well-documented support for implementing blog functionality with Markdown docs and front matter in particular makes Gatsby stand out as a suitable replacement for Jekyll.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I remember reading articles about developers jumping ship from Jekyll to Gatsby even when I was just getting started with Jekyll. It's taken a bit for my React skills to catch up, but ever since they started to, the idea of rebuilding my website in Gatsby has been on my mind. But enough talking (and in my case this morning, browsing Awwwards and developer friends' websites for inspiration): let's get learning and building!&lt;/p&gt;

&lt;h1&gt;
  
  
  Getting Started
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Some git business
&lt;/h2&gt;

&lt;p&gt;My first task was just setting up a repo I could commit code to. With previous website redesigns, I had just made a new repo, either ditching the old one or manually copying the code back in. This time, I created a new &lt;code&gt;gatsby-dev&lt;/code&gt; branch, letting me keep all my historical commits in one place even after I merge and deploy my new site.&lt;/p&gt;

&lt;p&gt;Since I needed to update my live website while working on my new one — for example, publishing this blog post — I needed to keep copies of both master and dev branches on my computer. To do this, I simply re-cloned my &lt;code&gt;portfolio&lt;/code&gt; repo into a new folder, checking out into the dev branch there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting into Gatsby
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.gatsbyjs.com/docs/quick-start/"&gt;"Quick Start"&lt;/a&gt; — a reasonable place to start, right? At the &lt;em&gt;very&lt;/em&gt; beginning of learning something, though, I actually find that documentation isn't the most helpful resource, often either too slow to get a sense of things or too complicated to understand well. As an alternative, I find video tutorials to be much more effective. Watching someone walk through setting up a project, seeing all the terminal commands, file structures, and code almost as if you were doing it yourself, is, for me, the best way to quickly get a sense of how to work with a new framework or tool.&lt;/p&gt;

&lt;p&gt;Specifically, I used this crash course from Traversy Media. The video is an hour long, but I find I can usually get away with playing tutorials at 2x speed and skipping through them a bit. Traversy Media tutorials are always top-quality, and this was no different, giving me exactly the kind of walkthrough that I wanted, from the first &lt;code&gt;npm i&lt;/code&gt; to a functional blog.&lt;/p&gt;

&lt;p&gt;After the Traversy video, I got curious about hooking up a CMS or similar interface, as I wanted to find a workflow comparable to using Jekyll admin, and something I could potentially use for client projects. I had heard good things about &lt;a href="http://sanity.io"&gt;Sanity.io&lt;/a&gt;, so I did a quick search and found &lt;a href="https://www.youtube.com/watch?v=5idNI1Qpy40"&gt;a livestream&lt;/a&gt; of a developer connecting Sanity.io to their blog. This wasn't a walkthrough, so it was a little disjointed, the developer figuring things out as they went; but the understanding I came away with was that Sanity would actually be as much, probably even more, configuration and work to set up than the Gatsby blog itself. As such, I decided to table it for later. Build a functional file-based Gatsby blog first, then try to hook it up. To reference a quote that &lt;a class="comment-mentioned-user" href="https://dev.to/pliao39"&gt;@pliao39&lt;/a&gt;
 &lt;a href="https://twitter.com/pliao39/status/1285090450051870722"&gt;shared with me&lt;/a&gt;: "Make it work, then make it right."&lt;/p&gt;

&lt;h2&gt;
  
  
  Generate starter code
&lt;/h2&gt;

&lt;p&gt;Gatsby development starts by installing the Gatsby CLI with &lt;code&gt;npm i -g gatsby-cli&lt;/code&gt;. Now you can run &lt;code&gt;gatsby new [dirname]&lt;/code&gt; to generate a starter Gatsby project. I ran &lt;code&gt;gatsby new temp&lt;/code&gt; to generate the code in a new folder, copying the generated files over to my actual repo manually.&lt;/p&gt;

&lt;p&gt;Once the starter code is generated, running &lt;code&gt;gatsby develop&lt;/code&gt; should spin it up at localhost:8000. I ran into a few issues immediately, though. Running &lt;code&gt;gatsby develop&lt;/code&gt; in the newly generated temp folder spit out the command line error &lt;code&gt;Cannot find module 'gatsby-cli/lib/reporter'&lt;/code&gt;. I found &lt;a href="https://stackoverflow.com/questions/56658152/how-to-fix-gatsby-develop-error-cannot-find-module-gatsby-cli-lib-reporter"&gt;a StackOverflow thread&lt;/a&gt; offering several solutions: run &lt;code&gt;npm install --save gatsby-cli&lt;/code&gt;; use &lt;code&gt;yarn&lt;/code&gt;; delete &lt;code&gt;node_modules&lt;/code&gt; and run &lt;code&gt;npm i&lt;/code&gt; again. The last option was the simplest, so I tried it, and voila, the error went away.&lt;/p&gt;

&lt;p&gt;Copying the starter code into my repo folder once again prevented &lt;code&gt;gatsby develop&lt;/code&gt; from working for some reason, this time displaying &lt;code&gt;Error: The result of this StaticQuery could not be fetched&lt;/code&gt; in the browser. Once again, deleting &lt;code&gt;node_modules&lt;/code&gt; and re-running &lt;code&gt;npm i&lt;/code&gt; fixed the problem.&lt;/p&gt;

&lt;p&gt;With the starter code up and running, I set a goal for the night: set up all the necessary CSS and imports to replicate the navbar of my current site.&lt;/p&gt;

&lt;h1&gt;
  
  
  Configuring Gatsby
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Atomic-ish CSS
&lt;/h2&gt;

&lt;p&gt;There are tons of options for implementing styling in React, including with Gatsby. There are ready-out-of-the-box frameworks like Bootstrap and Bulma; hand-written scoped CSS-in-JS; and utility frameworks like Tailwind.css. I'm highly partial to Tailwind.css, which brings just about all of CSS, responsiveness and all, to its utility classes, making it incredibly easy and fast to style web interfaces without touching CSS directly. This was a smaller project where I had a good idea of how the website should look, so there was less of a need for rapid prototyping. Like with a previous project, then, I settled for an in-between solution: writing my own mostly utility-based, sometimes component-based, atomic-ish CSS, borrowing lots of conventions and patterns from Tailwind.&lt;/p&gt;

&lt;p&gt;For example, here are a bunch of positioning/display classes that pretty directly mirror Tailwind's:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.absolute&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.relative&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;relative&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.fixed&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;fixed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.block&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.flex&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.flex-col&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;flex-direction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;column&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.justify-center&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;justify-content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.justify-end&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;justify-content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex-end&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.items-center&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;align-items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I even wrote a script to generate padding and margin classes, exactly how Tailwind does them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:root&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;--p-1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt;&lt;span class="n"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;0.25rem&lt;/span&gt;&lt;span class="p"&gt;;}&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="nt"&gt;---p-1&lt;/span&gt;&lt;span class="o"&gt;:&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;-0.25rem&lt;/span&gt;&lt;span class="p"&gt;;}&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="nt"&gt;--p-2&lt;/span&gt;&lt;span class="o"&gt;:&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;0.5rem&lt;/span&gt;&lt;span class="p"&gt;;}&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="nt"&gt;---p-2&lt;/span&gt;&lt;span class="o"&gt;:&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;-0.5rem&lt;/span&gt;&lt;span class="p"&gt;;}&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="nt"&gt;--p-3&lt;/span&gt;&lt;span class="o"&gt;:&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;0.75rem&lt;/span&gt;&lt;span class="p"&gt;;}&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="c"&gt;/* ... */&lt;/span&gt;

    &lt;span class="nt"&gt;--mx-24&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;margin-left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;6rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nl"&gt;margin-right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;6rem&lt;/span&gt;&lt;span class="p"&gt;;}&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="nt"&gt;---mx-24&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;margin-left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;-6rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nl"&gt;margin-right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;-6rem&lt;/span&gt;&lt;span class="p"&gt;;}&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="nt"&gt;--mx-32&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;margin-left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;8rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nl"&gt;margin-right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;8rem&lt;/span&gt;&lt;span class="p"&gt;;}&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="nt"&gt;---mx-32&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;margin-left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;-8rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nl"&gt;margin-right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;-8rem&lt;/span&gt;&lt;span class="p"&gt;;}&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="c"&gt;/* ... */&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.p-1&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;@apply&lt;/span&gt; &lt;span class="err"&gt;--p-1;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.-p-1&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;@apply&lt;/span&gt; &lt;span class="err"&gt;---p-1;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.p-2&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;@apply&lt;/span&gt; &lt;span class="err"&gt;--p-2;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.-p-2&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;@apply&lt;/span&gt; &lt;span class="err"&gt;---p-2;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* ... */&lt;/span&gt;

&lt;span class="nc"&gt;.mx-24&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;@apply&lt;/span&gt; &lt;span class="err"&gt;--mx-24;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.-mx-24&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;@apply&lt;/span&gt; &lt;span class="err"&gt;---mx-24;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.mx-32&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;@apply&lt;/span&gt; &lt;span class="err"&gt;--mx-32;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.-mx-32&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;@apply&lt;/span&gt; &lt;span class="err"&gt;---mx-32;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* ... */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But there were also some higher-level classes sprinkled in, that are much easier to implement in a stylesheet than with repeated long strings of Tailwind-esque utility classes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.container&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;75rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* 1500px */&lt;/span&gt;
    &lt;span class="err"&gt;@apply&lt;/span&gt; &lt;span class="err"&gt;--mx-auto;&lt;/span&gt;
    &lt;span class="err"&gt;@apply&lt;/span&gt; &lt;span class="err"&gt;--px-4;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.name-lg&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;42px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;line-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.53&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.name-sm&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;28px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;600px&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="nc"&gt;.name-lg&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;56px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;-12px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nc"&gt;.name-sm&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;36px&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;I also took one more thing from Tailwind: Preflight, a light modification on the popular Normalize.css, designed to "smooth over cross-browser inconsistencies and make it easier for you to work within the constraints of your design system." (&lt;a href="https://tailwindcss.com/docs/preflight"&gt;Tailwind docs&lt;/a&gt;) The micro-framework removes margins on various content blocks (&lt;code&gt;p&lt;/code&gt;, &lt;code&gt;h1&lt;/code&gt;, &lt;code&gt;blockquote&lt;/code&gt;, etc.), makes headings and lists unstyled, and makes images block-level, among other small things; I added further small changes to reset styling on buttons, allowing me to make them look however I wanted while preserving the accessibility and usability benefits of using HTML buttons. &lt;/p&gt;

&lt;h2&gt;
  
  
  Global CSS + PostCSS in Gatsby
&lt;/h2&gt;

&lt;p&gt;It seems like every React framework has a slightly different way to add global CSS. In Vanilla React, you can add a good old &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; tag in &lt;code&gt;public/index.html&lt;/code&gt; or an import in &lt;code&gt;src/app.js&lt;/code&gt; or any other high level component. Next.js forces you to import them in &lt;code&gt;pages/_app.js&lt;/code&gt;. The recommended way in Gatsby seems to be importing CSS files into a root-level file called &lt;code&gt;gatsby-browser.js&lt;/code&gt;, which, I'll be honest, I have no idea what it actually does — but it works! My imported CSS becomes global app-wide CSS, and I can use the utility classes that I built out.&lt;/p&gt;

&lt;p&gt;You might have noticed, though, that there are a bunch of &lt;code&gt;@apply&lt;/code&gt;s in my CSS. Naively, I thought that this was a natively usable feature of CSS. After seeing &lt;code&gt;invalid property&lt;/code&gt; in both Chrome and Firefox, I did a little more research, only to find that the &lt;code&gt;@apply&lt;/code&gt; directive has been essentially deprecated, never making it past the draft phase and consequently supported by exactly zero browsers. I was used to using it before, i.e. with Tailwind, because my previous projects had been set up with PostCSS, so I went about setting up PostCSS with Gatsby.&lt;/p&gt;

&lt;p&gt;Thanks to Gatsby's plugin ecosystem, this was super easy to do. I ran &lt;code&gt;npm i gatsby-plugin-postcss&lt;/code&gt;, then &lt;code&gt;npm i postcss-apply&lt;/code&gt;, finally adding both PostCSS and the postcss-apply plugin to Gatsby by adding the following object in the &lt;code&gt;plugins&lt;/code&gt; array in &lt;code&gt;gatsby-config.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`gatsby-plugin-postcss`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;postCssPlugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`postcss-apply`&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this done, I could now reference the rules I put in &lt;code&gt;:root&lt;/code&gt; in the rest of my CSS file, for example &lt;code&gt;.container&lt;/code&gt; containing directives &lt;code&gt;@apply --mx-auto&lt;/code&gt; and &lt;code&gt;@apply --px-4&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding Google Fonts
&lt;/h2&gt;

&lt;p&gt;Adding Google Fonts is again an easy task thanks to a Gatsby plugin called &lt;code&gt;gatsby-plugin-google-fonts&lt;/code&gt;. I simply &lt;code&gt;npm i&lt;/code&gt;'d it, then added the following snippet to &lt;code&gt;gatsby-config.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`gatsby-plugin-google-fonts`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;fonts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="s2"&gt;`DM Sans\:400,400i,500,500i,700,700i`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s2"&gt;`DM Serif Display\:400,400i`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s2"&gt;`DM Serif Text\:400,400i`&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;swap&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;I've been trying to use TypeScript (JavaScript with types, with full backwards compatibility; it's pretty intuitive) as much as possible. Conveniently, one of the three pages in Gatsby's starter code was titled "Using TypeScript," informing me that "Gatsby supports TypeScript by default!"&lt;/p&gt;

&lt;p&gt;Getting TypeScript set up was accordingly simple. I ran &lt;code&gt;npm i typescript&lt;/code&gt;, then &lt;code&gt;tsc --init&lt;/code&gt;, creating a &lt;code&gt;tsconfig.json&lt;/code&gt; file in the root directory. The file is flooded with commented-out options, with a few defaults enabled. The only change I had to make was un-commenting &lt;code&gt;"jsx": "react"&lt;/code&gt; to get .tsx files working.&lt;/p&gt;

&lt;h1&gt;
  
  
  Building the navbar
&lt;/h1&gt;

&lt;p&gt;Now, everything was set up for me to be able to fully replicate then navbar from my old website (in reality, I jumped straight into trying to build out the navbar, adding configuration and CSS as I went).&lt;/p&gt;

&lt;p&gt;My navbar is pretty straightforward: a little nameplate on the left, and a list of links on the right. There are two little fancy things on top of this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dpAljdEZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/jja9vti1gic69n8w79ke.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dpAljdEZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/jja9vti1gic69n8w79ke.gif" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Once scrolled past a certain point, the "samson/zhang" nameplate shrinks to "s.zhang".&lt;/li&gt;
&lt;li&gt;On mobile, the list of links is replaced by a right-side hamburger menu.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The nameplate font size also shrinks slightly on mobile.&lt;/p&gt;

&lt;p&gt;So, I created a &lt;code&gt;navbar.tsx&lt;/code&gt; component with a functional React component within it. The component has two boolean state variables: &lt;code&gt;scrolled&lt;/code&gt; and &lt;code&gt;expanded&lt;/code&gt;, corresponding to the nameplate shrink and menu pop-out, respectively. Since I have to render the list of links in two places, I made an &lt;code&gt;items&lt;/code&gt; array of objects with &lt;code&gt;text&lt;/code&gt; and &lt;code&gt;to&lt;/code&gt; properties (I could avoid this by cleverly changing classes around, and would/have in projects where referencing a common array would be troublesome, but with how nice React state management and re-rendering is, it's way cleaner to write the loop twice here).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;Navbar&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;expanded&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setExpanded&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="o"&gt;&amp;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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;scrolled&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setScrolled&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="o"&gt;&amp;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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Home&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;About&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/about&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To handle scrolling, I added a simple event listener and handler function. The handler simply sets &lt;code&gt;scrolled&lt;/code&gt; to true or false depending on whether the window has scrolled more than 50 pixels down.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;scroll&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handleScroll&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;handleScroll&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;setScrolled&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scrollY&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;handleScroll&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;removeEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;scroll&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handleScroll&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I added this event listener in &lt;code&gt;useEffect&lt;/code&gt; and return a cleanup function to remove it so that the listener is cleanly added on component mount and removed on dismount. Not that the navbar would be mounting much or dismounting...ever, but important to make sure that a new listener isn't being added on each render cycle. This is a pattern I got quite comfortable with when working with an authentication library that fired events to indicate login success or failures.&lt;/p&gt;

&lt;p&gt;Now, the actual markup. The container div is a straightforward white bar, with a max width and padding set by &lt;code&gt;.container&lt;/code&gt;. It's set to &lt;code&gt;position: sticky&lt;/code&gt; with some margin above it, so the page scrolls a little before it latches on to the top.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;
    &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sticky&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"container flex h-16 mt-16 mb-16 items-center bg-white"&lt;/span&gt;
&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        ...
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You might notice that those utility classes, other than &lt;code&gt;.container&lt;/code&gt;, are Tailwind classes that I replicated verbatim — what can I say, I really love Tailwind. I set &lt;code&gt;position: sticky&lt;/code&gt; and &lt;code&gt;top: 0&lt;/code&gt; in an inline style because I don't anticipate using these properties much elsewhere.&lt;/p&gt;

&lt;p&gt;Here's the nameplate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt; &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"hover-light"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;scrolled&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"font-display name-sm relative"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;s.zhang&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"font-display name-lg relative"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            samson&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;br&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;zhang
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Within a link tag linking home (how Gatsby handles links with its router; a flat &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; tag would cause the new page to load anew, while Gatsby loads new content and changes the URL without reloading the page), I display either the long or short version of my nameplate depending on whether the page has been scrolled.&lt;/p&gt;

&lt;p&gt;Here there are a few non-Tailwind classes. &lt;code&gt;hover-light&lt;/code&gt; adds &lt;code&gt;opacity: 0.6&lt;/code&gt; on hover; &lt;code&gt;font-display&lt;/code&gt; sets the font to DM Serif Display; and &lt;code&gt;name-sm&lt;/code&gt; and &lt;code&gt;name-lg&lt;/code&gt; handle some subtle font size changing, as I showed before.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.name-lg&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;42px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;line-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.53&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.name-sm&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;28px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;600px&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="nc"&gt;.name-lg&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;56px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;-12px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nc"&gt;.name-sm&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;36px&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's the right-side list of links for non-mobile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex ml-auto hidden flex-sm"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"ml-6"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt;
                &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"hover-light font-bold"&lt;/span&gt;
                &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's another flex container, pushed to the right side with &lt;code&gt;ml-auto&lt;/code&gt;. Within the container, each item in items is mapped to a simple link. Following mobile-first design patterns, these links are hidden by default, overridden at &lt;code&gt;min-width: 600px&lt;/code&gt; by setting &lt;code&gt;display&lt;/code&gt; to &lt;code&gt;flex&lt;/code&gt;. This is implemented through those Tailwind-esque classes that you see, &lt;code&gt;hidden&lt;/code&gt; and &lt;code&gt;flex-sm&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.hidden&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.block&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.flex&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;600px&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="nc"&gt;.hidden-sm&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nc"&gt;.block-sm&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nc"&gt;.flex-sm&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The menu button is the other way around, appearing only at mobile resolution: &lt;code&gt;block&lt;/code&gt; or &lt;code&gt;flex&lt;/code&gt; by default but &lt;code&gt;display: none&lt;/code&gt; at &lt;code&gt;min-width: 600px&lt;/code&gt;. Clicking the button sets &lt;code&gt;expanded&lt;/code&gt; to true, opening up the menu.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;
    &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"ml-auto hidden-sm hover-light font-bold"&lt;/span&gt;
    &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;setExpanded&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="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex items-center"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;FaBars&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"mr-2"&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt; Menu
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;&amp;lt;FaBars&amp;gt;&lt;/code&gt; here is a hamburger menu icon from FontAwesome, nicely wrapped by a package called &lt;code&gt;react-icons&lt;/code&gt;. The icon renders as an SVG, requiring a &lt;code&gt;flex&lt;/code&gt; parent container and &lt;code&gt;margin-right&lt;/code&gt; to display nicely next to the button text.&lt;/p&gt;

&lt;p&gt;The opening and closing of the menu is implemented through margins. The menu width is set to 12rem, &lt;code&gt;display: fixed&lt;/code&gt; with &lt;code&gt;top&lt;/code&gt; and &lt;code&gt;right&lt;/code&gt; set to 0. By default, &lt;code&gt;margin-right&lt;/code&gt; is set to -12rem, hiding the menu to the right of the screen. When &lt;code&gt;expanded&lt;/code&gt; is true, &lt;code&gt;margin-right&lt;/code&gt; resets to 0, bringing the menu into view. One peculiarity is that the shadow on the menu is also only applied when expanded, to prevent there from being a persistent shadow on the right edge of the screen. A CSS transition property makes everything go fluidly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;
    &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`fixed bg-white w-48 flex flex-col justify-center px-8 &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;expanded&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mr-0 shadow-2xl&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;-mr-48&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;100%&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;all 0.2s ease&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;
        &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"absolute mr-8"&lt;/span&gt;
        &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;6rem&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="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;setExpanded&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="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;FaTimes&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"ml-auto my-2"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt;
                &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"hover-light font-bold"&lt;/span&gt;
                &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Within the menu is a button to close the menu (again using FontAwesome), and the same &lt;code&gt;items&lt;/code&gt; map as before, with slightly different CSS properties.&lt;/p&gt;

&lt;p&gt;And with that, plus tweaking around with positioning and styling, I had fully replicated the navbar from my old site! Here's a comparison (new on left, old on right):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yB8Jd1R8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/u8392crjdzar6k6muh8y.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yB8Jd1R8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/u8392crjdzar6k6muh8y.jpg" alt="Alt Text"&gt;&lt;/a&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SIqWXJ4I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/82nfqm242uz5u2pmgmh9.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SIqWXJ4I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/82nfqm242uz5u2pmgmh9.jpg" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion (of day 1)
&lt;/h1&gt;

&lt;p&gt;On its own, building this navbar is a pretty trivial task. Even still, comparing it to the technical implementation of my old site reveals a ton of growth already. My old CSS used selectors like &lt;code&gt;.home-header&lt;/code&gt; and worked exclusively in pixel units. I didn't know how to use &lt;code&gt;position: sticky&lt;/code&gt; (or maybe it hadn't been supported by browsers yet), so I used JS to apply a &lt;code&gt;.sticky&lt;/code&gt; class to the navbar on scroll, making it stick to the top of the page. Now I'm working with clean, maintainable atomic (ish) CSS in responsive rem units, and using React component lifecycles and state management to handle interactivity (although in this case, extremely limited interactivity).&lt;/p&gt;

&lt;p&gt;In terms of my impressions of Gatsby — at the end of the day, it's good old React, with its own twist and ecosystem. I actually &lt;a href="https://twitter.com/wwsalmon/status/1283256503307296770"&gt;compared Next.js to Jekyll when first learning&lt;/a&gt; it, with its file structure and static-optimized build process. Gatsby, too, feels very Jekyll-esque, sharing a similar file structure and static build process as Next.js. Setup feels more JS-config-file-heavy in Gatsby, with its  four root config JS files, while it's more template-y and thus Jekyll-y with Next.js, with its nice &lt;code&gt;_app.js&lt;/code&gt; and &lt;code&gt;_document.js&lt;/code&gt; files. On the flip side, Gatsby has an ecosystem of super easy-to-use plugins. I haven't really experienced the differentiating features of either framework yet — SSR on Next.js, markdown-based page generation and &lt;code&gt;gatsby-image&lt;/code&gt; in Gatsby — so I don't have an opinion either way; I'll continue to use these tools and get a better sense of them.&lt;/p&gt;

&lt;p&gt;Compared to Jekyll, though, I am somewhat glad that I didn't try to jump into Gatsby when I was just getting started. It took me a months-long, slow ease, from d3.js to React to Next.js, before I found my way here. At the end of the day, Gatsby is a React framework; it takes a solid React foundation to make use of it well.&lt;/p&gt;

&lt;p&gt;In terms of this redesign project, I now have the tools to build nearly any purely static part of my website, my familiar CSS and React at my fingertips. There's plenty more Gatsby-specific work to be done with setting up the blog and connecting it up to a CMS, and then actually designing and building out the site beyond that, but it's exciting to build and learn! I probably won't work on my website every day, with a bunch of other projects to juggle, but I'll keep sharing my progress as I go. Learn, build, share, repeat.&lt;/p&gt;

</description>
      <category>gatsby</category>
      <category>react</category>
      <category>webdev</category>
    </item>
    <item>
      <title>From fearing React to PH webapp launch in 2 months!</title>
      <dc:creator>Samson Zhang</dc:creator>
      <pubDate>Sat, 11 Jul 2020 16:34:15 +0000</pubDate>
      <link>https://forem.com/wwsalmon/from-fearing-react-to-ph-webapp-launch-in-2-months-4d58</link>
      <guid>https://forem.com/wwsalmon/from-fearing-react-to-ph-webapp-launch-in-2-months-4d58</guid>
      <description>&lt;p&gt;Here's a quick pitch for the app before the story. I would really appreciate it if you could give it some love over on PH! 🤗&lt;/p&gt;

&lt;p&gt;--&lt;/p&gt;

&lt;p&gt;Whether it's to build in public, keep track of work for reports, or just to learn and reflect better, we could all benefit from logging our projects. SZ Project Tracker makes keeping dev logs or learning journals as easy as posting to Twitter 💨&lt;/p&gt;

&lt;p&gt;📝 Write in markdown, drop images in&lt;br&gt;
🌎 Make projects &amp;amp; updates public in just a click&lt;/p&gt;

&lt;p&gt;▶▶▶ &lt;a href="https://producthunt.com/posts/sz-project-tracker"&gt;https://producthunt.com/posts/sz-project-tracker&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;--&lt;/p&gt;

&lt;p&gt;Four months ago, I didn't know React or any other JS framework. In fact, having done tons of static web dev work, I was terrified of React. How do you make an entire website in JavaScript??&lt;/p&gt;

&lt;p&gt;Still, I wanted to go beyond making landing pages and WordPress templates. I made several attempts at learning React and some backend stuff like the MERN stack. In theory, I know how to spin up a MERN app with a CRUD API and all, but none of these tutorials really stuck with me. I was left not knowing how to deploy the local apps and servers I had built, not knowing how to extend it into something usable.&lt;/p&gt;

&lt;p&gt;Eventually, I found my way to serverless-stack.com. This was a huge tutorial that teaches you how to set up an AWS backend (Cognito, DynamoDB, Lambda proxy API), as well as a React frontend connected through it. It didn't teach how to use these tools so much as just use them, throwing new functions and AWS services at you every other page. This is how I learn best, though -- from blitzing through full React page after React page, I started to pick up a better intuition than I had from any tutorial before. I finished the tutorial series in about a week and a half in May.&lt;/p&gt;

&lt;p&gt;Throughout the process, I tracked my learning and progress in a markdown file committed to the repo. I did the same for another project. It was really useful to have screenshots and code snippets from previous versions to reference in blog posts, or just for personal reflection. Using a nice, lightweight markdown editor was the best solution I found for jotting down these quick updates, but I kept thinking about how nice it would be to have an app properly designed for this. I looked online, but surprisingly couldn't find one. Well, good thing I've been learning how to make one!&lt;/p&gt;

&lt;p&gt;So that's the project that I jumped into: an app to track your projects, making dev and learning logs 10x easier to keep than with a traditional or markdown document editor. I actually ditched the Serverless framework as I was taught in serverless-stack, instead using AWS Amplify CLI to super quickly get all my services up and running; still, serverless-stack served as my main guide, for backend as well as frontend reference.&lt;/p&gt;

&lt;p&gt;At some point in the project, I outgrew serverless-stack for both frontend and backend. Frontend came first. Serverless-stack used class components, while newer tutorials used functional components; my code was a hodgepodge of both at the beginning. Josh Kaufman says that, contrary to the 10,000 hours of learning =&amp;gt; expert rule, you can learn something &lt;em&gt;reasonably&lt;/em&gt; well in 20 hours (&lt;a href="https://www.youtube.com/watch?v=5MgBikgcWnY"&gt;https://www.youtube.com/watch?v=5MgBikgcWnY&lt;/a&gt;). I remember almost tangibly &lt;em&gt;feeling&lt;/em&gt; this kind of 20 hour mark. I split off my code into components and lib functions, refactored class components into functional ones with confidence. A partner joined the team, and I was surprised to realize how much better I could recognize good or bad React code in PRs just weeks after not knowing React at all.&lt;/p&gt;

&lt;p&gt;Backend came much later -- really in the last week, even. I was scared of the Amplify CLI. There were so many config files, with so many parameters and variables. Every push I made came with the warning that things might break, that data might be lost. So I worked with the backend as little as I could at first. I set up auth, storage, API, and just left it; API schema updates were all I dared to run. I couldn't run away forever, though. Two features, both procrastinated until this very last week, forced me to actually learn. One was making images attached to public updates publicly viewable, as they were uploaded into private S3 buckets. My nicely protective Amplify functions weren't enough for this. Now I had to deal with ACLs, using the full AWS SDK, dealing with credentials and auth tokens directly -- diving straight into the backend config stuff that I feared most. The second feature was adding users to a Mailchimp mailing list on signup. Mailchimp has a nice API, but it can't be called on the clientside, so I had to set up a Lambda function for it. I decided to do this on pre-launch day. "This should be quick," I thought at 2PM; at 11:20PM, having coded discreetly through a meeting, it finally seemed to work. I asked a friend to sign up with her Google account, and felt a huge sense of relief when her email popped up in the Mailchimp audience list.&lt;/p&gt;

&lt;p&gt;Progress on this project was quite sporadic, as side-projects often are. I would blitz forwards for a week, knocking out feature after feature and squashing bug after bug, then the rest of my life would catch up to me and I would take a lull. When a partner and mentor joined the team, progress actually slowed down; caught in thinking about containerized environments, CI/CD, delegating work, etc., I lost the motivation that came from tangible progress. I was also consistently working in unfamiliar territory. I had no idea how to solve or even approach many of the problems I came up against; I would stare for hours at documentation and tutorial videos, writing test functions, my frustration growing as I didn't seem to make an inch of progress. Out of many, many repeated such experiences, though, I found that this frustrated deadlock often &lt;em&gt;did&lt;/em&gt; eventually result in learning and progress. "Micro-framework: let yourself be stuck on things for three hours. Stare at tutorials, write test functions, don't make progress; struggle &amp;amp; learn and eventually it just might click," I tweeted after one session (&lt;a href="https://twitter.com/wwsalmon/status/1280620786664038405"&gt;https://twitter.com/wwsalmon/status/1280620786664038405&lt;/a&gt;). One potential implication, then, is that this kind of learning and from-scratch skill building can't be done in small chunks, but rather require continuous long blocks of time. (Not entirely sure if this is true, maybe it would actually be sped up if broken apart? To be tested, but the value of long continuous blocks of work time is a popular idea)&lt;/p&gt;

&lt;p&gt;One day, I was tired of this project sitting around, draining me of my energy as the rest of my life and what I could be doing flew by.&lt;/p&gt;

&lt;p&gt;"We're gonna launch in two weeks, by Wednesday, July 8," I told my partner. Planning out features, we frequently said things like, "this isn't necessary for v1, let's implement it for v2." It's the classic "cut scope rather than extending the deadline" -- and it worked. I had a target. My energy was back. I refactored code, redesigned UIs, and added small missing features like crazy, in the course of four or five days sprinting across the remaining distance to a mostly functional, fairly reliable app.&lt;/p&gt;

&lt;p&gt;An online community/program I've been a part of, Summer of Shipping (&lt;a href="https://summerofshipping.com/"&gt;https://summerofshipping.com/&lt;/a&gt;), has demo opportunities at meetings every Thursday. My goal was to demo that Thursday; I grabbed a few screenshots, wrote some copy, and put together a quick landing page (so easy with @rmrm's a17t and Tailwind CSS). My demo was full of bugs (Google sign-in redirected to localhost instead of live site, Twitter URL opened as relative instead of absolute...everything goes wrong in the demo), but it was mostly done and out there!&lt;/p&gt;

&lt;p&gt;The idea of a ProductHunt launch was really only in the back of my mind until pretty late on. I had previously launched a Chrome extension, YouTube Liberation (&lt;a href="https://www.producthunt.com/posts/youtube-liberation"&gt;https://www.producthunt.com/posts/youtube-liberation&lt;/a&gt;) on PH. My friends and everyone I reached out to raved about how useful the extension was, but after hyping myself up, greating a fancy animated thumbnail, choosing the day to post to get maximum views, I got all of...three upvotes. For this project, my expectations were basically non-existant. I built it for myself; I hadn't showed many other people, nor did anyone seem especially like they were jumping for it. Still, I had pretty much already made everything I needed when making the landing page. It'd be minimal effort to just hop in Illustrator, turn these into graphics, and toss it up on PH, I thought. This time I at least have the Summer of Shipping network -- maybe it'll get a few signups.&lt;/p&gt;

&lt;p&gt;Yesterday morning, I fixed the bugs from the demo, filled out the PH fields, and was about to hit launch when I remembered that PH allowed you to launch through a hunter. It just so happened that &lt;a class="comment-mentioned-user" href="https://dev.to/swyx"&gt;@swyx&lt;/a&gt;
, senior dev advocate at AWS working on Amplify and a huge advocate for the "build in public" mentality that Summer of Shipping and my project tracker alike build upon, had given a talk at Thursday's SoS meeting and seen my demo. I messaged him on Twitter, sending over my prepared slides, asking him if he would be willing to hunt the product for me. I wasn't sure if he would -- were my product, my hastily assembled marketing materials good enough? Would he harshly dismiss me? No! He said yes!&lt;/p&gt;

&lt;p&gt;I sent him some more materials. He sent me...bugs that he encountered when he signed up for the app. "Today will be a bit of a bug squashing marathon 😅," I messaged him. "Better prelaunch than postlaunch," he replied.&lt;/p&gt;

&lt;p&gt;I had a bunch of other work I was planning to yesterday. For a design job, I needed to put together mockups by Saturday; for an entrepreneurship program, conduct a bunch of customer interviews. Instead of doing this, I ended up spending the whole day fixing bugs and making minor improvements. I changed the url from the abysmal sz-project-tracker-v0.netlify.app to the slightly less abysmal szpt.netlify.app. I wanted a mailing list of people who signed up, so I made a Mailchimp account and looked into their API. This, oh god, this was 8 hours straight of the standstill blundering-my-way-through learning that I had talked about earlier.&lt;/p&gt;

&lt;p&gt;Everything was bashed out. The PH page was ready. At midnight, it went live. I woke up at 8:30 AM this morning. The PH page had 8 upvotes. Already broke my 3 upvote record, lmao...but I thought I could do better. I posted on Twitter; I asked &lt;a class="comment-mentioned-user" href="https://dev.to/swyx"&gt;@swyx&lt;/a&gt;
, &lt;a href="https://twitter.com/coolnalu"&gt;@irid&lt;/a&gt;, and some others to repost; ngl, I intended for this &lt;del&gt;IndieHackers&lt;/del&gt;DEV.to post right here to be mostly promotional, but it's turned into a much more substantial story as I've been writing it, I think I'll post it as a blog post too 😛 Right now it has 29 upvotes, but...just dropped from 13th to 14th on the homepage?? Before you keep reading, if you haven't already, help me out, guys! Here's the link again:&lt;/p&gt;

&lt;p&gt;▶▶▶ &lt;a href="https://producthunt.com/posts/sz-project-tracker"&gt;https://producthunt.com/posts/sz-project-tracker&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Upvote! Comment! Sign up! Do your thing.&lt;/p&gt;

&lt;p&gt;Anyhow -- that's where we are now. Two full months of learning and building, from being terrified of JavaScript and writing about the death of hand-written CSS to falling in love with React and CSS frameworks like Tailwind and a17t.&lt;/p&gt;

&lt;p&gt;Other than technical skills, what have I learned? I think two big ideas have been heavily affirmed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;"Do to learn, not to do." This is a long-held philosophy of mine, and it's really been the driving force for getting to where I am today. I didn't go to any bootcamps or follow any curriculums in the past two months, but I've made myself so much more valuable by just diving in and trying to make things that I care about (my GitHub got me hired as frontend developer at an early-stage startup!).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Prioritize connections, people, and communities. Did going to Summer of Shipping talks about the inner workings of authentication or HTTP/CORS help me build my app? No, but it gave me a group of people to be a part of; my work provided value for the community, so mentors could invest their time in me. Indirectly, SoS had a huge impact on my network too, by pushing me to get on Twitter, and creating opportunities for engagement through mentors' and the community's accounts. Through SoS and more broadly on Twitter, I've connected with so many amazing people, building a network of experts and industry professionals as well as people working on their own projects and careers like me, who have kept me going and offered incredible insights and advice when I reached out to them.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These, really, are my two superweapons. They've empowered me so much; the realm of possibilities in front of me seems unbounded. What's next for me? We'll see how szpt goes, maybe I'll work on it a bit more. In the longer term,  I have an amazing mentor pushing me to find something that I would want to work on for the next 5-7 years of my life; I'm chasing lots of other opportunities to keep learning, doing, connecting.&lt;/p&gt;

&lt;p&gt;I hope you enjoyed reading this post, and one last time, here's the product pitch (GO UPVOTE):&lt;/p&gt;

&lt;p&gt;Whether it's to build in public, keep track of work for reports, or just to learn and reflect better, we could all benefit from logging our projects. SZ Project Tracker makes keeping dev logs or learning journals as easy as posting to Twitter 💨&lt;/p&gt;

&lt;p&gt;📝 Write in markdown, drop images in&lt;br&gt;
🌎 Make projects &amp;amp; updates public in just a click&lt;/p&gt;

&lt;p&gt;▶▶▶ &lt;a href="https://producthunt.com/posts/sz-project-tracker"&gt;https://producthunt.com/posts/sz-project-tracker&lt;/a&gt; (CLICK IT AND PRESS THAT ORANGE BUTTON)&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
    </item>
    <item>
      <title>The most important step in web design happens before you push a single pixel</title>
      <dc:creator>Samson Zhang</dc:creator>
      <pubDate>Thu, 25 Jun 2020 16:25:29 +0000</pubDate>
      <link>https://forem.com/wwsalmon/the-most-important-step-in-web-design-happens-before-you-push-a-single-pixel-575p</link>
      <guid>https://forem.com/wwsalmon/the-most-important-step-in-web-design-happens-before-you-push-a-single-pixel-575p</guid>
      <description>&lt;p&gt;&lt;em&gt;This post was originally published on &lt;a href="https://www.samsonzhang.com/2020/06/25/the-most-important-step-in-web-design-happens-before-you-push-a-single-pixel.html"&gt;my blog&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This post's claim: &lt;strong&gt;client research is the most important step to take when designing a website&lt;/strong&gt;. You should do it every time, before you make a single sketch or mockup, before you touch a single line of code. Your clients will be happier, your designs will be prettier, you'll become a better designer.&lt;/p&gt;

&lt;p&gt;That's the actionable, practical form of the advice that I'm attempting to give. The underlying idea is the importance of design thinking. More than building pretty websites or graphics, design is about knowing as well as you can the problem that you want to solve or the message that you want to communicate. Only knowing this can your hands-on skills in Illustrator or CSS really come into use, producing valuable and ultimately visually appealing designs.&lt;/p&gt;

&lt;p&gt;This was a realization I've come to with a few recent web design/development projects, notably redesigning and rebuilding &lt;em&gt;The Phillipian&lt;/em&gt;'s website, &lt;a href="https://phillipian.net/"&gt;phillipian.net&lt;/a&gt;; in this post, I'll be using the simple website I built for food-rescue non-profit &lt;a href="http://stem4free.org/"&gt;Stem4Free&lt;/a&gt; as an example.&lt;/p&gt;

&lt;h2&gt;
  
  
  Doing it wrong
&lt;/h2&gt;

&lt;p&gt;A couple of my friends started Stem4Free in April. The organization worked to rescue food from restaurants and businesses that would otherwise go to waste, donating them instead to food pantries, homeless shelters, nursing homes, hospitals, etc.&lt;/p&gt;

&lt;p&gt;I joined Stem4Free as what else but the website design team manager, except there wasn't anyone to manage, so it was just me working on the website. The WordPress website that one of the founders had put together used Elementor and a ton of other plugins. My job was to make it...better. I determined that a complete overhaul was in order, getting rid of Elementor and building a template from scratch. (After phillipian.net, this was a familiar task...)&lt;/p&gt;

&lt;p&gt;So I got to work, playing around with the existing logo, pulling colors and trying font combinations:&lt;/p&gt;

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

&lt;p&gt;For the website, I interpreted my task as making the website, well, look nicer. I looked through a bunch of other nonprofits' websites, looked over the copy on the existing website, and threw together a mockup in Illustrator:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--aOYHgP4m--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/w4rcy4mi6hpctfp7ceep.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aOYHgP4m--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/w4rcy4mi6hpctfp7ceep.jpg" alt="Mockup of v1 of website"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This was, more or less, the website that I built:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zD8_Ufeu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/72wep4a3bnaf7txcq2n1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zD8_Ufeu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/72wep4a3bnaf7txcq2n1.jpg" alt="Live v1 of website"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Alright, my WordPress/frontend skills are still pretty solid. Those blocks on the front page even use some lightly-styled Gutenberg blocks with a Custom Post Type to make them easy to update!&lt;/p&gt;

&lt;p&gt;My design thinking, on the other hand, was taking a deep nap. Everything about this website, from the design process to the mockups, should have set off alarm bells. The visual hierarchy is hopelessly confused. There are long chunks of text with no clear purpose and multiple evenly-weighted buttons that leaves visitors with no idea what they should do.&lt;/p&gt;

&lt;p&gt;This bad design soon began to cost me. The board requested changes each week. Some were easy to implement; others seemed to be asking for a redesign of the whole site — so eventually, that's exactly what I decided to do, doing it right this time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Doing it right/the most important step: Client Research
&lt;/h2&gt;

&lt;p&gt;The superweapon to doing design right is simple — do client research. Do it before you make a single mockup, write a single line of code.&lt;/p&gt;

&lt;p&gt;What should you accomplish with client research? Get to actually know your client. Know their story, their purpose statement, the people they interact with, the impression they want to give off.&lt;/p&gt;

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

&lt;p&gt;some questions&lt;/p&gt;

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

&lt;p&gt;some answers&lt;/p&gt;

&lt;p&gt;Immediately, this gives you a picture of what's actually important to communicate in the website. Walls of text elucidate into highlighted numbers and sentences. Knowing who you're communicating to tells you what was wrong before, what was missing or broken about the design.&lt;/p&gt;

&lt;p&gt;The client usually isn't clear on every part of what they want to communicate — that's why they got you to help. The client research process, then, becomes a shared journey of discovering the client brand: you guiding them along with questions, both arriving together at a better understanding.&lt;/p&gt;

&lt;p&gt;As an illustration of this, a powerful question is to ask for adjectives that &lt;em&gt;do&lt;/em&gt; and &lt;em&gt;don't&lt;/em&gt; (this is often where the real clarity come from) describe the client's brand. Vague ideas clarify into distinct characteristics:&lt;/p&gt;

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

&lt;p&gt;Asking about competition, too, identifies how to strengthen messaging:&lt;/p&gt;

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

&lt;p&gt;Now we know what's important. We know what we want to say. Only now are you ready to do the rest of your job, building the way to say it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Doing it right: Keep It Simple, Stupid
&lt;/h2&gt;

&lt;p&gt;Forget the colors. Forget the fancy offset boxes. Forget the hero images.&lt;/p&gt;

&lt;p&gt;Only put in what matters. Build a clear hierarchy. Visitors should know exactly where they should be looking, what they should be thinking.&lt;/p&gt;

&lt;p&gt;Here's the homepage that I designed the second time around, live at stem4free.org:&lt;/p&gt;

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

&lt;p&gt;Bam, number to the face. We have a real impact. Above, a straightforward introduction. Below, live stats on donations, because we want to communicate that we're active and reputable. Below that, our clear main call to action — join as a volunteer! If you're a business, join as a partner! And then updates and blog posts, because again, we're active and reputable. The messaging is focused and clear.&lt;/p&gt;

&lt;p&gt;There's no hero image. Why would you want a hero image? We don't have any good ones, our media comes from high schoolers taking pictures of donations with their phones, so that's what we put in a grid below. That's the good stuff, where the impact isn't in attention-grabbing quality, it's in quantity. We're active and reputable.&lt;/p&gt;

&lt;p&gt;It's literally monochrome. Could colors have been tactfully used to make this website just a little better, the brand more distinctive and memorable? Yeah, for sure — but its absence really isn't significant, affirming that colors really wouldn't have been communicating that much in the first place. We avoid this complexity and focus on what matters: the copy, the typography, the layout.&lt;/p&gt;

&lt;p&gt;Obviously these aren't generalized rules. Hero images and graphics can be really effective; colors are often critical to a brand or design. The important thing is to design not with arbitrary rules (or shiny Awwwards sites) as the starting point, but with understanding your client, figuring out what's actually meaningful and important to design around.&lt;/p&gt;

&lt;p&gt;Aside from the homepage, branch pages were an important feature of the redesign:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BmwzAZCc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/gipi6mx687b5kkffudc6.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BmwzAZCc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/gipi6mx687b5kkffudc6.jpg" alt="Website v2 branch page"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;They're really simple, but they contain the information that matters. The idea came from the client research, of course — give prospective volunteers and businesses a centralized branch info page. See the localized stats, see the directors' faces and contact info.&lt;/p&gt;

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

&lt;p&gt;When I made the first version of the Stem4Free website, I wondered why it was so &lt;em&gt;ugly&lt;/em&gt;. I attributed it to lack of time put into it — I didn't even make a mobile mockup before bashing out the WordPress template — or just my design skills being rusty.&lt;/p&gt;

&lt;p&gt;When I made the second version of the website, I was really happy with how pretty and cohesive it looked. I wasn't trying to make it pretty, I was just trying to communicate. As I mentioned earlier, there weren't even colors or a hero image to the site; but when the core design and messaging of a website is clear, everything comes together around it. Your intuition can get to work without obstacles throwing it off at every step; small improvements add up into something bigger rather than going in a million different directions.&lt;/p&gt;

&lt;p&gt;Requests for changes didn't stop, of course. This is a growing, changing student organization, and I'm one of their staff (a director, even); I didn't aim to make a thorough, for-forever package. What I did build, though, was a design with solid, well-researched foundations. The changes being requested now are minor corrections or iterative changes — they make sense, and extend the original design rather than compromising or diverging from it.&lt;/p&gt;

&lt;p&gt;What allowed this to happen — as promised, making the second version of the website prettier, the client more happy, and me more sane — was that client research in the middle. That's because good design isn't about being able to make the prettiest things by magic; it's about making well-thought out things, asking the right questions and doing the work to get there. Design processes can get much more involved beyond client research: rounds of workshops and design sprints, for example. If you're a seasoned designer or developer who has worked with tons of clients, you already know these truths, but if you're just getting started or still figuring things out, it's never too early to strive to put design thinking at the center of your process.&lt;/p&gt;

</description>
      <category>design</category>
    </item>
    <item>
      <title>How to Support User-Selected Google Fonts in WordPress Themes &amp; Plugins</title>
      <dc:creator>Samson Zhang</dc:creator>
      <pubDate>Wed, 10 Jun 2020 23:25:20 +0000</pubDate>
      <link>https://forem.com/wwsalmon/how-to-support-user-selected-google-fonts-in-wordpress-themes-plugins-55f5</link>
      <guid>https://forem.com/wwsalmon/how-to-support-user-selected-google-fonts-in-wordpress-themes-plugins-55f5</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://www.samsonzhang.com/2020/06/10/how-to-support-user-selected-google-fonts-in-wordpress-themes-plugins.html"&gt;my blog&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

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

&lt;p&gt;Recently I’ve been working on &lt;a href="https://github.com/wwsalmon/morse-wp-theme"&gt;Morse&lt;/a&gt;, a WordPress theme custom-built for small newspapers, drawing on my experience building and then maintaining &lt;a href="https://phillipian.net/"&gt;&lt;em&gt;The Phillipian&lt;/em&gt;&lt;/a&gt;&lt;a href="https://phillipian.net/"&gt;’s website&lt;/a&gt; for a year. I wanted this theme to be highly customizable, able to easily accomodate any paper’s branding and maybe even &lt;em&gt;The Phillipian&lt;/em&gt;’s one day. A big part of this is allowing users to specify what fonts they want to be used.&lt;/p&gt;

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

&lt;p&gt;To start, it would just be Google Fonts. Good variety, hopefully easy to implement. The vision was that a user could go to the Customizer interface in WordPress, plug in a font name, and watch the preview update to use that font where applicable, then publish it and have their changes go live.&lt;/p&gt;

&lt;p&gt;Honestly, this was one of the features I was most intimidated by. I’ve worked a fair bit with WordPress and PHP, and with JavaScript, but never in combination. I looked through the source code of open-source plugins and themes that had already implemented Google Fonts integration and got lost in their endless includes and complexities, not even knowing where to find the code that mattered.&lt;/p&gt;

&lt;p&gt;This morning, though, I managed to figure it out, implementing exactly what I wanted with way simpler code than expected! To solidify my own learning (&lt;a href="https://twitter.com/patrick_oshag/status/971100425498841089?lang=en"&gt;learn, build, share, repeat&lt;/a&gt;) and to share this knowledge with anyone else who might want to implement the same functionality in their WordPress theme, I’ve created this post as a little tutorial of my solution.&lt;/p&gt;

&lt;h2&gt;
  
  
  WP Customizer
&lt;/h2&gt;

&lt;p&gt;First, let’s create fields in the customizer to allow users to specify fonts. For simplicity, I implemented these as straightforward string fields: users will have to browse through fonts beforehand and enter the exact name. Google Fonts does have an API for getting a list of all available fonts, and some WordPress plugins have implemented fancy lists&lt;/p&gt;

&lt;p&gt;We’ll add three different fields: a body font, a heading font, and an accent font (for the occassional button, label, etc.; obviously you can make these options whatever you want). This is straightforward to implement:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function morse_wp_plugin_customizer($wp_customize)
{
    $wp_customize-&amp;gt;add_section('morse-wp-custom-section', array(
        'title' =&amp;gt; "Morse WP Theme Overall Settings"
    ));
    $wp_customize-&amp;gt;add_setting('morse-wp-font-body');
    $wp_customize-&amp;gt;add_control('morse-wp-font-body-control', array(
            'label' =&amp;gt; 'Google Font for Body',
            'type' =&amp;gt; 'string',
            'section' =&amp;gt; 'morse-wp-custom-section',
            'settings' =&amp;gt; 'morse-wp-font-body'
    ));
    $wp_customize-&amp;gt;add_setting('morse-wp-font-heading');
    $wp_customize-&amp;gt;add_control('morse-wp-font-heading-control', array(
            'label' =&amp;gt; 'Google Font for Headings',
            'type' =&amp;gt; 'string',
            'section' =&amp;gt; 'morse-wp-custom-section',
            'settings' =&amp;gt; 'morse-wp-font-heading'
    ));
    $wp_customize-&amp;gt;add_setting('morse-wp-font-accent');
    $wp_customize-&amp;gt;add_control('morse-wp-font-accent-control', array(
            'label' =&amp;gt; 'Google Font for Accents',
            'type' =&amp;gt; 'string',
            'section' =&amp;gt; 'morse-wp-custom-section',
            'settings' =&amp;gt; 'morse-wp-font-accent'
    ));
}

add_action('customize_register', 'morse_wp_plugin_customizer');
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Utility Classes
&lt;/h2&gt;

&lt;p&gt;In a template PHP file, we can now access these customizer settings using &lt;code&gt;get_theme_mod()&lt;/code&gt;, for example &lt;code&gt;get_theme_mod(&lt;/code&gt;&lt;code&gt;"&lt;/code&gt;&lt;code&gt;morse-wp-font-heading&lt;/code&gt;&lt;code&gt;"&lt;/code&gt;&lt;code&gt;, "Georgia")&lt;/code&gt; to get the heading font, with a default of Georgia if the option has not been set by the user. Now, how do we get this into our CSS?&lt;/p&gt;

&lt;p&gt;The trick here is to use utility classes instead of trying to write dynamic fonts into semantic CSS. Instead of specifying that &lt;code&gt;.article-title h1&lt;/code&gt; should have our heading font, we specify a utility class &lt;code&gt;.morse-font-heading&lt;/code&gt; that we can apply to any element we want our heading font on. Instead of trying to insert snippets into or overwrite various parts of a stylesheet, then, we can simply add our utility classes in an inline style block using the &lt;code&gt;wp_head&lt;/code&gt; hook:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function morse_wp_plugin_add_styles(){ ?&amp;gt;
&amp;lt;style&amp;gt;
    .morse-font-body{
        font-family: &amp;lt;?php echo get_theme_mod("morse-wp-font-body", "Georgia"); ?&amp;gt;
    }

    .morse-font-heading{
        font-family: &amp;lt;?php echo get_theme_mod("morse-wp-font-heading", "Georgia"); ?&amp;gt;
    }

    .morse-font-accent{
        font-family: &amp;lt;?php echo get_theme_mod("morse-wp-font-accent", "sans-serif"); ?&amp;gt;
    }
&amp;lt;/style&amp;gt;
&amp;lt;?php
}
add_action("wp_head","morse_wp_plugin_add_styles");
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Note: above is only a snippet of the &lt;code&gt;add_styles&lt;/code&gt; function I actually use. The same approach can be applied to implementing user-specified colors, for example, which I might write about later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Web Font Loader
&lt;/h2&gt;

&lt;p&gt;The last step, and the one that confused/intimidated me for a little bit, was how to actually load these fonts from Google Fonts. Google Fonts has a JavaScript Web Font Loader library/API that allows fonts to be dynamically loaded, but I’ve never connected WordPress functionality with JavaScript before. It’s something I’ll have learn eventually, especially with Gutenberg blocks being React-based. In this case, though, I was able to figure out a straightforward solution, based &lt;a href="https://wordpresssupercharged.com/how-to-improve-page-speed-by-deferring-web-fonts/"&gt;on this tutorial&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;First, download the &lt;a href="https://github.com/typekit/webfontloader/blob/master/webfontloader.js"&gt;Web Font Loader library&lt;/a&gt;. Throw that JS file somewhere in your plugin or theme, and enqueue it like any other script.&lt;/p&gt;

&lt;p&gt;Now, all we need to do is run &lt;code&gt;WebFont.load()&lt;/code&gt; with our desired parameters to load in our fonts. &lt;a href="https://github.com/typekit/webfontloader"&gt;Web Font Loader’s GitHub&lt;/a&gt; provides documentation on what these parameters look like, but here’s a quick example:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WebFont.load({
  google: {
    families: ["Roboto:400,700", "Roboto Mono"]
  }
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Pretty easy, right? WordPress allows us to easily insert inline scripts using &lt;code&gt;wp_add_inline_script&lt;/code&gt;, which we can include right after we enqueue the Web Font Loader library. All we have left to do, then, is generate the parameters to call based on our Customizer settings. This just takes some simple PHP. First, I create an array of fonts with &lt;code&gt;array_unique([get_theme_mod("morse-wp-font-body"), get_theme_mod("morse-wp-font-heading"), get_theme_mod("morse-wp-font-accent")])&lt;/code&gt; — &lt;code&gt;array_unique&lt;/code&gt; making sure I don’t duplicate-request fonts — then turn this into the specific format that the loader wants with a for loop and some string concatenation. For now, I just specified 400 and 700 weights (regular and bold), though it’s only a matter of busiwork if you wanted to allow user-specified weights as well.&lt;/p&gt;

&lt;p&gt;Here’s how it all comes together:&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function morse_wp_enqueue_fonts(){
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    wp_enqueue_script( 'webfontloader', plugin_dir_url( __FILE__) . "../js/webfontloader.js", NULL, '', true );

    $fonts_array = array_unique([get_theme_mod("morse-wp-font-body"), get_theme_mod("morse-wp-font-heading"), get_theme_mod("morse-wp-font-accent")]);
    $fonts_string = "";
    foreach ($fonts_array as $f){
            $fonts_string = $fonts_string . "\"" . $f . ":400,700\",";
    }
    $config_string = "
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;WebFont.load({&lt;br&gt;
  google: {&lt;br&gt;
    families: [&lt;br&gt;
    " . $fonts_string . "&lt;br&gt;
    ]&lt;br&gt;
  }&lt;br&gt;
});&lt;br&gt;
        ";&lt;br&gt;
        wp_add_inline_script( 'webfontloader', $config_string, 'after' );&lt;br&gt;
}&lt;br&gt;
add_action("wp_enqueue_scripts", "morse_wp_enqueue_fonts");&lt;br&gt;
&lt;/p&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  And We’re Done!&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;And there we have it, an easy-to-implement, easy-to-extend solution for loading user-specified Google Fonts into your WordPress theme. There’s a lot more to look into — here are a few ideas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dropdown list of potential fonts&lt;/li&gt;
&lt;li&gt;User-specified font weights&lt;/li&gt;
&lt;li&gt;User-uploaded custom fonts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But, honestly, I thought this feature was gonna be really hard to build, and it turned out to be surprisingly simple, and highly functional for its simplicity. No plugins, no jank hacks going against WordPress’ design, just a few straightforward lines of code.&lt;/p&gt;

&lt;p&gt;Find the full source code for the &lt;a href="https://github.com/wwsalmon/morse-wp-theme"&gt;Morse theme&lt;/a&gt; and &lt;a href="https://github.com/wwsalmon/morse-wp-plugin"&gt;theme-specific plugin (where the code from this tutorial is located)&lt;/a&gt; on &lt;a href="https://github.com/wwsalmon"&gt;my GitHub&lt;/a&gt;, and let me know what you think of this solution through &lt;a href="https://twitter.com/wwsalmon"&gt;Twitter (@wwsalmon)&lt;/a&gt; or in the comments of this post!&lt;/p&gt;

</description>
      <category>wordpress</category>
    </item>
    <item>
      <title>CSS is an art, and it's dying. A reflection on CSS and JS frameworks and the evolution of CSS</title>
      <dc:creator>Samson Zhang</dc:creator>
      <pubDate>Wed, 13 May 2020 05:16:51 +0000</pubDate>
      <link>https://forem.com/wwsalmon/the-artistry-of-css-and-its-death-a-reflection-about-css-and-js-frameworks-and-the-evolution-of-css-1npg</link>
      <guid>https://forem.com/wwsalmon/the-artistry-of-css-and-its-death-a-reflection-about-css-and-js-frameworks-and-the-evolution-of-css-1npg</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://www.samsonzhang.com/2020/05/13/the-artistry-of-css-and-its-death-a-reflection-about-css-and-js-frameworks-and-the-evolution-of-css.html"&gt;my blog&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I’ve been working through the &lt;a href="https://serverless-stack.com/"&gt;serverless-stack.com&lt;/a&gt; tutorial. It’s awesome, teaching you how to set up an AWS backend, React front-end, and now is going through CI/CD through &lt;a href="https://seed.run/"&gt;Seed&lt;/a&gt;. Like most tutorials, it does feel like I’m just going through the process, not copy-pasting but reading and re-writing code without being too sure of what it all does and why I’m using it; really in-depth learning only comes with time and experience: building your own project from scratch, dealing with all the considerations, use cases, and bugs that come with that. The usage of Seed instead of CircleCI or TravisCI like I see on almost every repo is also a reminder of the dizzying amount of options out there. I had briefly made a foray into learning MongoDB and Express before this current serverless endeavor, and it seems to be something many recruiters want to see; and there are a million alternatives to the React frontend, from other JS frameworks like Angular and Vue to ditching this totally and just writing straight JS.&lt;/p&gt;

&lt;p&gt;The Seed website seemed to use the same AWS and React Bootstrap components that the Serverless tutorial was teaching, so somehow I went into a slight rabbithole and ended up on the &lt;a href="https://markdotto.com/projects/"&gt;website of Mark Otto&lt;/a&gt;, the creator of Bootstrap, also the top designer at GitHub and Twitter before that (it suddenly made sense why GitHub uses Bootstrap). Scrolling through his projects, I saw a bunch of really cool resources and minisites. A &lt;a href="https://codeguide.co/"&gt;“code guide”&lt;/a&gt; with HTML/CSS quality standards and conventions; a &lt;a href="https://mdo.github.io/mdoml/"&gt;concept for HTML elements&lt;/a&gt; redesigned for more modern use cases; and “A &lt;a href="http://wtfhtmlcss.com/"&gt;curated list&lt;/a&gt; of commonly frustrating HTML and CSS quandaries, miscues, and dilemmas.”&lt;/p&gt;

&lt;p&gt;Reading through these pages, I was struck by a sense of nostalgia. At the bottom of the HTML section of the Code Guide, just a two-sentence sidenote, was: “Writing markup in a JavaScript file makes the content harder to find, harder to edit, and less performant. Avoid it whenever possible.” The assertion “JS bad” seems to come up at every conference and on every blog. JS slow, JS inaccessible, JS bad for SEO, JS bad for mobile. And the counterargument — you’re ignoring the developer experience, JS frameworks make it 20x easier and faster to build a powerful web app, build and optimize from there. Ship ship ship. Done is better than perfect. But Mark Otto’s two line snippet didn’t seem to be referring to this, the mess of frameworks and CSS-in-JS and everything else that we’ve found ourselves in. It seems to be speaking of a simpler time, when JS meant scripts adding functionality to a website, not the backbone of the website itself. When being a front-end web developer meant writing nicely organized HTML and perfectly cascading CSS — almost the opposite of what I was invested so heavily in learning now.&lt;/p&gt;

&lt;p&gt;To be sure, if you haven’t been able to tell yet, I don’t claim to be anything near a professional web developer. All this fancy JS stuff, backend stuff, most of really high-value web development skills today I’m just getting into. Even for static websites, I’ve never worked with huge codebases and design systems, tested across every browser and device and connection speed, squeezed out every byte and millisecond of loading time from a website. But I’ve read about these things obsessively, and when it comes out to just bashing out a website, I’m confident that I can do it well. At this point, CSS feels like a native language to me. Sure, maybe I don’t memorize all the ins and outs of the grammar, but I grew up with it and so know it comprehensively by experience: all the ways to say something, what “sounds” right and what works but “sounds” wrong, what is good or bad practice not by memorization but by experience. Often I feel faster and more creative designing by bashing out code than in Illustrator. Alternatively, give me a mockup and I can implement it with five completely different sets of CSS.&lt;/p&gt;

&lt;p&gt;Recently, I came across something like the following in a friend’s project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;    &lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nl"&gt;justify-content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nt"&gt;container&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1200px&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;What on earth? I asked him what his thinking was. “The program where I learned CSS said to use flexbox on everything,” he told me.&lt;/p&gt;

&lt;p&gt;Just to center a top level container! Whatever happened to &lt;code&gt;margin: 0 auto&lt;/code&gt;? Oh, man. Maybe I was just lagging behind the times — was this really best practice now? I remember when flexbox was the dreamlike new thing on the scene, the solution to all of our CSS alignment problems, the end of floats and clearfixes and &lt;em&gt;tables&lt;/em&gt; (my friend will never know of these dark ages, I guess). Browser support was barely existent, and “&lt;a href="https://css-tricks.com/snippets/css/a-guide-to-flexbox/"&gt;A Complete Guide to Flexbox&lt;/a&gt;” had just been published on the CSS-Tricks frontpage. Sparingly, I began integrating it into my own projects. Today, it’s a crucial part of my toolkit, but one I chose to take out when needed, not anything like a default. Now we’ve got Grid, too. New CSS developers these days are spoiled.&lt;/p&gt;

&lt;p&gt;And CSS frameworks! Bootstrap was heavy, I had always heard. Makes sense for huge web apps, but way overkill for my tiny little websites. And it was just cleaner and easier to get exactly what I wanted with my own from-scratch code. But now, what do we have? Developers aren’t even writing straight CSS anymore. Atomic frameworks like the currently hugely popular Tailwind CSS are the default. Instead of applying classes in HTML and carefully putting together a harmonious stylesheet, you just apply &lt;code&gt;w-16&lt;/code&gt;, &lt;code&gt;h-16&lt;/code&gt;, &lt;code&gt;mx-auto&lt;/code&gt; classes to your markup, and bam — &lt;code&gt;width: 16px&lt;/code&gt;, &lt;code&gt;height: 16px&lt;/code&gt;, &lt;code&gt;margin: 0 auto&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;My gut reaction to this is, nononononono. I want to keep writing the CSS I know and love. It’s elegant, it’s beautiful, it’s my craft. CSS is almost a part of my identity at this point: my artistry, my outlet of creative expression, my value proposition as a part of society. These new frameworks, not to mention ideas like CSS-in-JS and pre-styled React component libraries, seem to be ripping CSS as I know it away from me, threatening to rip a chunk of my very being away from me. And I’m not alone in this — Chris Hawkes, for example, pretty much voices the same sentiment in his video “&lt;a href="https://www.youtube.com/watch?v=qECXE-retvk"&gt;Why I Don’t Like Tailwind CSS&lt;/a&gt;” (though I disagree with him that SASS is similar — SASS is still cascading, is still CSS properties, is still what makes CSS CSS in my heart; I love it and use it extensively). &lt;/p&gt;

&lt;p&gt;Yet, resisting means risking straggling behind the unstopping forward-marching standard of “this is the way that things are done.” There are far more advocates than detractors for atomic frameworks. Hawke’s video has 205 dislikes to 241 likes, for example. And these frameworks don’t just throw away CSS — they are built upon the deepest understanding of and experience with it, not just in technical aspects but in core design philosophy. A comment by a Tailwind CSS community manager on Hawke’s video links to a few essays dating back all the way to 2012 diving into HTML and CSS semantics and scalability. They make a compelling argument of the inherent difficulty of writing and maintaining large CSS codebases by virtue of its cascading nature, the proposed solution being using utility classes. This is exactly the solution that utility-focused atomic frameworks provide.&lt;/p&gt;

&lt;p&gt;These frameworks being thoroughly thought-out and considered, however, doesn't mean that their users are inheriting this CSS knowledge and understanding. The most common developer-perspective argument I’ve heard for using CSS frameworks is rooted in the “ship ship ship” rapid iteration mindset that seems to be the foundation of the software development and tech industries. Frameworks allow developers to stop worrying about carefully maintaining CSS and instead focus on building and improving their product. CSS, then, is no longer a language to learn by immersion, by painstaking honing of understanding and technique through time and experience, but rather an ugly back layer, a necessary evil, to hide away behind conveniently designed frameworks and component libraries. The understanding of CSS as a fundamental and artistic skill, like sketching with pencil on paper for painters and digital artists, necessary to learn first, learn well, and continuously practice, is dying, maybe dead.&lt;/p&gt;

&lt;p&gt;Maybe this is overly drastic or simplistic. &lt;a href="https://www.w3.org/"&gt;W3C&lt;/a&gt; certainly isn’t stopping their work tirelessly assessing and improving CSS. Web development is a huge field and industry; not every website is a JS-based webapp that can ditch CSS, and most jobs will still require direct hands-on CSS work. Using frameworks doesn’t allow you to forego CSS knowledge entirely, either. Utility classes correspond to CSS properties; understanding CSS patterns and behavior is crucial to using them well, as I was reminded of while &lt;a href="https://www.youtube.com/watch?v=7gX_ApBeSpQ"&gt;watching Tailwind CSS developer Adam Wathan&lt;/a&gt; work through different combinations of flexbox and margin classes to get part of a Coinbase clone looking right. And if nothing else, developers specializing in CSS will be needed to maintain and build frameworks and libraries.&lt;/p&gt;

&lt;p&gt;Yet, the state of and most useful skills in web development will continue to ever evolve, and the practice of artistic, intricate, unmaintainable CSS seems to be taking more and more of a backseat. The starkest difference might be in new developers. Sure, intro courses will still start with the basics of HTML and CSS; but moving into any sort of bigger project, frameworks will be much easier to use and in most ways much more effective than slowly mastering raw CSS with time. More developers will be like my friend, setting &lt;code&gt;display: flex&lt;/code&gt; on everything, struggling with more complex layouts. And maybe that’s okay.&lt;/p&gt;

&lt;p&gt;Maybe it’s even for the better. For me, leaving CSS behind is certainly a bigger step forward: moving beyond static web dev, towards JS, towards full-stack, to more powerful frameworks, to bigger and better things. In some ways, particularly with regards to CSS, my perspective is that of a teacher, someone who has been around and knows what I’m doing. Zooming out a bit, though, I’m still very much a student, early in my learning process. From this perspective, I have one last analogy to offer.&lt;/p&gt;

&lt;p&gt;CSS is like arithmetic. It’s at the very base layer of the web, what browser engines process to spit out what’s on the screen. Here are the axioms that just &lt;em&gt;are&lt;/em&gt;, by very definition of the system. CSS frameworks, then, are like algebra. The problems they solve are essentially arithmetic problems, but abstracted a layer. After a while, any arithmetic that you do only passes by in the back of your mind; you think instead in terms of variables, functions, and roots, like utility classes and components to CSS properties. Even higher level JS frameworks and CSS-in-JS, then, are like calculus, abstracting on algebra, providing a whole new pattern for how websites are defined and built. (SASS is like arithmetic tricks, like learning factoring or divisibility rules that provide you shortcuts without actually learning the algebra that makes them work.) As a student, then, maybe my bemoaning the death of CSS as an art is simply that of a student growing out of his comfort zone.&lt;/p&gt;

&lt;p&gt;But CSS, in its beautiful, raw, sometimes badly-written form, will forever (probably…?) remain at the core of the web as we know it. Maybe the whole field is moving forward, abstracting on top of it, so new developers can make use of it faster and better; but I’m glad that I started learning web development before atomic frameworks blew up, before grid, before flexbox, when avoiding Javascript for front-end development was a valuable and feasible thing, when CSS was an art and a language I immersed myself in. Now I’m in time to move along with the field, my knowledge of what once was allowing me to especially appreciate what now is and what’s to come.&lt;/p&gt;

</description>
      <category>css</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Offsetting Overlapping Labels in d3.js with a Recursive Algorithm</title>
      <dc:creator>Samson Zhang</dc:creator>
      <pubDate>Wed, 13 May 2020 03:21:16 +0000</pubDate>
      <link>https://forem.com/wwsalmon/offsetting-overlapping-labels-in-d3-js-with-a-recursive-algorithm-ak6</link>
      <guid>https://forem.com/wwsalmon/offsetting-overlapping-labels-in-d3-js-with-a-recursive-algorithm-ak6</guid>
      <description>&lt;p&gt;&lt;em&gt;or: using recursion on a real project for the first time eeeee&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://www.samsonzhang.com/2020/04/29/offsetting-overlapping-labels-in-d3js-with-a-recursive-algorithm-or-using-recursion-on-a-real-project-for-the-first-time-eeeee.html"&gt;my blog&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;What is recursion? Simply a function that calls itself. As an example, here’s a recursive algorithm for calculating factorials:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a pretty trivial application; the same alg is easy enough to implement with a simple for loop. But for certain complex operations like merge sort, a recursive approach can be much easier and faster.&lt;/p&gt;

&lt;p&gt;To be honest, I never thought I’d use recursion outside of my intro compsci class, at least not directly for my own algorithms. I’ve mainly done front-end development — the complexity of my JS functions rarely went beyond a for loop; if we’re fancy, then maybe some multidimensional arrays and manipulation of them. Recursion didn’t seem like something that was useful for functions that weren’t ridiculously complex or specific.&lt;/p&gt;

&lt;p&gt;Recently I’ve been working a lot with d3.js, building a &lt;a href="https://www.npmjs.com/package/sota.js"&gt;charting library&lt;/a&gt; for &lt;em&gt;The Phillipian&lt;/em&gt;’s annual &lt;a href="http://pdf.phillipian.net/2019/05102019.pdf"&gt;State of the Academy project&lt;/a&gt;. It’s been an amazing project, learning and getting comfortable with d3.js and Javascript in general, as I’ve only used it tangentially in service of non-JS static content before; this project gives me a good starting point for getting more into JS with tools like React. But I digress. d3.js is relevant because using it requires so much more direct and creative programming. It gives you tons of power by aiding with manipulating data and corresponding elements, but you’re leaving behind the world of document flow and generally built-in layout functionality. Coordinates and dimensions are set directly, so you need to come up with your own algorithms for dealing with overlap and even alignment.&lt;/p&gt;

&lt;p&gt;As such, the comfort I gained working with arrays on seemingly arbitrary problems has actually come in handy. One such thing, unexpectedly, was recursion! Let me explain.&lt;/p&gt;

&lt;p&gt;Here’s the starting point. Nice simple stackedBarChart, and I was just starting to implement labels:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rXUMCkco--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.samsonzhang.com/img/blog/2020-04/startingpoint.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rXUMCkco--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.samsonzhang.com/img/blog/2020-04/startingpoint.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The code so far is straightforward. First, I create a polyline, for now with just two points, going from the center of each bar to a certain amount above it. Then, above each of these lines, I render the appropriate text label, and push the right bound of each text label to an array. These right bounds are used to calculate the third point of the polyline, ending right where the text does. (Not sure why it’s not working for “Both” in the example above whoops)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;chartGroups&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.sota-stackedBarChart-label-aboveBar-line&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;polyline&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;class&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;sota-stackedBarChart-label-aboveBar-line&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;points&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;x1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&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;y1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;barHeight&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&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;x2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;x1&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;y2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;labelBelow&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;x1&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;y1&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;x2&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;y2&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stroke-width&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;separatorStrokeWidth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stroke&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lineColor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fill&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;none&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;labelRightBounds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

&lt;span class="nx"&gt;chartGroups&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.sota-stackedBarChart-label-aboveBar-text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;class&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;sota-stackedBarChart-label-aboveBar-text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;valueLabels&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&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;d3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.1f&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])}&lt;/span&gt;&lt;span class="s2"&gt;%`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;y&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;labelRightBounds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getBBox&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getBBox&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;labelBelow&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;alignment-baseline&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;bottom&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;chartGroups&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.sota-stackedBarChart-label-aboveBar-line&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;polyline&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;points&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;currentPoints&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;points&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;currentPoints&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;` &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;labelRightBounds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;labelBelow&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, okay! Clearly the labels overlapping is a problem. A reference from previous print versions of the survey provide guidance on how to fix this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Yo-w3afI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.samsonzhang.com/img/blog/2020-04/ref1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Yo-w3afI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.samsonzhang.com/img/blog/2020-04/ref1.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Not the above/below thing — we’ll keep all the labels above for simplicity. But the “Yes, on campus” and “Yes, off campus” labels provide a perfect model for how to offset the labels so they don’t overlap: raise the height of the first overlapping label, and move the second overlapping label right until it no longer overlaps. Time to code it!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hYIVTIhw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.samsonzhang.com/img/blog/2020-04/raise1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hYIVTIhw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.samsonzhang.com/img/blog/2020-04/raise1.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Whoops, that’s not right!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_sBPrdkA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.samsonzhang.com/img/blog/2020-04/raise2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_sBPrdkA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.samsonzhang.com/img/blog/2020-04/raise2.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There we go.&lt;/p&gt;

&lt;p&gt;This implementation came with a few notable changes from the previous code. I realized that only the text needed to be rendered to calculate the third point of the polyline, and to discover any overlaps, so instead of first creating a polyline with two points and adding the third later, I started by adding the text in their appropriate default spots.&lt;/p&gt;

&lt;p&gt;This time, more than the right boundary of each text label is pushed to the &lt;code&gt;labelRightBounds&lt;/code&gt; array, but rather an array of both its left boundary position and its width. This allows us to calculate overlap between labels, which I do in a straightforward for loop. Iterating from first to last, if the current element overlapped with the previous one, then I would raise the height of the previous one and extend the left position of the current element.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;labelRightBounds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

&lt;span class="nx"&gt;chartGroups&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.sota-stackedBarChart-label-aboveBar-text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;class&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;sota-stackedBarChart-label-aboveBar-text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;valueLabels&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&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;d3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.1f&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])}&lt;/span&gt;&lt;span class="s2"&gt;%`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;y&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;labelRightBounds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getBBox&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getBBox&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;labelBelow&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;alignment-baseline&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;bottom&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;labelHeights&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;prevRightBound&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;labelLeft&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;i&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;labelRightBounds&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;labelRightBounds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;prevRightBound&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;labelLeft&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;labelRightBounds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;prevRightBound&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;labelLeft&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;labelHeights&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;labelHeights&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;labelHeights&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;prevRightBound&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;labelRightBounds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;labelRightBounds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;chartGroups&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.sota-stackedBarChart-label-aboveBar-line&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;polyline&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;class&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;sota-stackedBarChart-label-aboveBar-line&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;points&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;x1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&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;y1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;barHeight&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&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;x2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;x1&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;y2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;labelHeights&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;labelBelow&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;x3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;labelRightBounds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;labelRightBounds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;y3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;y2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;x1&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;y1&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;x2&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;y2&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;x3&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;y3&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stroke-width&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;separatorStrokeWidth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stroke&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lineColor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fill&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;none&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;chartGroups&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.sota-stackedBarChart-label-aboveBar-text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;labelRightBounds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;y&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;labelHeights&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;labelBelow&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;Iterating from front to back is nice because previous right boundary shifts are accounted for; i.e. the shifting of a previous label might cause it to overlap with the next one, which will now have to move more. But thinking about this alg reveals that it doesn’t actually account for not just one, but multiple labels in a row overlapping, because if a label is found to be overlapping with a previous label, only the height of this immediately preceding label is raised. If this preceding label was overlapping with its preceding label, this precending label would not get raised another step, as it should if the second label were raised by the third.&lt;/p&gt;

&lt;p&gt;To make the heights work, you’d have to iterate from last to first label, raising each one a successive step higher if necessary; but then you wouldn’t be able to account for rightward shifts causing overlap, which propagate from first to last label. To solve this problem we’d have to iterate both forwards and backwards.&lt;/p&gt;

&lt;p&gt;One solution is to do just that — loop over everything twice: once first-to-last to apply rightward shifts, and again last-to-first to apply height shifts. But before I could get to that, a different idea took hold. &lt;strong&gt;Recursion!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s take a look at the final implementation — just the recursive part replacing the for loop, as the rendering code didn’t really change.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getLabelHeight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nx"&gt;labelRightBounds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;labelHeights&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;labelRightBounds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;labelRightBounds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;labelLeft&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;labelRightBounds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;labelRightBounds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;labelRightBounds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;labelRightBounds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;labelLeft&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;nextHeight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;getLabelHeight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;thisHeight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;nextHeight&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;labelHeights&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;thisHeight&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;thisHeight&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;getLabelHeight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;labelHeights&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;getLabelHeight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We still have our &lt;code&gt;labelHeights&lt;/code&gt; and &lt;code&gt;labelRightBounds&lt;/code&gt; arrays, the latter storing the left coordinate and width of each label. We define our recursive function function &lt;code&gt;getLabelHeight&lt;/code&gt; with parameter &lt;code&gt;i&lt;/code&gt;, the index of the current label in consideration.&lt;/p&gt;

&lt;p&gt;Our first conditional checks for the base case: if the label in consideration is the last label. In this case, we set the corresponding &lt;code&gt;labelHeights&lt;/code&gt; slot to the default height, and return it (or at least the coefficient of it; -2 in this case because the line is -1 units up, and the text -2. This coefficient increments by one from here on out).&lt;/p&gt;

&lt;p&gt;If this isn’t the case, then the next label exists, and the current label either overlaps with it or not. If it doesn’t overlap, then easy, we set the corresponding &lt;code&gt;labelHeights&lt;/code&gt; slot to the default value and return it. Before we do that, we call &lt;code&gt;labelHeights(i+1)&lt;/code&gt; so the next labelHeight is calculated.&lt;/p&gt;

&lt;p&gt;If it does overlap, or is closer than a specified margin, then we must raise the height of the current label. What do we raise the current label to? To one step higher than the height of the next label. But we don’t know the height of the next label, do we? Let’s find it, by calling the very function &lt;code&gt;getLabelHeight&lt;/code&gt; on it, passing in &lt;code&gt;i+1&lt;/code&gt; as the parameter; then we can set the return value of the function as the corresponding &lt;code&gt;labelHeights&lt;/code&gt;  slot, and return this value for the preceding function call. In the case that the next label is raised because it overlaps with the label after that, it will return a raised height, and the height of the current label will be raised to the appropriate height above that. This applies for however many overlapping labels you have; they might clip out of the SVG Canvas, but the heights will cascade all the way from either the first non-overlapping label or the last one back, forming a nice staircase of heights.&lt;/p&gt;

&lt;p&gt;Sounds good. Does it actually work?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_sBPrdkA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.samsonzhang.com/img/blog/2020-04/raise2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_sBPrdkA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.samsonzhang.com/img/blog/2020-04/raise2.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Okay, nothing changed, but that was what was supposed to happen. That means that the function works for a two-overlap scenario. Let’s shrink down our window to see the cascade in action:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--y8ADtzYa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.samsonzhang.com/img/blog/2020-04/raise3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--y8ADtzYa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.samsonzhang.com/img/blog/2020-04/raise3.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hell yeah, there we go!&lt;/p&gt;

&lt;p&gt;The recursive function ended up being 16 lines of code, compared to the single loop’s 9; implementing the second backwards loop for the same functionality as the recursive function probably would have at least doubled the line count, so the recursive function is certainly nicer to write, and arguably understand, than a double loop. Is it faster to run? I have no idea. In effect it’s looping over the data the same way: once forward, each function call calling the function on the next label before it returns, up until the last label; and then once backward, each function return propagating to the previous function call for the last operations before turning to the next previous call, all the way up to the first call which returns the value to nothing.&lt;/p&gt;

&lt;p&gt;None of this was particularly mind-blowing, but it brought me a lot of joy to use what I had learned forever ago in a classroom on a real project, a neat trick that solved a real, somewhat tricky problem.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>d3js</category>
    </item>
  </channel>
</rss>
