<?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: Zane Milakovic</title>
    <description>The latest articles on Forem by Zane Milakovic (@khrome83).</description>
    <link>https://forem.com/khrome83</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%2F19497%2F7ede3ec0-d0f7-4c38-86a2-45118863982b.png</url>
      <title>Forem: Zane Milakovic</title>
      <link>https://forem.com/khrome83</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/khrome83"/>
    <language>en</language>
    <item>
      <title>How we built cross-region uptime verification (and why single-location monitoring is broken)</title>
      <dc:creator>Zane Milakovic</dc:creator>
      <pubDate>Sun, 01 Mar 2026 16:15:52 +0000</pubDate>
      <link>https://forem.com/khrome83/how-we-built-cross-region-uptime-verification-and-why-single-location-monitoring-is-broken-24mo</link>
      <guid>https://forem.com/khrome83/how-we-built-cross-region-uptime-verification-and-why-single-location-monitoring-is-broken-24mo</guid>
      <description>&lt;p&gt;If you've ever been woken up at 3am by a monitoring alert that turned out to be nothing, you already understand the problem.&lt;/p&gt;

&lt;p&gt;Most uptime monitoring works like this: a server in Virginia pings your site every minute. If it gets a bad response, it sends you an alert. Simple, effective, and wrong about 20% of the time.&lt;/p&gt;

&lt;p&gt;That number isn't made up — it's roughly what I saw across my own client sites over two years of using various monitoring tools. About one in five alerts was a false positive caused by network routing issues, transient DNS problems, or a brief hiccup at the monitoring provider's own data center.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix is obvious (in hindsight)
&lt;/h2&gt;

&lt;p&gt;When a check fails, don't immediately alert. Instead, trigger verification checks from other regions. If Chicago says your site is down but Amsterdam, Virginia, and Singapore all say it's fine — that's not an outage. That's a network blip.&lt;/p&gt;

&lt;p&gt;This is what I built into &lt;a href="https://flarewarden.com" rel="noopener noreferrer"&gt;FlareWarden&lt;/a&gt;. Here's roughly how it works:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Initial check fails.&lt;/strong&gt; One of our 18 monitoring regions reports a failure. Timer starts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Cross-region verification.&lt;/strong&gt; We immediately fire checks from multiple other regions. The number of confirming regions required is configurable — you might want 2 out of 3 for a personal project, or 4 out of 5 for production infrastructure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Consensus determines outcome.&lt;/strong&gt; If verification checks confirm the outage, alert fires. If they don't, we log it as a regional issue and move on. You sleep through the night.&lt;/p&gt;

&lt;p&gt;The whole verification loop typically completes in 30-60 seconds. Fast enough to catch real outages quickly, slow enough to filter out noise.&lt;/p&gt;

&lt;h2&gt;
  
  
  The parent/child thing
&lt;/h2&gt;

&lt;p&gt;The other architectural decision I'm pretty happy with is the monitor hierarchy.&lt;/p&gt;

&lt;p&gt;Traditional monitoring gives you a flat list: site A is up, site B is down. But that's not how web apps actually work. Your e-commerce site depends on Stripe for payments, Cloudflare for CDN, maybe Shopify for inventory. When Stripe goes down, your site isn't &lt;em&gt;down&lt;/em&gt; — checkout is broken but the rest works fine.&lt;/p&gt;

&lt;p&gt;FlareWarden uses a parent/child model. Your main site is the parent monitor. Dependencies like Stripe or your CDN are child monitors of type "dependency." When a dependency fails, the parent status changes to "degraded" rather than "down." Content monitors (checking that specific text exists on a page) are independent children — they alert you directly without affecting the parent status.&lt;/p&gt;

&lt;p&gt;This means your status page automatically reflects what's actually happening: "Website operational, payment processing degraded" is way more useful than "Website down."&lt;/p&gt;

&lt;h2&gt;
  
  
  Auto-discovery
&lt;/h2&gt;

&lt;p&gt;The last piece I wanted to get right was setup friction. Configuring monitors manually for every domain, every SSL cert, every third-party integration — it's tedious and you always miss something.&lt;/p&gt;

&lt;p&gt;FlareWarden's Smart Setup scans your URL and automatically identifies: third-party service dependencies (we recognize 700+ services), SSL certificate details and expiry dates, critical page content that should always be present, and your overall tech stack.&lt;/p&gt;

&lt;p&gt;You paste a URL, review what it found, and click confirm. Full monitoring in about two minutes.&lt;/p&gt;

&lt;h2&gt;
  
  
  If you want to try it
&lt;/h2&gt;

&lt;p&gt;Free tier is 15 monitors with 5-minute checks, no credit card, no expiry. I'm running founding member pricing (40% off forever) on paid plans through June if you want faster checks or more monitors.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://flarewarden.com" rel="noopener noreferrer"&gt;https://flarewarden.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Would genuinely love technical feedback from this community. The verification logic, the monitor hierarchy, the auto-discovery — poke holes in any of it. That's how it gets better.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>monitoring</category>
      <category>devops</category>
      <category>architecture</category>
    </item>
    <item>
      <title>An Unexpected Benefit of Tailwind CSS</title>
      <dc:creator>Zane Milakovic</dc:creator>
      <pubDate>Tue, 03 Dec 2019 00:00:00 +0000</pubDate>
      <link>https://forem.com/khrome83/an-unexpected-benefit-of-tailwind-css-1eg0</link>
      <guid>https://forem.com/khrome83/an-unexpected-benefit-of-tailwind-css-1eg0</guid>
      <description>&lt;p&gt;CSS has been around for a long time now. I have been using it for over a decade. In that time, we went from keeping it as a single separate file to using naming conventions and pre-processors. Moving past &lt;a href="https://github.com/nemophrost/atomic-css"&gt;Atomic&lt;/a&gt;, BEM, and &lt;a href="http://smacss.com/"&gt;SMACSS&lt;/a&gt;, we started to put the CSS directly into the JavaScript. And to be honest, it has slowed down development for me.&lt;/p&gt;

&lt;p&gt;So when I built Khrome.dev earlier in the year, with the goal of building it in a single day, I choose to use &lt;a href="https://tailwindcss.com/"&gt;Tailwind CSS&lt;/a&gt; as an experiment. It felt like moving back in time in a lot of ways, and I was really drawn to the approach of composing classes in HTML. Basically, never having used it before, it felt simple and easy to grasp. And it got out of my way to let me build quickly.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;For those not familiar, Tailwind CSS is a utility-first framework that really tries to focus on giving you the building blocks to craft what you need. I am very much against CSS frameworks because of the opinionated styles. But Tailwind CSS is different, it's just basic properties with some well-defined defaults. Anything I didn't like, I replaced.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The Easiest Refactor Ever
&lt;/h2&gt;

&lt;p&gt;Due to a project at work, I decided to migrate my personal blog I had just launched from Vue to Svelte. It was an experiment to ensure the technology choice was a wise one. I ended up liking the outcome and merging the branch into the master branch at the beginning of October.&lt;/p&gt;

&lt;p&gt;The refactor took a lot longer than expected, but not because of Svelte or Vue. It was replacing the functionality I was getting from Gridsome that really cost me time. I had to replace the GraphQL file system interface to processing the markdown files. I also had to write custom code to handle the RSS feed and sitemap generation.&lt;/p&gt;

&lt;p&gt;The only part that was not hard, was the styling. The act of actually migrating the HTML and Styles from Vue to Svelte.&lt;/p&gt;

&lt;h3&gt;
  
  
  Refactoring a Component
&lt;/h3&gt;

&lt;p&gt;Vue uses a template structure that is basically HTML with some custom templates. Here is my Hero tile on the homepage of Khrome.dev. I choose a simple presentation component on purpose.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt;
    &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"bg-regal-blue text-white overflow-hidden bg-repeat"&lt;/span&gt;
    &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"background-image: linear-gradient(to bottom, rgba(40,69,105,0) 0%,rgba(36,60,90,1) 80%), url('./dots.svg');"&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"hero container-inner mx-auto flex flex-col justify-between py-16"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mt-8 sm:mt-0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;g-image&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"../../static/code_review.svg"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"hero"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mx-auto sm:mx-0"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-4xl font-bold w-full text-center mt-16"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-white"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          I build
          &lt;span class="nt"&gt;&amp;lt;strong&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-orange-700"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;products&lt;span class="nt"&gt;&amp;lt;/strong&amp;gt;&lt;/span&gt;  &lt;span class="ni"&gt;&amp;amp;amp;&lt;/span&gt; 
          &lt;span class="nt"&gt;&amp;lt;strong&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-orange-700"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;teams&lt;span class="nt"&gt;&amp;lt;/strong&amp;gt;&lt;/span&gt;.
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is the same component in Svelte.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt;
    &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"bg-regal-blue text-white overflow-hidden bg-repeat"&lt;/span&gt;
    &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"background-image: linear-gradient(to bottom, rgba(40,69,105,0) 0%,rgba(36,60,90,1) 80%), url('./dots.svg');"&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"hero container-inner mx-auto flex flex-col justify-between py-16"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mt-8 sm:mt-0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"../../static/code_review.svg"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"hero"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mx-auto sm:mx-0"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-4xl font-bold w-full text-center mt-16"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-white"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        I build
        &lt;span class="nt"&gt;&amp;lt;strong&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-orange-700"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;products&lt;span class="nt"&gt;&amp;lt;/strong&amp;gt;&lt;/span&gt;  &lt;span class="ni"&gt;&amp;amp;amp;&lt;/span&gt; 
        &lt;span class="nt"&gt;&amp;lt;strong&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-orange-700"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;teams&lt;span class="nt"&gt;&amp;lt;/strong&amp;gt;&lt;/span&gt;.
      &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is really difficult to notice the difference. Basically the &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; tag needed to be removed, and the Gridsome specific &lt;code&gt;&amp;lt;g-image&amp;gt;&lt;/code&gt; tag needed to be replaced with a plain old HTML compliant &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Benefits of Utility-first frameworks
&lt;/h2&gt;

&lt;p&gt;At first glance, this may seem like a silly example. But that is the beautify. For every line template code that I migrated over, I only brought with it the CSS I used.&lt;/p&gt;

&lt;p&gt;Another way to look at this --- during the redesign I made a few decisions, like removing the search interface. Those components were not brought over, and the CSS was not included automatically.&lt;/p&gt;

&lt;p&gt;Yes, you can get some of those benefits from CSS in JS or having component-specific CSS files. But I did not have to change any tooling. And if I removed part of a component, the CSS for that piece is gone as well.&lt;/p&gt;

&lt;p&gt;Utility-first frameworks like Tailwind CSS, are an elegant and simple solution to solving the issues we all face when refactoring or removing dead HTML. How do we ensure the CSS is also correctly removed?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://khrome.dev/blog/an-unexpected-benefit-of-tailwind-css"&gt;Khrome.dev&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>svelte</category>
      <category>vue</category>
      <category>tailwindcss</category>
      <category>css</category>
    </item>
    <item>
      <title>The Rise of Privacy Focused Analytics</title>
      <dc:creator>Zane Milakovic</dc:creator>
      <pubDate>Mon, 02 Dec 2019 00:00:00 +0000</pubDate>
      <link>https://forem.com/khrome83/the-rise-of-privacy-focused-analytics-3m41</link>
      <guid>https://forem.com/khrome83/the-rise-of-privacy-focused-analytics-3m41</guid>
      <description>&lt;p&gt;Privacy is growing to be a more significant concern in your user's minds. Every week it seems that a new scandal is surfacing over at Facebook that is making headlines. Or we have a smart gadget that hides a microphone for years. Zoom's teleconference software was recently revealed with a security mistake. It was installing a hidden unsecured server for the 's consent. The trending headlines teach us not to trust the tech companies with our information. Why should your users trust you?&lt;/p&gt;

&lt;p&gt;Changes are happening in the public eye. The EU signed the GDPR in 2016, the biggest data privacy regulation in over 20 years. Companies recently facing scrutiny are scrambling to try to change their public image. Yes, Facebook announced on the switch to be a privacy-focused company, but they rely on ad revenue. How can they be?&lt;/p&gt;

&lt;p&gt;How can we trust these mega tech corporations to power our analytics? Actionable insights these tools provide come at a cost, the abuse of our user's data. There is a reason Google Analytics is free. The data they collect is the payment, and then some. It's time you and I are responsible for our users. After all, we both want to make products that users can trust.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Data We Need
&lt;/h2&gt;

&lt;p&gt;The point of website analytics is to delve information that is meaningful to us about our users. The information we need boils down to a few themes.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We need to understand growth.&lt;/li&gt;
&lt;li&gt;We need to understand some basics about our audience.&lt;/li&gt;
&lt;li&gt;We need to understand what technology we need to support.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Growth
&lt;/h3&gt;

&lt;p&gt;This is a pretty basic concept that has been a staple since the early days of the web. Are we gaining or losing visitors? Where are those visitors going on our site? How are they getting to our site?&lt;/p&gt;

&lt;h3&gt;
  
  
  Demographics
&lt;/h3&gt;

&lt;p&gt;As the internet grew and sites became more global, we started asking about our users. From what countries are our visitors from? Are we supporting the locales?&lt;/p&gt;

&lt;h3&gt;
  
  
  Technology
&lt;/h3&gt;

&lt;p&gt;We need to understand how to support our audience. Techniques have exploded for capturing user data. The devices to access the web have changed. There is more surface area than ever. What browsers are they using? What physical devices did they connect with? What was the screen size of those devices?&lt;/p&gt;

&lt;p&gt;These basic data concepts can power a wave of changes to our business from changing where to advertise due to inbound sources changing. Or we may focus on our mobile experience because the user base shifted over the last few months.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Cost of Privacy
&lt;/h2&gt;

&lt;p&gt;Privacy-focused solutions tend to capture a lot less data. Does this make them less beneficial? But it depends on your specific needs. Analytics suites tend to have a lot of tools that go underused and don't serve any of the sites using them. But your paying for these extra features with the data you capture about your users.&lt;/p&gt;

&lt;p&gt;Most of the information our business need to get recorded in privacy-focused tools. So what is the difference? What are you giving up? The answer is it depends.&lt;/p&gt;

&lt;p&gt;A privacy-focused toolset means we may understand less about our users. But we were also less invasive and more transparent. Google Analytics, for example, shows demographic data around age or gender. It does so by marring data about your users from across the internet. Your site visitors viewing habits and interests are also identified. Many of these are invasive data points. Yes, they may help you target an ad campaign. After all ad buying one of Google's primary sources of revenue. So if we lose all that information, how do we capture it? What did we do before Google Analytics? We use to survey our audience. The users self identify, and opt into questions they were willing to answer. And it was responsible and transparent.&lt;/p&gt;

&lt;p&gt;Privacy-focused analytics can have a higher cost. Either a monetary cost like a monthly fee. Even though the fee is very low, it can feel hard to justify if you spent years not pay for Google Analytics. Or the tools need more overhead like self-hosting, infrastructure, and technical setup.&lt;/p&gt;

&lt;p&gt;You will have to judge for yourself if you can live without the features, or do the setup. But the result is you will be no longer tracking unneeded data about your users.&lt;/p&gt;

&lt;h2&gt;
  
  
  The New Breed of Analytics
&lt;/h2&gt;

&lt;p&gt;Privacy-focused analytics is not new. In the early days of the web analytics captured very little information. The mechanisms to capture data and marry data were not available. Yet, we use to drown ourselves in our site graphs month to month charting success.&lt;/p&gt;

&lt;p&gt;Google Analytics and others of its ilk were sparks that lit the fire. It moved analytics off our systems and small providers and into the cloud. It expanded analytical capabilities. It drove insights into our business and taught us new questions to answer. It gave us insights on how to spend on ads. It became self-serving to the corporation and could remain free. In the beginning, it might have been innocent. Remember, Google’s motto used to be "Don't be evil." It's hard to argue that is still the case.&lt;/p&gt;

&lt;p&gt;Recently, several companies have released privacy-focused analytics. These products give us most of the capabilities we need.&lt;/p&gt;

&lt;h3&gt;
  
  
  Simple Analytics
&lt;/h3&gt;

&lt;p&gt;This software is based on a premise, let’s make analytics simple. And the creator &lt;a href="https://twitter.com/intent/user?screen_name=AdriaanvRossum"&gt;@AdriaanvRossum&lt;/a&gt; delivers on exactly that goal.&lt;/p&gt;

&lt;p&gt;You type the domain you are going to add, and you’re left with this snipped.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;async&lt;/span&gt; &lt;span class="na"&gt;defer&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.simpleanalytics.io/hello.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;noscript&amp;gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://api.simpleanalytics.io/hello.gif"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/noscript&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once inserted into your site, the analytics page automatically updates with data. Pretty slick!&lt;/p&gt;

&lt;p&gt;This is the shortest and easiest analytics snipped I have seen. There is no complicated tracking code or additional javascript closures inserted into the page. Just a linked file off of a CDN, and a small &lt;code&gt;.gif&lt;/code&gt; for tracking sites when JavaScript is disabled.&lt;/p&gt;

&lt;p&gt;There is a really great benefit for not having a unique tracking code. I don’t have to disable tracking in my lower environments and review apps. Because they are not &lt;code&gt;khrome.dev&lt;/code&gt; or &lt;code&gt;www.khrome.dev&lt;/code&gt; they are simply not counted. Pretty convenient since I host on with &lt;a href="https://zeit.co"&gt;zeit.co&lt;/a&gt; and make great use of review deployments.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hA0l5IDH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/khromedotdev/image/upload/c_scale%2Cw_auto:100%2Cdpr_auto%2Cf_auto%2Cq_auto/v1570448523/simple_analytics_u4pusp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hA0l5IDH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/khromedotdev/image/upload/c_scale%2Cw_auto:100%2Cdpr_auto%2Cf_auto%2Cq_auto/v1570448523/simple_analytics_u4pusp.png" alt="https://res.cloudinary.com/khromedotdev/image/upload/c" width="800" height="823"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Of all the examples here, Simple Analytics has very high scrutiny in the data they store, to make sure no processes could be considered device fingerprinting. Based out of Europe, they are very cautious to make sure their clients do not need to have any disclaimer based on the tracked metrics. For example, IP addresses are not stored, even in an anonymous hashed form. This an in compliance with &lt;a href="https://ico.org.uk/for-organisations/guide-to-pecr/guidance-on-the-use-of-cookies-and-similar-technologies/what-are-cookies-and-similar-technologies/#cookies5"&gt;PECR&lt;/a&gt; in the UK. And all of the data is stored in Iceland, which has one of the highest standards for data protection and encryption. To&lt;/p&gt;

&lt;p&gt;I am currently using Simple Analytics on my own site. Based on the research I have done, they seem to be the most compliant, especially in Europe. I have found the experience to be as easy as described. Visit &lt;a href="https://referral.simpleanalytics.com/zane-milakovic"&gt;Simple Analytics&lt;/a&gt; to sign up. Using this link will give you a 7-day trial, plus one month free!&lt;/p&gt;

&lt;h3&gt;
  
  
  Fathom Analytics
&lt;/h3&gt;

&lt;p&gt;The brainchild of Jack Ellis and Paul Jarvis, two independent developers. They also wanted to build a privacy-focused analytics solution.&lt;/p&gt;

&lt;p&gt;One of the more interesting things about Fathom Analytics is the amount of care and work put into tracking more than just pageviews. Fathom Analytics uses a fairly complex hashing solution with table lookup to estimate the number of unique visitors and unique pageviews. What is critical though, is that they can not determine the individual path of a single user. You can read more about that in a post they wrote about &lt;a href="https://usefathom.com/anonymization/"&gt;anonymization&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--n1A1gkTo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/khromedotdev/image/upload/c_scale%2Cw_auto:100%2Cdpr_auto%2Cf_auto%2Cq_auto/v1575342260/Screen_Shot_2019-12-02_at_9.03.50_PM_tfsg8y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--n1A1gkTo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/khromedotdev/image/upload/c_scale%2Cw_auto:100%2Cdpr_auto%2Cf_auto%2Cq_auto/v1575342260/Screen_Shot_2019-12-02_at_9.03.50_PM_tfsg8y.png" alt="https://res.cloudinary.com/khromedotdev/image/upload/c" width="800" height="841"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While version 1.0 seemed to have issues with my SPA, version 2.0 promised to fix these problems. I have not tested this since last summer with version 1.0. But I know Paul Jarvis was receptive of the feedback on Twitter and promised to address it in version 2.0.&lt;/p&gt;

&lt;p&gt;What is really promising about Fathom Analytics, is the amount of data that is captured. The presentation is not only one of the strongest in version 2.0 of the data. But it includes metrics not found elsewhere, like "Average Time on Site", "Bounce Rate" and "Unique Visits". I am a really big fan of the previous months' comparisons as well. Clicking on "N Current Visitors" also gets you a great breakdown of the pages that are currently being viewed. A pretty nifty trick that makes the whole interface feel a little smoother.&lt;/p&gt;

&lt;h3&gt;
  
  
  Netlify Analytics
&lt;/h3&gt;

&lt;p&gt;This addition to privacy-focused analytics is very new. Launching just a few weeks ago, it is already making some headlines. You do need to be a Netlify customer and pay $9 a month for the feature though, which does appear to be fairly limited in data.&lt;/p&gt;

&lt;p&gt;One of the coolest tricks is that you don’t need to install it. Once you activate it, it's live on your website, using data the CDN captures. Which basically means you don't have any client-side JavaScript that is capturing the data. It is a very neat trick.&lt;/p&gt;

&lt;h4&gt;
  
  
  Bandwidth Bar Chart
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--clJpF8t9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/khromedotdev/image/upload/c_scale%2Cw_auto:100%2Cdpr_auto%2Cf_auto%2Cq_auto/v1570448576/netlify_bandwidth_xzk1ev.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--clJpF8t9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/khromedotdev/image/upload/c_scale%2Cw_auto:100%2Cdpr_auto%2Cf_auto%2Cq_auto/v1570448576/netlify_bandwidth_xzk1ev.png" alt="https://res.cloudinary.com/khromedotdev/image/upload/c" width="800" height="405"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Sources Table
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OyU-VqUg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/khromedotdev/image/upload/c_scale%2Cw_auto:100%2Cdpr_auto%2Cf_auto%2Cq_auto/v1570448576/netlify_sources_aehvzn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OyU-VqUg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/khromedotdev/image/upload/c_scale%2Cw_auto:100%2Cdpr_auto%2Cf_auto%2Cq_auto/v1570448576/netlify_sources_aehvzn.png" alt="https://res.cloudinary.com/khromedotdev/image/upload/c" width="800" height="404"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Pageviews Chart
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DSz6r0zP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/khromedotdev/image/upload/c_scale%2Cw_auto:100%2Cdpr_auto%2Cf_auto%2Cq_auto/v1570448576/netlify_pageviews_rspoyw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DSz6r0zP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/khromedotdev/image/upload/c_scale%2Cw_auto:100%2Cdpr_auto%2Cf_auto%2Cq_auto/v1570448576/netlify_pageviews_rspoyw.png" alt="https://res.cloudinary.com/khromedotdev/image/upload/c" width="800" height="367"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://khrome.dev/blog/the-rise-of-privacy-focused-analytics"&gt;Khrome.dev&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>analytics</category>
      <category>privacy</category>
      <category>web</category>
    </item>
    <item>
      <title>Creating Compelling and Useful 404 Pages</title>
      <dc:creator>Zane Milakovic</dc:creator>
      <pubDate>Sun, 01 Dec 2019 00:00:00 +0000</pubDate>
      <link>https://forem.com/khrome83/creating-compelling-and-useful-404-pages-2n6m</link>
      <guid>https://forem.com/khrome83/creating-compelling-and-useful-404-pages-2n6m</guid>
      <description>&lt;p&gt;A custom 404 page has become a hallmark of well-polished websites. It allows expressing the creative brand of the site. Sometimes it shows a neat trick and easter eggs.&lt;/p&gt;

&lt;h2&gt;
  
  
  404 Page Examples
&lt;/h2&gt;

&lt;p&gt;Take &lt;a href="https://dev.to/404"&gt;dev.to&lt;/a&gt; 404 pages as an example. It is an incredibly simple page that shows the glitching gif logo of the dev.to brand. It does not offer anything extra, except a link back home.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NLGCs3Q1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://res.cloudinary.com/khromedotdev/image/upload/c_scale%2Cw_auto:100%2Cdpr_auto%2Cf_auto%2Cq_auto/v1570448524/dev_to_404_nfgiri.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NLGCs3Q1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://res.cloudinary.com/khromedotdev/image/upload/c_scale%2Cw_auto:100%2Cdpr_auto%2Cf_auto%2Cq_auto/v1570448524/dev_to_404_nfgiri.gif" alt="An animated box with the word DEV flashing and erratic." title="Dev.to 404 Page" width="800" height="560"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/404"&gt;GitHub&lt;/a&gt; takes things a step future with their 404 pages. They use Star Wars as a pop culture reference to relate to the inner geek in me. GitHub goes a step further than most sites by tracking the users' mouse movement to create a parallax effect. I remember that this brought me a moment of joy when I found this page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PUwlvquU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://res.cloudinary.com/khromedotdev/image/upload/c_scale%2Cw_auto:100%2Cdpr_auto%2Cf_auto%2Cq_auto/v1570448524/github_404_ryiozm.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PUwlvquU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://res.cloudinary.com/khromedotdev/image/upload/c_scale%2Cw_auto:100%2Cdpr_auto%2Cf_auto%2Cq_auto/v1570448524/github_404_ryiozm.gif" alt="A animage screen of GitHubs 404 page with a SciFi scene moving around the screen in relation to the mouse cursor." title="GitHub 404 Page" width="800" height="540"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Another enjoyable example is from the folks over at &lt;a href="https://www.carwow.co.uk/404.html"&gt;Carwow&lt;/a&gt; in the UK. They build a mini-game into the 404 page that feels very retro-inspired. So while not the most practical 404 pages, it gave the development team someplace to show off. The brand charm comes through when you lose, where they try to convert you to a test drive.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KyWZrBf8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://res.cloudinary.com/khromedotdev/image/upload/c_scale%2Cw_auto:100%2Cdpr_auto%2Cf_auto%2Cq_auto/v1575251153/carwow_xp5xyk.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KyWZrBf8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://res.cloudinary.com/khromedotdev/image/upload/c_scale%2Cw_auto:100%2Cdpr_auto%2Cf_auto%2Cq_auto/v1575251153/carwow_xp5xyk.gif" alt="https://res.cloudinary.com/khromedotdev/image/upload/c" width="459" height="211"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Suggestions Useful 404 Pages
&lt;/h2&gt;

&lt;p&gt;There is much debate about what makes a useful 404 page. Dev.to does not provide many methods to recover, while GitHub by contract shows the full header and footer of the website and a second search bar.&lt;/p&gt;

&lt;p&gt;In general, it's hard to say anything negative about giving help to users landing on a 404 page so they can recover. Bad 404 pages can cause users to leave the site altogether. A useful 404 page conveys to the user you care about helping them find the relevant information. It could also mean saving a customer or creating a new conversion.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Show consistent navigation so users can self recover.&lt;/li&gt;
&lt;li&gt;Provide a secondary search to encourage users to find the content they seek.&lt;/li&gt;
&lt;li&gt;Make suggestions based on the users' search history, current trending content, or just what you want to feature.&lt;/li&gt;
&lt;li&gt;Provide links to any monitoring or status pages in case the 404 page shown was by some other error.&lt;/li&gt;
&lt;li&gt;Show your brand and messaging off and delight.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  My Blogs 404 Page
&lt;/h3&gt;

&lt;p&gt;For my site, I decided to leave things clean and minimalistic during the launch of my website. I left the header and footer on the page. I then spruced it up with an excellent illustration from &lt;a href="https://undraw.co"&gt;unDraw.io&lt;/a&gt; that I color matched.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OA8ioquw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/khromedotdev/image/upload/c_scale%2Cw_auto:100%2Cdpr_auto%2Cf_auto%2Cq_auto/v1570448524/custom_404_kgq3ro.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OA8ioquw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/khromedotdev/image/upload/c_scale%2Cw_auto:100%2Cdpr_auto%2Cf_auto%2Cq_auto/v1570448524/custom_404_kgq3ro.png" alt="A illustration of a man holding a map and looking confused." title="Khrome.dev 404 Page" width="800" height="641"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://khrome.dev/blog/creating-compelling-and-useful-404-pages"&gt;Khrome.dev&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>404</category>
    </item>
    <item>
      <title>Would you build social images with code?</title>
      <dc:creator>Zane Milakovic</dc:creator>
      <pubDate>Wed, 31 Jul 2019 02:03:36 +0000</pubDate>
      <link>https://forem.com/khrome83/would-you-build-social-images-with-code-38j6</link>
      <guid>https://forem.com/khrome83/would-you-build-social-images-with-code-38j6</guid>
      <description>&lt;p&gt;Really simple question. If I build an interface allowing for the sharing and creation of themes for social images, would you use it?&lt;/p&gt;

&lt;p&gt;Imaging coding social twitter images, or dev.to banners in HTML and CSS and sharing that theme with others. Then being able to reuse it easily to create other social images. &lt;/p&gt;

&lt;p&gt;Interested? &lt;/p&gt;

&lt;p&gt;This is inspired by the work I did here &lt;a href="https://khrome.dev/image"&gt;khrome.dev of image generator&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If people are interested, I will put a signup page up and start building it this weekend. &lt;/p&gt;

&lt;p&gt;What features would you need to use this?&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>product</category>
      <category>html</category>
      <category>css</category>
    </item>
    <item>
      <title>Custom Decorators with Storybook &amp; Vue</title>
      <dc:creator>Zane Milakovic</dc:creator>
      <pubDate>Thu, 18 Jul 2019 00:00:00 +0000</pubDate>
      <link>https://forem.com/khrome83/custom-decorators-with-storybook-vue-1pbd</link>
      <guid>https://forem.com/khrome83/custom-decorators-with-storybook-vue-1pbd</guid>
      <description>&lt;p&gt;Storybook has excellent Vue support. While it did not support Vue at launch, it now does. So it has become my goto technology while fleshing out base components. It is critical to my development process, and I think it should be considered for your process as well.&lt;/p&gt;

&lt;p&gt;No longer do I need to stub out pages or hack together a test page. Instead, I can focus on my design language. Each story is a base component, making it incredibly clear and more comfortable to process. It has sped up my development in unexpected ways.&lt;/p&gt;

&lt;p&gt;The ecosystem within Storybook also covers many of my concerns. I love the "knobs" plugin. It allows me to stress test each component by mixing settings. The a11y plugin gives me a high-level view of the current state of the component to ensure that everything is accessible. To simulate different viewports, I use the breakpoint component. These tools ensure that I think about these concerns much earlier in my process. Also, my work is better for it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;I did run into a small issue recently, in any case. How to build for multiple themes at once? One of the patterns I rely on is the use of background colors to modify the pallet in the foreground. I like the concept of breaking up the page. Moreover, as content shifts in my marketing pages, I want the flexibility to change the pallet on the fly. Here is an example of the same signup banner across three different pallets.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fkhrome.dev%2Fuploads%2Fmultiple_themes.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fkhrome.dev%2Fuploads%2Fmultiple_themes.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So I want to demonstrate with a simple &lt;code&gt;&amp;lt;base-badge&amp;gt;&lt;/code&gt; component. This component takes a label in its default slot and shows it in a colored badge. It is excellent for notification counts and tagging content. Here is an example of it in Storybook.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fkhrome.dev%2Fuploads%2Fbadge-on-single-background.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fkhrome.dev%2Fuploads%2Fbadge-on-single-background.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  BaseBadge Breakdown
&lt;/h3&gt;

&lt;p&gt;Here is the &lt;code&gt;BaseBadge.vue&lt;/code&gt; file.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Note&lt;/em&gt; - This uses TypeScript and &lt;code&gt;nuxt-property-decorator&lt;/code&gt; of which I am a massive fan.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"badge"&lt;/span&gt; &lt;span class="na"&gt;:class=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;{ secondary, small, large }"&amp;gt;
    &lt;span class="nt"&gt;&amp;lt;slot&amp;gt;&amp;lt;/slot&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"ts"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Prop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Vue&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nuxt-property-decorator&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="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BaseBadge&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Vue&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Prop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kr"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;secondary&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Prop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kr"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;small&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Prop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kr"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;large&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;style&lt;/span&gt; &lt;span class="na"&gt;scoped&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nc"&gt;.badge&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;inline-block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;text-align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#fff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#fff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#ee0028&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2px&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;4px&lt;/span&gt; &lt;span class="m"&gt;6px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;text-decoration&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="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;400&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;1.1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;letter-spacing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.025rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;text-transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;uppercase&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* Grey Modifications - Badge */&lt;/span&gt;
&lt;span class="nc"&gt;.__bg-grey&lt;/span&gt; &lt;span class="nc"&gt;.badge&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#da0629&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#fdfcfb&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#fdfcfb&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* Dark Modifications - Badge */&lt;/span&gt;
&lt;span class="nc"&gt;.__bg-dark&lt;/span&gt; &lt;span class="nc"&gt;.badge&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#f32144&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#010b19&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#010b19&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.secondary&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#010b19&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* Grey Modifications - Secondary */&lt;/span&gt;
&lt;span class="nc"&gt;.__bg-grey&lt;/span&gt; &lt;span class="nc"&gt;.secondary&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#010b19&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#fdfcfb&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#fdfcfb&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* Dark Modifications - Secondary */&lt;/span&gt;
&lt;span class="nc"&gt;.__bg-dark&lt;/span&gt; &lt;span class="nc"&gt;.secondary&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#ffffff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#010b19&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#010b19&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.small&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;14px&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;4px&lt;/span&gt; &lt;span class="m"&gt;8px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;line-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;letter-spacing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.25&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.large&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;20px&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;6px&lt;/span&gt; &lt;span class="m"&gt;12px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;letter-spacing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.1rem&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;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;style&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For those new to Vue and TypeScript, I am going to break this down quickly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"badge"&lt;/span&gt; &lt;span class="na"&gt;:class=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;{ secondary, small, large }"&amp;gt;
    &lt;span class="nt"&gt;&amp;lt;slot&amp;gt;&amp;lt;/slot&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The template section is relatively standard and straightforward Vue. We are creating a span that contains the text passed to the default slot. The interface accepts a size and a color pallet. The default is assumed to be standard size and primary color pallet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"ts"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Prop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Vue&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nuxt-property-decorator&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="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BaseBadge&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Vue&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Prop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kr"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;secondary&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Prop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kr"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;small&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Prop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kr"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;large&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the &lt;code&gt;&amp;lt;script lang=" ts"&amp;gt;&lt;/code&gt; where we tell Vue to process this as TypeScript.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;import&lt;/code&gt; line is used to pull in our decorators and classes from &lt;a href="https://github.com/nuxt-community/nuxt-property-decorator" rel="noopener noreferrer"&gt;nuxt-property-decorator&lt;/a&gt; which is a nice wrapper around four other modules. This just cleans up the interface instead of having to pull in &lt;a href="https://github.com/kaorun343/vue-property-decorator" rel="noopener noreferrer"&gt;vue-property-decorator&lt;/a&gt;, &lt;a href="https://github.com/vuejs/vue-class-component" rel="noopener noreferrer"&gt;vue-class-component&lt;/a&gt;, &lt;a href="https://github.com/ktsn/vuex-class/" rel="noopener noreferrer"&gt;vuex-class&lt;/a&gt;, and &lt;a href="https://github.com/nuxt-community/nuxt-class-component" rel="noopener noreferrer"&gt;nuxt-class-component&lt;/a&gt; separately.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;@Component({})&lt;/code&gt; decorator defines the class as a component. The &lt;code&gt;@Prop(Boolean)&lt;/code&gt; defines props for the Vue component. Notice that &lt;code&gt;Boolean&lt;/code&gt;/&lt;code&gt;boolean&lt;/code&gt; repeated during the prop declaration, this sets up both TypeScript type checking and Vue prop type checking. Notice that we do not have any state or logic to deal with inside the Vue component. I wanted to focus on the CSS instead.&lt;/p&gt;

&lt;h3&gt;
  
  
  CSS Styling
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.secondary&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#010b19&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* Grey Modifications - Secondary */&lt;/span&gt;
&lt;span class="nc"&gt;.__bg-grey&lt;/span&gt; &lt;span class="nc"&gt;.secondary&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#010b19&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#fdfcfb&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#fdfcfb&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* Dark Modifications - Secondary */&lt;/span&gt;
&lt;span class="nc"&gt;.__bg-dark&lt;/span&gt; &lt;span class="nc"&gt;.secondary&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#ffffff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#010b19&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#010b19&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;style&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looking at just a small subset of the CSS, you can see that we are modifying &lt;code&gt;.secondary&lt;/code&gt; three times. The default pallet is considered the "Primary" theme and sets the background color. When the component is within an element with the &lt;code&gt;.__bg-grey&lt;/code&gt; or &lt;code&gt;.__bg-dark&lt;/code&gt; class applied, it gets modified.&lt;/p&gt;

&lt;p&gt;The structure I use for different sections within my site is to have a section define the background color. Then the contents within respond to that.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"__bg-dark"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;base-badge&lt;/span&gt; &lt;span class="na"&gt;secondary&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The goal is to ensure that as the parent section changes the theme, all the children behave accordingly, modifying their color pallets accordingly.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Storybook Story
&lt;/h2&gt;

&lt;p&gt;The Storybook setup is relatively basic. It uses the standard patterns for using Vue within Storybook, as well as a few "Knobs."&lt;/p&gt;

&lt;h3&gt;
  
  
  Basic Storybook Story
&lt;/h3&gt;

&lt;p&gt;First, we import &lt;code&gt;storiesOf&lt;/code&gt; method from the &lt;code&gt;@storybook/vue&lt;/code&gt; module. The method allows us to create a story and define it in a namespace. We also import our &lt;code&gt;BaseBadge.vue&lt;/code&gt; component.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Tip - I place all my base component within a folder called "Components." The purpose is to make it clear to others what components can be combined to create more significant sets of components. Base Components typically have minimal to no state and are the lowest level component in Vue.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;storiesOf&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@storybook/vue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;BaseBadge&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../components/BaseBadge.vue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stories&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;storiesOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Components/Base Badge&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;module&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we are going to display the &lt;code&gt;BaseBadge&lt;/code&gt; on the page. We add the story to Storybook using the &lt;code&gt;stories.add&lt;/code&gt; method. I name the page for all root instances of my components "Default" unless I have different implementation setups.&lt;/p&gt;

&lt;p&gt;The template is just a simple ES6 template string that exports the same contents you would have in a Vue template.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;stories&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Default&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;BaseBadge&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
        &amp;lt;base-badge&amp;gt;
          New
        &amp;lt;/base-badge&amp;gt;
      `&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Adding Knobs
&lt;/h3&gt;

&lt;p&gt;We have a basic rendering, but we can't test any of the stress cases. We want to be able to change the text, modify the size and color pallet. First, we want to import the interface types we need from &lt;code&gt;@storybook/addon-knobs&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;radios&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="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@storybook/addon-knobs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we want to extend the story definition object to include a props object that tells Storybook what "knobs" to enable and the rules and labels for each knob. In this case, we use &lt;code&gt;text&lt;/code&gt; knob to define the content within the badge. The &lt;code&gt;radio&lt;/code&gt; knob is used to select the theme and size of the badge.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;BaseBadge&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="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="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&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;Text&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;NEW&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;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nl"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;radios&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Theme&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;Primary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;primary&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;Secondary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;secondary&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;primary&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;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nl"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;radios&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Size&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;Small&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;small&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;Normal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;normal&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;Large&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;large&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;normal&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
          &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="nx"&gt;template&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;Lastly, we want to modify the template to use the values from these knobs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;            &lt;span class="nt"&gt;&amp;lt;base-badge&lt;/span&gt; &lt;span class="na"&gt;v-bind=&lt;/span&gt;&lt;span class="s"&gt;"{
              secondary: theme === 'secondary',
              small: size === 'small',
              large: size === 'large',
            }"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
              {{text}}
            &lt;span class="nt"&gt;&amp;lt;/base-badge&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is the whole story completed. While we have not solved for the multiple background colors, we have built the Vue component and the Storybook story for it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;storiesOf&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@storybook/vue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;radios&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;boolean&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@storybook/addon-knobs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;BaseBadge&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../components/BaseBadge.vue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stories&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;storiesOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Components/Base Badge&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;module&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;stories&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Default&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;BaseBadge&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&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;Text&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;NEW&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;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;radios&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Theme&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;Primary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;primary&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;Secondary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;secondary&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;primary&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;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;radios&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Size&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;Small&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;small&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;Normal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;normal&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;Large&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;large&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;normal&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
          &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
        &amp;lt;base-badge v-bind="{
          secondary: theme === 'secondary',
          small: size === 'small',
          large: size === 'large',
        }"&amp;gt;
          {{text}}
        &amp;lt;/base-badge&amp;gt;
      `&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So far we have built this. The thing I showed you in the beginning.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fkhrome.dev%2Fuploads%2Fbadge-on-single-background.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fkhrome.dev%2Fuploads%2Fbadge-on-single-background.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Building our Decorator
&lt;/h3&gt;

&lt;p&gt;Storybook uses decorators to extend the functionality of a story. These decorators look a little different than the ES7 decorators we see in typescript, but the concept is similar. We want to extend the functionality of the core object and introduce new behavior.&lt;/p&gt;

&lt;p&gt;Building a decorator in Storybook is reasonably straightforward. It is just an export of an object from a method. The &lt;code&gt;data&lt;/code&gt; method within the object is used to return properties for the template. The &lt;code&gt;template&lt;/code&gt; then has access to anything data returns.&lt;/p&gt;

&lt;p&gt;In this case, the data method is returning objects of CSS styles. This object follows the rules of using styles in JS for Vue. So &lt;code&gt;box-shadow&lt;/code&gt; becomes &lt;code&gt;boxShadow&lt;/code&gt; and is the key while the value is a string of the contents for that CSS property.&lt;/p&gt;

&lt;p&gt;We then use &lt;code&gt;:style="wrapper"&lt;/code&gt; bindings to apply those styles to the HTML elements.&lt;/p&gt;

&lt;p&gt;Finally, the &lt;code&gt;&amp;lt;story /&amp;gt;&lt;/code&gt; component within the template, tells Storybook where to inject our root story. For every instance of &lt;code&gt;&amp;lt;story /&amp;gt;&lt;/code&gt;, Storybook makes a copy of the template the decorator is applied too. We want this applied to our &lt;code&gt;&amp;lt;base-badge&amp;gt;&lt;/code&gt; template.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sectionStates&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0 2rem 2rem&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;thin solid transparent&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;boxShadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rgba(0, 0, 0, 0.15) 0rem 0.125rem 0.3125rem 0rem&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;borderRadius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0.3125rem&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2rem&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;light&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;backgroundColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#ffffff&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;grey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;backgroundColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#fdfcfb&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;boxShadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rgba(0, 0, 0, 0.2) 0rem 0.125rem 0.3125rem 0rem&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;backgroundColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#010b19&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;boxShadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rgba(0, 0, 0, 0.5) 0rem 0.125rem 0.3125rem 0rem&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;heading&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;fontSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0.75rem&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0.5rem 0 0.5rem 2rem&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#737373&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;textTransform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;uppercase&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;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
    &amp;lt;div&amp;gt;
      &amp;lt;div :style="heading"&amp;gt;On Light Background&amp;lt;/div&amp;gt;
      &amp;lt;div class="__bg-light" :style="[wrapper, light]"&amp;gt;&amp;lt;story/&amp;gt;&amp;lt;/div&amp;gt;
      &amp;lt;div :style="heading"&amp;gt;On Gray Background&amp;lt;/div&amp;gt;
      &amp;lt;div class="__bg-grey" :style="[wrapper, grey]"&amp;gt;&amp;lt;story/&amp;gt;&amp;lt;/div&amp;gt;
      &amp;lt;div :style="heading"&amp;gt;On Dark Background&amp;lt;/div&amp;gt;
      &amp;lt;div class="__bg-dark" :style="[wrapper, dark]"&amp;gt;&amp;lt;story/&amp;gt;&amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
    `&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;sectionStates&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The thing that makes this work with multiple backgrounds is the inclusion of the &lt;code&gt;.__bg-light&lt;/code&gt;, &lt;code&gt;.__bg-grey&lt;/code&gt;, and &lt;code&gt;.__bg-dark&lt;/code&gt; CSS classes. These are using in my global styles to augment any children.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note - I called this &lt;strong&gt;sectionStates&lt;/strong&gt; because the standard Vue component that defines the background color in my project is a section. So it has a few colored states that children components have to react too.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Including our Decorator
&lt;/h3&gt;

&lt;p&gt;The next step is to make use of this decorator in the story we built earlier. First, we want to add the &lt;code&gt;addDecorator&lt;/code&gt; method to our imports. This method is used to apply custom decorators to Storybook stories.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;sectionStates&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../utils/sectionStates.ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;storiesOf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;addDecorator&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@storybook/vue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we chain the &lt;code&gt;storesOf&lt;/code&gt; method and call &lt;code&gt;addDecorator&lt;/code&gt; method passing in &lt;code&gt;sectionStates&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stories&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;storiesOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Components/Base Badge&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;module&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;addDecorator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;sectionStates&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output is three instances instead of one. Each instance has a different background color. Every child within each instance is respecting its parents' container. The outcome perfectly mimics the behavior of the &lt;code&gt;BaseSection.vue&lt;/code&gt; component.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fkhrome.dev%2Fuploads%2Fbadge-on-multiple-backgrounds.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fkhrome.dev%2Fuploads%2Fbadge-on-multiple-backgrounds.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As a bonus, this allows us to validate the accessibility of each change. We see all the possibilities across all backgrounds pallets.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fkhrome.dev%2Fuploads%2Fbadge-with-a11y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fkhrome.dev%2Fuploads%2Fbadge-with-a11y.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;This pattern is beneficial in many situations -&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Building multiple themes like this example&lt;/li&gt;
&lt;li&gt;Supporting shared components across multiple brands&lt;/li&gt;
&lt;li&gt;Working with other types of external modifiers that work by CSS namespacing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In general, this is very easy to do, provided your comfortable with CSS in JS, and you follow the strict class naming structure.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://khrome.dev/custom-decorators-with-storybook-and-vue" rel="noopener noreferrer"&gt;Khrome.dev&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>vue</category>
      <category>storybook</category>
    </item>
    <item>
      <title>HTML Elements with Flex-box Quirks</title>
      <dc:creator>Zane Milakovic</dc:creator>
      <pubDate>Tue, 09 Jul 2019 03:37:39 +0000</pubDate>
      <link>https://forem.com/khrome83/html-elements-with-flex-box-quirks-eek</link>
      <guid>https://forem.com/khrome83/html-elements-with-flex-box-quirks-eek</guid>
      <description>&lt;p&gt;I have interviewed many frontend developers. One of the methods I have used during the candidate screening process is to give a CSS pair programming exam. The exam allowed my team to have an accurate way to rule out candidates that did not have strong HTML and CSS knowledge. Given that one of the current trends in the industry is to focus on frameworks, I find that knowledge of basic web fundamentals is lacking. Attention to detail is always an issue with young developers as well.&lt;/p&gt;

&lt;p&gt;The setup for the interview was simple - the interviewing candidate would start a phone conversation with the interviewer. We would give them a collaborative link to a CodePen. After a few minutes of explanation of what we were expecting, they would start coding.&lt;/p&gt;

&lt;p&gt;The task was simple; we asked them to create the styling for a simple login form. We provided the HTML, and they could not modify it. Using a picture of the solution as a guide, they would start. &lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;During about ~50 interviews with this method, we kept seeing the same issue. Young developers would rely heavily on flex-box and fail at aligning the content vertically within a &lt;code&gt;fieldset&lt;/code&gt;. Even talented developers that nailed all the colors and understood the details struggled.&lt;/p&gt;

&lt;p&gt;It turns out that the browsers have some nasty quirks when it comes to flex-box.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Truth
&lt;/h2&gt;

&lt;p&gt;Flex-box is buggy. Philip Walton has a great readme on his &lt;a href="https://github.com/philipwalton/flexbugs#9-some-html-elements-cant-be-flex-containers"&gt;GitHub&lt;/a&gt; that breaks out all the intricate quirks with flex-box by the browser vendor. While flex-box came out in 2009, the spec got finalized in 2011. Years later, many of the bugs still exist.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fieldset
&lt;/h3&gt;

&lt;p&gt;In the exam above, the &lt;code&gt;fieldset&lt;/code&gt; element was the biggest surprise challenge. In Chrome and Edge, the &lt;code&gt;display: flex&lt;/code&gt; property does not get applied by the browser. This issue has been frustrating developers for years. Years later, both Google and Microsoft have either punted on the bug or flagged it as "by design."&lt;/p&gt;

&lt;p&gt;So avoid flex-box on &lt;code&gt;fieldset&lt;/code&gt;. Unless you are using Firefox or Safari, turns out they fixed this bug.&lt;/p&gt;

&lt;h3&gt;
  
  
  Button
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;button&lt;/code&gt; element does not support the &lt;code&gt;align-items&lt;/code&gt; property in Chrome. Safari does not support &lt;code&gt;justify-content&lt;/code&gt;. Good news though, you can place the content within &lt;code&gt;div&lt;/code&gt; or &lt;code&gt;span&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;Even though Safari fixed &lt;code&gt;fieldset&lt;/code&gt;, the &lt;code&gt;summary&lt;/code&gt; element can't be a flex container still. &lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus - CSS Challenge
&lt;/h2&gt;

&lt;p&gt;You can see the solution to the CSS exam below. Want to test your CSS skills? Fork the CodeSandbox and remove the CSS. See if you can reproduce the original without using flexbox.&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/q61ge"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://khrome.dev/html-elements-with-flex-box-quirks"&gt;Khrome.dev&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>frontend</category>
      <category>html</category>
      <category>css</category>
      <category>flexbox</category>
    </item>
    <item>
      <title>Custom 404 Pages on Zeit Now v2 Platform</title>
      <dc:creator>Zane Milakovic</dc:creator>
      <pubDate>Fri, 05 Jul 2019 23:54:26 +0000</pubDate>
      <link>https://forem.com/khrome83/custom-404-pages-on-zeit-now-v2-platform-4h2o</link>
      <guid>https://forem.com/khrome83/custom-404-pages-on-zeit-now-v2-platform-4h2o</guid>
      <description>&lt;p&gt;A custom 404 page has become a hallmark of well-polished websites. It allows expressing the creative brand of the site. Sometimes it shows a neat trick and easter eggs.&lt;/p&gt;

&lt;h2&gt;
  
  
  404 Page Basics
&lt;/h2&gt;

&lt;p&gt;Take &lt;a href="https://dev.to/404"&gt;dev.to&lt;/a&gt;  404 page as an example. It is an incredibly simple page that shows the glitching gif logo of the dev.to brand. It does not offer anything extra, except a link back home.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dQVJHg1M--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/je4wl8m2fjnby161ql40.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dQVJHg1M--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/je4wl8m2fjnby161ql40.gif" alt="" width="800" height="560"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/404"&gt;GitHub&lt;/a&gt; takes things a step future with their 404 pages. They use Star Wars as a pop culture reference to relate to the inner geek in me. GitHub goes a step further than most sites by tracking the users' mouse movement to create a parallax effect. I remember that this brought me a moment of joy when I found this page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--J_dUqOus--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/1ezcflm2iq8vt6j9379i.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--J_dUqOus--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/1ezcflm2iq8vt6j9379i.gif" alt="" width="800" height="540"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Suggestions Useful 404 Pages
&lt;/h3&gt;

&lt;p&gt;There is much debate about what makes a useful 404 page. Dev.to does not provide many methods to recover, while GitHub by contract shows the full header and footer of the website and a second search bar.&lt;/p&gt;

&lt;p&gt;In general, it's hard to say anything negative about giving help to users landing on a 404 page so they can recover. Bad 404 pages can cause users to leave the site altogether. A useful 404 page conveys to the user you care in helping them find the relevant information. It could also mean saving a customer or creating a new conversion.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Show consistent navigation so users can self recover.&lt;/li&gt;
&lt;li&gt;Provide a secondary search to encourage users to find the content they seek.&lt;/li&gt;
&lt;li&gt;Make suggestions based on the users' search history, current trending content, or just what you want to feature.&lt;/li&gt;
&lt;li&gt;Provide links to any monitoring or status pages in case the 404 page shown was by some other error.&lt;/li&gt;
&lt;li&gt;Show your brand and messaging off some. Delight.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The State of Default 404 Pages on Now
&lt;/h2&gt;

&lt;p&gt;Zeit launched the second version of the Now platform (Now v2) without custom 404 pages. This forces customers to use the built-in default 404 pages. While the design is sharp and works great to fit the Zeit brand, it offers the user no assistance. It is also very opinioned and stands out against most brands. In short, you should replace it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8jcc7Whv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/2zhap1qbv653berropu8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8jcc7Whv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/2zhap1qbv653berropu8.png" alt="" width="800" height="521"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Replacing the Default 404 Page
&lt;/h3&gt;

&lt;p&gt;The first thing we need is a new 404 page. I used &lt;a href="https://gridsome.org/docs/pages/#add-a-404-page"&gt;Gridsome&lt;/a&gt;, which had me create a &lt;code&gt;404.vue&lt;/code&gt; in the &lt;code&gt;src/pages&lt;/code&gt; folder. Now when I build the project, a &lt;code&gt;404.html&lt;/code&gt; file is created in the &lt;code&gt;/dist&lt;/code&gt; folder.&lt;/p&gt;

&lt;p&gt;Zeit uses a &lt;code&gt;now.json&lt;/code&gt; file to customize the build process. Most users need this file so they can specify how the site is built, and customize the routing experience. &lt;/p&gt;

&lt;p&gt;The typically static site deployed on Now looks something like this -&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"builds"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"src"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"package.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"use"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@now/static-build"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now uses this JSON file to create a static build based on the output of &lt;code&gt;npm run build&lt;/code&gt; in your &lt;code&gt;package.json&lt;/code&gt; file. If you deployed with just this configuration, you would get the default Now v2 404 pages.&lt;/p&gt;

&lt;p&gt;To setup custom 404 pages, we have to add routes.&lt;/p&gt;

&lt;h4&gt;
  
  
  Adding Routes
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"builds"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"src"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"package.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"use"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@now/static-build"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"routes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"src"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/(.*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"dest"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/$1"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"handle"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"filesystem"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"src"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/.*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"dest"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/404"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first line in the routes array, says that any traffic that enters should look for content within the destination directory. Routing can be a little confusing in Now v2. By default, the &lt;code&gt;/dist&lt;/code&gt; folder that gets created after running &lt;code&gt;npm run build&lt;/code&gt; with most static site generators,  is treated as the root of your output.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;{"handle": "filesystem"}&lt;/code&gt; line tells Now to expose the filesystem as available routes. If you had three HTML files - &lt;code&gt;A.html&lt;/code&gt;,  &lt;code&gt;B.html&lt;/code&gt; and &lt;code&gt;C.html&lt;/code&gt; in your &lt;code&gt;/dist&lt;/code&gt; folder, each of these would be mapped and available automatically. &lt;/p&gt;

&lt;p&gt;At this point, going to &lt;code&gt;/404&lt;/code&gt; on your site shows you the custom 404 page, but going to &lt;code&gt;/not-a-real-page&lt;/code&gt; does not. We have mapped the file system to make it available, but we have not told our routes to catch anything that has fallen through. So users won't see the show the 404 page when it's needed.&lt;/p&gt;

&lt;p&gt;The last line captures anything that was not found by the filesystem and sends the request to the &lt;code&gt;404.html&lt;/code&gt; page correctly. It also sends the correct status of &lt;code&gt;404&lt;/code&gt; to the users' browser.&lt;/p&gt;

&lt;h3&gt;
  
  
  Final Results
&lt;/h3&gt;

&lt;p&gt;I decided to leave things clean and minimalistic for the launch of my site. I left the header and footer on the page. I then spruced it up with an excellent illustration from &lt;a href="https://undraw.co"&gt;unDraw.io&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Mu15LmR3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/s54med88bamlj2bevq4r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Mu15LmR3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/s54med88bamlj2bevq4r.png" alt="" width="800" height="641"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let me know what you think in the comments.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://khrome.dev/custom-404-pages-on-zeit-now-v2-platform"&gt;Khrome.dev&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>404</category>
      <category>zeit</category>
      <category>now</category>
      <category>serverless</category>
    </item>
  </channel>
</rss>
