<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Michael Scherr</title>
    <description>The latest articles on DEV Community by Michael Scherr (@michaeldscherr).</description>
    <link>https://dev.to/michaeldscherr</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%2F76602%2Fcc7485af-17ab-4943-9097-ceee47779ba6.jpg</url>
      <title>DEV Community: Michael Scherr</title>
      <link>https://dev.to/michaeldscherr</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/michaeldscherr"/>
    <language>en</language>
    <item>
      <title>Switching from SASS to PostCSS</title>
      <dc:creator>Michael Scherr</dc:creator>
      <pubDate>Sun, 31 Mar 2019 18:28:25 +0000</pubDate>
      <link>https://dev.to/michaeldscherr/switching-from-sass-to-postcss-4p0c</link>
      <guid>https://dev.to/michaeldscherr/switching-from-sass-to-postcss-4p0c</guid>
      <description>&lt;p&gt;Ever since &lt;a href="https://github.com/postcss/postcss"&gt;PostCSS&lt;/a&gt; came out, I've exclusively used it for &lt;a href="https://github.com/postcss/autoprefixer"&gt;Autoprefixer&lt;/a&gt;. I didn't really dig into the power of &lt;code&gt;PostCSS&lt;/code&gt; and how it could actually be a comparable solution to &lt;a href="https://sass-lang.com/"&gt;SASS&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This article will go into depth about my skepticisms, and how I was able to find solutions using &lt;code&gt;PostCSS&lt;/code&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  The Push
&lt;/h1&gt;

&lt;p&gt;What originally considered me to switch was my interest in &lt;a href="https://tailwindcss.com/docs/what-is-tailwind/"&gt;Tailwind CSS&lt;/a&gt;. I love the ideas of &lt;code&gt;utility libraries&lt;/code&gt;, but found it difficult to sell to other developers. However, we've been looking for ways to optimize development time, so &lt;code&gt;Tailwind&lt;/code&gt; felt like a good choice. It's built with &lt;code&gt;PostCSS&lt;/code&gt; in mind, so I at least wanted to give it a try.&lt;/p&gt;

&lt;h1&gt;
  
  
  Previous Approach
&lt;/h1&gt;

&lt;p&gt;My previous approach was a mix of &lt;a href="https://sass-lang.com/"&gt;SASS&lt;/a&gt;, &lt;a href="http://getbem.com/"&gt;BEM&lt;/a&gt;, &lt;a href="https://speakerdeck.com/dafed/managing-css-projects-with-itcss"&gt;ITCSS&lt;/a&gt;, &amp;amp; &lt;a href="https://smacss.com/"&gt;SMACSS&lt;/a&gt;. They solved the following issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;SASS&lt;/code&gt; - &lt;code&gt;variables, loops, imports, nested selectors, etc&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;BEM&lt;/code&gt; - reduce &lt;code&gt;specificity&lt;/code&gt; and allow for easier additions / modifications to code&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ITCSS&lt;/code&gt; - (same as &lt;code&gt;BEM&lt;/code&gt;), but also allows for an &lt;code&gt;inverted triangle&lt;/code&gt; hierarchy for &lt;code&gt;specificity&lt;/code&gt; gets more and more specific.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SMACSS&lt;/code&gt; - used mostly for &lt;code&gt;modifier classes&lt;/code&gt;, like &lt;code&gt;.js-is-active, .has-posts, etc&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This worked really well for me. I saw the power of &lt;code&gt;utility libraries&lt;/code&gt;, but figured I would let them evolve and try them out later.&lt;/p&gt;

&lt;h1&gt;
  
  
  The Switch
&lt;/h1&gt;

&lt;p&gt;I won't bore you with the details, but I went through many iterations like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;PostCSS&lt;/code&gt; only, no &lt;code&gt;plugins&lt;/code&gt; besides &lt;code&gt;Tailwind&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PostCSS + SASS&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PostCSS&lt;/code&gt; only, with &lt;code&gt;Tailwind&lt;/code&gt; and other complementary &lt;code&gt;plugins&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The core issue with the &lt;code&gt;PostCSS + SASS&lt;/code&gt; approach was the inevitable duplication. For example, these situations can't work together:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
  * cannot set $font-size-px-base in PostCSS
  * since SASS gets run first, so would have
  * to duplicate code
*/&lt;/span&gt;
&lt;span class="k"&gt;@function&lt;/span&gt; &lt;span class="nf"&gt;rem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$pixels&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$context&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$font-size-px-base&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;@if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;unitless&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$pixels&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$pixels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$pixels&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;@if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;unitless&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$context&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$context&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;@return&lt;/span&gt; &lt;span class="nv"&gt;$pixels&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nv"&gt;$context&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/**
  * can't use Tailwind variables
  * in the SASS function `scale` since
  * SASS runs before PostCSS
*/&lt;/span&gt;
&lt;span class="nc"&gt;.selector&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="nf"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;font-size&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'fontSize.lg'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'fontSize.xl'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/**
  * Interpolation was also a nightmare
  * I couldn't figure out how to make
  * something like this work
  * even with PostCSS Simple Vars
*/&lt;/span&gt;
&lt;span class="nc"&gt;.selector&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;$path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'path.theme'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nl"&gt;background-image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sx"&gt;url($(path)&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;png&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Final Solution
&lt;/h2&gt;

&lt;p&gt;The final solution involved &lt;code&gt;PostCSS&lt;/code&gt; only, with &lt;code&gt;Tailwind&lt;/code&gt; and other complementary &lt;code&gt;plugins&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When I thought about it, I was really using &lt;code&gt;SASS&lt;/code&gt; for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;imports&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mixins&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;functions&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;variables&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;nested selectors&lt;/code&gt; - &lt;small&gt;&lt;em&gt;rarely, but nice to have, especially for pseudo selectors&lt;/em&gt;&lt;/small&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;loops&lt;/code&gt; - &lt;small&gt;&lt;em&gt;very rarely, but nice to have&lt;/em&gt;&lt;/small&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;PostCSS&lt;/code&gt; has solutions for all of these once I realized the power of their plugin system. Furthermore, I found out some plugins, like for &lt;code&gt;functions&lt;/code&gt; and &lt;code&gt;mixins&lt;/code&gt;, allow you to inject &lt;code&gt;Javascript&lt;/code&gt; functions into &lt;code&gt;PostCSS&lt;/code&gt;. I was sold.&lt;/p&gt;

&lt;h3&gt;
  
  
  Imports
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/postcss/postcss-import"&gt;PostCSS Imports&lt;/a&gt; gives the exact same functionality as &lt;code&gt;SASS Imports&lt;/code&gt;, so it was an easy switch.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s2"&gt;'tailwindcss/base'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s2"&gt;'tailwindcss/components'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s2"&gt;'components/btn'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s2"&gt;'tailwindcss/utilities'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Functions
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/andyjansson/postcss-functions"&gt;PostCSS Functions&lt;/a&gt; allows you to write functions &lt;code&gt;IN JAVASCRIPT&lt;/code&gt; and inject them into &lt;code&gt;PostCSS&lt;/code&gt;. &lt;code&gt;IN JAVASCRIPT. I LOVE JAVASCRIPT&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// functions.js&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;settings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./settings&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;rem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pixels&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;defaults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fontSizePxBase&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;pixels&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;parseFloat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pixels&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pixels&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;rem`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;rem&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;
  
  
  Mixins
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/postcss/postcss-mixins"&gt;PostCSS Mixins&lt;/a&gt; is the same deal as &lt;code&gt;PostCSS Functions&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// mixins.js&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;placeholder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mixin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;immediateSelector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;vendors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;::-webkit-input-placeholder&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;:-moz-placeholder&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;::-moz-placeholder&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;:-ms-input-placeholder&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;vendors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;prev&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;vendor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;selector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;immediateSelector&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;`&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;vendor&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;vendor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nx"&gt;prev&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@mixin-content&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;prev&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{});&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;placeholder&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;
  
  
  Other Notable Plugins
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/postcss/postcss-nested"&gt;PostCSS Nested&lt;/a&gt; - offers &lt;code&gt;SASS nesting&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/postcss/postcss-simple-vars"&gt;PostCSS Simple Vars&lt;/a&gt; - offers simple &lt;code&gt;variables&lt;/code&gt; to be used in &lt;code&gt;selectors, etc&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/FullHuman/postcss-purgecss"&gt;PostCSS PurgeCSS&lt;/a&gt; - removes unnecessary &lt;code&gt;CSS&lt;/code&gt; based on file globs you pass in&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/cssnano/cssnano"&gt;CSSNano&lt;/a&gt; - minify &lt;code&gt;CSS&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/postcss/autoprefixer"&gt;Autoprefixer&lt;/a&gt; - remove unnecessary &lt;code&gt;prefixes&lt;/code&gt; based on your browser support&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Example PostCSS Config File
&lt;/h2&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// postcss.config.js&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;functions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./functions&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;mixins&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./mixins&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="cm"&gt;/**
   * context comes from what you pass
   * into `gulp-postcss`, omitting detailing here
   * since `gulp` is an implementation detail
   */&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;plugins&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;postcss-import&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)({&lt;/span&gt;
      &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;paths&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;components&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;paths&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node_modules&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;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tailwindcss&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./tailwind.config.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;postcss-functions&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)({&lt;/span&gt;
      &lt;span class="nx"&gt;functions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;postcss-mixins&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)({&lt;/span&gt;
      &lt;span class="nx"&gt;mixins&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;postcss-nested&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;postcss-simple-vars&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;autoprefixer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;I hope this at least got you interested in &lt;code&gt;PostCSS&lt;/code&gt; and what it's powerful &lt;code&gt;plugin&lt;/code&gt; system can provide for your next project.&lt;/p&gt;

</description>
      <category>sass</category>
      <category>postcss</category>
      <category>frontend</category>
      <category>css</category>
    </item>
    <item>
      <title>Building a Simple Chrome Extension</title>
      <dc:creator>Michael Scherr</dc:creator>
      <pubDate>Sat, 16 Feb 2019 19:59:00 +0000</pubDate>
      <link>https://dev.to/michaeldscherr/building-a-simple-chrome-extension-1mal</link>
      <guid>https://dev.to/michaeldscherr/building-a-simple-chrome-extension-1mal</guid>
      <description>&lt;p&gt;I decided to make my first experimental &lt;code&gt;Chrome Extension&lt;/code&gt;. My colleague came up with a really simple idea to implement, so I decided to give it a try.&lt;/p&gt;

&lt;h1&gt;
  
  
  The Functional Requirement
&lt;/h1&gt;

&lt;p&gt;Create a &lt;code&gt;Chrome Extension&lt;/code&gt; that will output a small colored square in the top left corner of a page, alerting you of which type of &lt;code&gt;domain (i.e. .dev, .stage)&lt;/code&gt; you're on. These &lt;code&gt;domains&lt;/code&gt; and &lt;code&gt;colors&lt;/code&gt; will be managed on an &lt;code&gt;Options Page&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Options Page
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;environments&lt;/code&gt; and their corresponding &lt;code&gt;color&lt;/code&gt; should be managed on a &lt;code&gt;Options Page&lt;/code&gt;, allowing you to add / remove any number of entries.&lt;/p&gt;

&lt;h2&gt;
  
  
  Active Tab
&lt;/h2&gt;

&lt;p&gt;The small square should only appear on &lt;code&gt;domains&lt;/code&gt; that match entries the user has added on the &lt;code&gt;Options Page&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The square's background color will reflect the current entry.&lt;/p&gt;

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

&lt;p&gt;&lt;em&gt;I originally followed &lt;a href="https://developer.chrome.com/extensions/getstarted" rel="noopener noreferrer"&gt;this tutorial&lt;/a&gt; to get started.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Every extension needs to have a &lt;code&gt;manifest.json&lt;/code&gt;. For a full list of options, visit their &lt;a href="https://developer.chrome.com/extensions/manifest" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Below is a bare bones example of a &lt;code&gt;manifest.json&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Environment Flag Example"&lt;/span&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="s2"&gt;"1.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Environment Flag Example Extension"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"manifest_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;"background"&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;"permissions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"options_page"&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;"content_scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Notable Settings
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Background Scripts
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://developer.chrome.com/extensions/background_pages" rel="noopener noreferrer"&gt;Documentation&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Extensions are event based programs used to modify or enhance the Chrome browsing experience. Events are browser triggers, such as navigating to a new page, removing a bookmark, or closing a tab. Extensions monitor these events in their background script, then react with specified instructions.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We'll be using &lt;code&gt;background scripts&lt;/code&gt; to add an &lt;code&gt;event listener&lt;/code&gt; to the &lt;code&gt;onInstalled&lt;/code&gt; event.&lt;/p&gt;

&lt;p&gt;This will allow us to run code when the &lt;code&gt;extension&lt;/code&gt; is installed. We'll use this &lt;code&gt;event&lt;/code&gt; to add some default entries for the &lt;code&gt;Options Page&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"background"&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;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"background.js"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"persistent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Why is &lt;code&gt;persistent&lt;/code&gt; marked as &lt;code&gt;false?&lt;/code&gt; As the documentation states:&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The only occasion to keep a background script persistently active is if the extension uses chrome.webRequest API to block or modify network requests. The webRequest API is incompatible with non-persistent background pages.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Permissions
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://developer.chrome.com/extensions/declare_permissions" rel="noopener noreferrer"&gt;Documentation&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;To use most chrome.* APIs, your extension or app must declare its intent in the "permissions" field of the manifest.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For example, if you would like to use Chrome's &lt;a href="https://developer.chrome.com/apps/storage" rel="noopener noreferrer"&gt;Storage API&lt;/a&gt;, you'll have to request permission for &lt;code&gt;storage&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"permissions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"storage"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Options Page
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://developer.chrome.com/extensions/options" rel="noopener noreferrer"&gt;Documention&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This entry will tell Chrome which &lt;code&gt;html&lt;/code&gt; file you would like to use for the &lt;code&gt;Options Page&lt;/code&gt; for your &lt;code&gt;Extension&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"options_page"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"options/options.html"&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;You would access this page by clicking on &lt;code&gt;Options&lt;/code&gt; in the menu dropdown for your &lt;code&gt;Extension&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fmichaeldscherr%2Fimage%2Fupload%2Fv1549736115%2FDev.to%2FPosts%2FSimple%2520Chrome%2520Extension%2Foptions-menu-item.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%2Fres.cloudinary.com%2Fmichaeldscherr%2Fimage%2Fupload%2Fv1549736115%2FDev.to%2FPosts%2FSimple%2520Chrome%2520Extension%2Foptions-menu-item.png" alt="Options Menu Item"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Content Scripts
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://developer.chrome.com/content_scripts.html" rel="noopener noreferrer"&gt;Documention&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Content scripts are files that run in the context of web pages. By using the standard Document Object Model (DOM), they are able to read details of the web pages the browser visits, make changes to them and pass information to their parent extension.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Essentially, any script you would like to actually run on a given page, needs to leverage this &lt;code&gt;api&lt;/code&gt;. In our example, we'll be injecting a colored square in the top left corner of the &lt;code&gt;active tab&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"content_scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"matches"&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="s2"&gt;"&amp;lt;all_urls&amp;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;"js"&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="s2"&gt;"content/content.js"&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;I also recommend watching the video on &lt;a href="https://www.youtube.com/watch?v=laLudeUmXHM" rel="noopener noreferrer"&gt;Content Scripts and Isolated Worlds&lt;/a&gt; for a better understanding of what's going on behind the scenes.&lt;/p&gt;

&lt;p&gt;We'll also need to update our permissions to use the &lt;code&gt;activeTab&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"permissions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"storage"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"activeTab"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Complete &lt;code&gt;manifest.json&lt;/code&gt;
&lt;/h2&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;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Environment Flag Example"&lt;/span&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="s2"&gt;"1.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Environment Flag Example Extension"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"manifest_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;"permissions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"storage"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"activeTab"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"background"&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;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"background.js"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"persistent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"options_page"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"options/options.html"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"content_scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"matches"&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="s2"&gt;"&amp;lt;all_urls&amp;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;"js"&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="s2"&gt;"content/content.js"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Let's Get Coding
&lt;/h1&gt;

&lt;p&gt;The entire codebase is available in my &lt;a href="https://github.com/michaeldscherr/chrome-extension-environment-flag" rel="noopener noreferrer"&gt;github repo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Bonus - for the purists out there, I made a &lt;a href="https://github.com/michaeldscherr/chrome-extension-environment-flag/tree/no-dependencies" rel="noopener noreferrer"&gt;branch with no dependencies&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;p&gt;Installing a development extension is pretty well documented already, so I won't be going over it here.&lt;/p&gt;

&lt;p&gt;Go ahead and follow &lt;a href="https://developer.chrome.com/extensions/getstarted" rel="noopener noreferrer"&gt;their official documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Background Script
&lt;/h2&gt;

&lt;p&gt;The first thing we should do is set some default data using chrome's &lt;a href="https://developer.chrome.com/apps/storage" rel="noopener noreferrer"&gt;storage api&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The two methods you need to know about for this tutorial are:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Value is set to &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;key&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Value currently is &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;second parameter&lt;/code&gt; for each method is a &lt;code&gt;callback function&lt;/code&gt; once the &lt;code&gt;storage&lt;/code&gt; operation is complete. We'll be leveraging this in &lt;code&gt;Vue&lt;/code&gt; to update internal state.&lt;/p&gt;

&lt;p&gt;Let's open up &lt;code&gt;background.js&lt;/code&gt; and add an event for when an extension is &lt;code&gt;installed&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// background.js&lt;/span&gt;

&lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onInstalled&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="cm"&gt;/**
   * lets add a default domain
   * for our options page
  */&lt;/span&gt;
  &lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;config&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;domain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;docker&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="s1"&gt;#2496ed&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="p"&gt;},&lt;/span&gt;
    &lt;span class="kc"&gt;null&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the code above, we are doing the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;add a new key to the &lt;code&gt;storage object&lt;/code&gt; called &lt;code&gt;config&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;add one &lt;code&gt;entry&lt;/code&gt; into &lt;code&gt;config&lt;/code&gt; for a domain ending with &lt;code&gt;docker&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Options Page
&lt;/h2&gt;

&lt;p&gt;For my tech stack, I decided to go with &lt;a href="https://getbootstrap.com/docs/4.0/getting-started/introduction/" rel="noopener noreferrer"&gt;Bootstrap 4&lt;/a&gt;, &lt;a href="https://vuejs.org/" rel="noopener noreferrer"&gt;Vue JS&lt;/a&gt;, &lt;a href="https://webpack.js.org/" rel="noopener noreferrer"&gt;Webpack&lt;/a&gt;, &amp;amp; native &lt;a href="https://www.javascript.com/" rel="noopener noreferrer"&gt;ES6 Javascript&lt;/a&gt;. I chose these because I'm comfortable with them, but feel free to choose your own.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;For the purposes of this tutorial, I won't be explaining much about Vue, since it's an implementation detail, and not necessary to build an extension.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;For a dependency free implementation, &lt;a href="https://github.com/michaeldscherr/chrome-extension-environment-flag/tree/no-dependencies" rel="noopener noreferrer"&gt;checkout this branch&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The &lt;code&gt;options.html&lt;/code&gt; page is very simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1.0"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"ie=edge"&lt;/span&gt; &lt;span class="na"&gt;http-equiv=&lt;/span&gt;&lt;span class="s"&gt;"X-UA-Compatible"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Environment Flag Options&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt;
      &lt;span class="na"&gt;crossorigin=&lt;/span&gt;&lt;span class="s"&gt;"anonymous"&lt;/span&gt;
      &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css"&lt;/span&gt;
      &lt;span class="na"&gt;integrity=&lt;/span&gt;&lt;span class="s"&gt;"sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO"&lt;/span&gt;
      &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt;
    &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;main&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;"container  py-5"&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;"col-sm-8  offset-sm-2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"app"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/main&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"../dist/options.bundle.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;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Go ahead and review the &lt;a href="https://github.com/michaeldscherr/chrome-extension-environment-flag/tree/master/options" rel="noopener noreferrer"&gt;options folder&lt;/a&gt; before we continue. It's a pretty standard &lt;code&gt;Vue&lt;/code&gt; application.&lt;/p&gt;

&lt;p&gt;Let's review some of the notable &lt;code&gt;Vue&lt;/code&gt; code. &lt;code&gt;Options.vue&lt;/code&gt; is where most of the magic happens with leveraging the &lt;code&gt;chrome api&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// options/Options.vue&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;data&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="cm"&gt;/**
             * empty array to be used to store
             * the chrome storage result
             */&lt;/span&gt;
            &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nf"&gt;mounted&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="cm"&gt;/**
         * once the component mounts
         * lets call the storage api
         * and request our `config` key
         * 
         * on our callback, lets call a method
         * to set our internal state
         */&lt;/span&gt;
        &lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;config&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setConfig&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nx"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;setConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="cm"&gt;/**
             * set our internal state
             * with the result from the
             * chrome api call
             */&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the code above, we are doing the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;setting internal state for a key called &lt;code&gt;config&lt;/code&gt;, and assigning it to an empty &lt;code&gt;array&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;on the &lt;code&gt;mounted()&lt;/code&gt; method, we are requesting the key &lt;code&gt;config&lt;/code&gt; from the &lt;code&gt;storage api&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;on the &lt;code&gt;callback function&lt;/code&gt;, we call a method called &lt;code&gt;this.setConfig&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;setConfig()&lt;/code&gt; assigns our internal state to what is returned from the &lt;code&gt;chrome api&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We then have two methods for altering the &lt;code&gt;chrome storage state&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;deleteEntry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="cm"&gt;/**
         * remove the entry at a specific index
         * from our internal state
         */&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;splice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="cm"&gt;/**
         * update the chrome storage api
         * with the new state
         */&lt;/span&gt;
        &lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="kc"&gt;null&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nf"&gt;addEntry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="cm"&gt;/**
         * add an entry to our internal state
         */&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="cm"&gt;/**
         * update the chrome storage api
         * with the new state
         */&lt;/span&gt;
        &lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="kc"&gt;null&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="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;After implementing these methods, the final &lt;code&gt;Options Page&lt;/code&gt; looks like this:&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%2Fres.cloudinary.com%2Fmichaeldscherr%2Fimage%2Fupload%2Fv1550340324%2FDev.to%2FPosts%2FSimple%2520Chrome%2520Extension%2Foptions-page-final.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%2Fres.cloudinary.com%2Fmichaeldscherr%2Fimage%2Fupload%2Fv1550340324%2FDev.to%2FPosts%2FSimple%2520Chrome%2520Extension%2Foptions-page-final.png" alt="Options Page Complete"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I know, it's nothing fancy… but that's not the point. Get out there and have some fun! You'll notice I added a &lt;code&gt;edu&lt;/code&gt; domain, go ahead and add that now if you would like.&lt;/p&gt;

&lt;h2&gt;
  
  
  Content Script
&lt;/h2&gt;

&lt;p&gt;Now that we have an &lt;code&gt;Options Page&lt;/code&gt; with a way to &lt;code&gt;add / delete entries&lt;/code&gt;, let's now implement the small square that will appear in the top left corner of valid domains.&lt;/p&gt;

&lt;p&gt;To do this, we need to use the &lt;code&gt;content script&lt;/code&gt; we discussed before. Let's go ahead and open up the &lt;code&gt;content/content.js&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// content/content.js&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * lets first request the `config` key from
 * the chrome api storage
 */&lt;/span&gt;
&lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;config&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;config&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="cm"&gt;/**
   * lets see if the `window.location.origin`
   * matches any entry from our
   * options page
   */&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;match&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;regex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;RegExp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;domain&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;regex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="cm"&gt;/**
   * if no match, don't do anything
   */&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="cm"&gt;/**
   * lets create the style attribute
   * by building up an object
   * then using join to combine it
   */&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;div&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;nodeStyleProperties&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;background-color&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;25px&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;5px&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pointer-events&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;none&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fixed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;5px&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;25px&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;z-index&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;999999&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;nodeStyle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nodeStyleProperties&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(([&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="cm"&gt;/**
   * apply the style to the node
   * and a class flag (doesn't do anything)
   */&lt;/span&gt;
  &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;style&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;nodeStyle&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;class&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;chrome-extension-environment-flag&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="cm"&gt;/**
   * append the node to the document
   */&lt;/span&gt;
  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Now, when I go to an &lt;code&gt;edu&lt;/code&gt; domain, I see the following in the top left corner:&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%2Fres.cloudinary.com%2Fmichaeldscherr%2Fimage%2Fupload%2Fv1550340821%2FDev.to%2FPosts%2FSimple%2520Chrome%2520Extension%2Fumbc-homepage.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%2Fres.cloudinary.com%2Fmichaeldscherr%2Fimage%2Fupload%2Fv1550340821%2FDev.to%2FPosts%2FSimple%2520Chrome%2520Extension%2Fumbc-homepage.png" alt="UMBC Homepage"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I hope this tutorial at least got you interested in Chrome Extensions. We only scratched the surface. Feel free to use any of the code in my repo for any purpose.&lt;/p&gt;

</description>
      <category>chrome</category>
      <category>extensions</category>
      <category>javascript</category>
      <category>vue</category>
    </item>
    <item>
      <title>Let's Encrypt Server Certificate via DNS Challenge</title>
      <dc:creator>Michael Scherr</dc:creator>
      <pubDate>Thu, 31 Jan 2019 02:29:00 +0000</pubDate>
      <link>https://dev.to/michaeldscherr/lets-encrypt-ssl-certificate-via-dns-challenge-8dd</link>
      <guid>https://dev.to/michaeldscherr/lets-encrypt-ssl-certificate-via-dns-challenge-8dd</guid>
      <description>&lt;p&gt;Have you ever wondered if it was possible to obtain an &lt;code&gt;Server Certificate&lt;/code&gt; before you migrate a website? I always assumed the &lt;code&gt;DNS&lt;/code&gt; had to be switched &lt;em&gt;before&lt;/em&gt; the &lt;code&gt;certificate validation&lt;/code&gt; could take place. I've come to find there are more options available.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Scenario
&lt;/h2&gt;

&lt;p&gt;My company is currently in the process of moving all of our client production sites to a new server. Part of that effort is ensuring all sites be placed on &lt;code&gt;https&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Server Certificate Challenges
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Let's Encrypt offers domain-validated certificates, meaning they have to check that the certificate request comes from a person who actually controls the domain. They do this by sending the client a unique token, and then making a web or DNS request to retrieve a key derived from that token.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There are two main options to obtain a &lt;code&gt;server certificate&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;HTTP Challenge&lt;/code&gt; - Posting a specified &lt;code&gt;file&lt;/code&gt; in a specified location on a web site&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DNS Challenge&lt;/code&gt; - Posting a specified &lt;code&gt;DNS record&lt;/code&gt; in the domain name system&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  HTTP Challenge
&lt;/h3&gt;

&lt;p&gt;This is usually handled by adding a &lt;code&gt;token&lt;/code&gt; inside a &lt;code&gt;.well-known&lt;/code&gt; directory in your &lt;code&gt;web root&lt;/code&gt;. &lt;code&gt;Certbot&lt;/code&gt; can then confirm you actually control resources on the specified domain, and will sign a &lt;code&gt;certificate&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  DNS Challenge
&lt;/h3&gt;

&lt;p&gt;This approach requires you to add specific &lt;code&gt;DNS TXT&lt;/code&gt; entry for each domain requested. This is useful when you haven't switched &lt;code&gt;DNS&lt;/code&gt; yet, but want to issue a &lt;code&gt;certificate&lt;/code&gt; in anticipation (for testing).&lt;/p&gt;

&lt;p&gt;For more information on challenges, visit &lt;a href="https://certbot.eff.org/docs/challenges.html" rel="noopener noreferrer"&gt;certbot's documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;We'll be discussing the &lt;code&gt;DNS Challenge&lt;/code&gt; approach for the rest of the article.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In the examples below, I'll be using &lt;code&gt;Apache&lt;/code&gt; &amp;amp; &lt;code&gt;Ubuntu 16.04&lt;/code&gt; following &lt;a href="https://certbot.eff.org/lets-encrypt/ubuntuxenial-apache" rel="noopener noreferrer"&gt;this guide&lt;/a&gt;. To find documentation for your specific web server / operating system, go to &lt;a href="https://certbot.eff.org/" rel="noopener noreferrer"&gt;certbot's homepage&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;First we need to install &lt;code&gt;certbot&lt;/code&gt; along with all necessary dependencies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# run as root&lt;/span&gt;

apt-get update &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install &lt;/span&gt;software-properties-common &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; add-apt-repository &lt;span class="nt"&gt;-y&lt;/span&gt; universe &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; add-apt-repository &lt;span class="nt"&gt;-y&lt;/span&gt; ppa:certbot/certbot &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get update &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; python-certbot-apache
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Configuration
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;I won't be going over wildcard domains here, but they are an option. Refer to &lt;a href="https://certbot.eff.org/" rel="noopener noreferrer"&gt;their documentation&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We now need to tell &lt;code&gt;certbot&lt;/code&gt; which domains we would like to issue a &lt;code&gt;certificate&lt;/code&gt; for. Remember to add each subdomain individually.&lt;/p&gt;

&lt;p&gt;Since we may have multiple &lt;code&gt;vhosts&lt;/code&gt; per server, we decided to use the &lt;code&gt;--manual&lt;/code&gt; &amp;amp; &lt;code&gt;certonly&lt;/code&gt; flags.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# run as root&lt;/span&gt;

&lt;span class="c"&gt;# replace with your domain&lt;/span&gt;
&lt;span class="c"&gt;# add all relevant subdomains&lt;/span&gt;
certbot &lt;span class="nt"&gt;--manual&lt;/span&gt; &lt;span class="nt"&gt;--preferred-challenges&lt;/span&gt; dns certonly &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-d&lt;/span&gt; yourwebsite.com &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-d&lt;/span&gt; www.yourwebsite.com &lt;span class="se"&gt;\ &lt;/span&gt;         &lt;span class="c"&gt;# don't forget www binding&lt;/span&gt;
    &lt;span class="nt"&gt;-d&lt;/span&gt; staging.yourwebsite.com &lt;span class="se"&gt;\ &lt;/span&gt;     &lt;span class="c"&gt;# example subdomain&lt;/span&gt;
    &lt;span class="nt"&gt;-d&lt;/span&gt; staging.stage1.yourwebsite.com &lt;span class="c"&gt;# example long subdomain&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Challenge Prompts
&lt;/h3&gt;

&lt;p&gt;Once you run the command above, it will prompt you to add a &lt;code&gt;DNS TXT&lt;/code&gt; record for each specified domain. It will look like this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Please deploy a DNS TXT record under the name
_acme-challenge.yourwebsite.com with the following value:

[random-string-of-characters]

Before continuing, verify the record is deployed.
(This must be set up in addition to the previous challenges; do not remove,
replace, or undo the previous challenge tasks yet. Note that you might be
asked to create multiple distinct TXT records with the same name. This is
permitted by DNS standards.)

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Press Enter to Continue

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

&lt;/div&gt;


&lt;p&gt;Now here's the important part. You need to remove the &lt;code&gt;base url&lt;/code&gt; from each &lt;code&gt;record name&lt;/code&gt;, like so:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;requested name from certbot&lt;/th&gt;
&lt;th&gt;actual name to add in &lt;code&gt;DNS&lt;/code&gt;
&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;_acme-challenge.yourwebsite.com&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;_acme-challenge&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;_acme-challenge.www.yourwebsite.com&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;_acme-challenge.www&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;_acme-challenge.staging.yourwebsite.com&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;_acme-challenge.staging&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;_acme-challenge.staging.stage1.yourwebsite.com&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;_acme-challenge.staging.stage1&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Once you add the &lt;code&gt;DNS TXT&lt;/code&gt; records, and click &lt;code&gt;Continue&lt;/code&gt; through each challenge prompt respectively, the validation should pass.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Press Enter to Continue
Waiting &lt;span class="k"&gt;for &lt;/span&gt;verification...
Cleaning up challenges

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/yourwebsite.com/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/yourwebsite.com/privkey.pem
   Your cert will expire on 2019-04-24. To obtain a new or tweaked
   version of this certificate &lt;span class="k"&gt;in &lt;/span&gt;the future, simply run certbot
   again. To non-interactively renew &lt;span class="k"&gt;*&lt;/span&gt;all&lt;span class="k"&gt;*&lt;/span&gt; of your certificates, run
   &lt;span class="s2"&gt;"certbot renew"&lt;/span&gt;
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let&lt;span class="s1"&gt;'s Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Updating Your VHost
&lt;/h2&gt;

&lt;p&gt;Now that we have a valid certificate, we can update our &lt;code&gt;vhost&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# inside the 443 binding&lt;/span&gt;

SSLEngine On
SSLCertificateFile /etc/letsencrypt/live/yourwebsite.com/cert.pem
SSLCertificateKeyFile /etc/letsencrypt/live/yourwebsite.com/privkey.pem
SSLCertificateChainFile /etc/letsencrypt/live/yourwebsite.com/chain.pem
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Then restart &lt;code&gt;Apache&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apachectl configtest

&lt;span class="c"&gt;# if syntax ok&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apachectl restart
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Bonus: Setup Auto Renewal
&lt;/h2&gt;

&lt;p&gt;We'll leverage the &lt;code&gt;crontab&lt;/code&gt; of the &lt;code&gt;root&lt;/code&gt; user to automatically renew certificates that will expire soon.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# run as root&lt;/span&gt;

&lt;span class="c"&gt;# edit the crontab&lt;/span&gt;
crontab &lt;span class="nt"&gt;-e&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Then add the following two lines at the bottom:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# every Monday at 2:30am&lt;/span&gt;
30 2 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; 1 /usr/bin/certbot renew &lt;span class="nt"&gt;--deploy-hook&lt;/span&gt; &lt;span class="s2"&gt;"service apache2 reload"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /var/log/letsencrypt/le-renew.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The &lt;code&gt;certbot&lt;/code&gt; will try and renew any certificates marked for renewal once a week. We then use the &lt;code&gt;--deploy-hook&lt;/code&gt; to only reload &lt;code&gt;apache&lt;/code&gt; if necessary.&lt;/p&gt;

&lt;p&gt;Special thanks to &lt;a href="https://dev.to/cpu"&gt;Daniel McCarney&lt;/a&gt; for the updated &lt;code&gt;crontab&lt;/code&gt; code.&lt;/p&gt;


&lt;div class="ltag__user ltag__user__id__127903"&gt;
    &lt;a href="/cpu" class="ltag__user__link profile-image-link"&gt;
      &lt;div class="ltag__user__pic"&gt;
        &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F127903%2F572c8b29-f6ae-4040-86f6-1aad943b7d61.jpeg" alt="cpu image"&gt;
      &lt;/div&gt;
    &lt;/a&gt;
  &lt;div class="ltag__user__content"&gt;
    &lt;h2&gt;
&lt;a class="ltag__user__link" href="/cpu"&gt;Daniel McCarney&lt;/a&gt;Follow
&lt;/h2&gt;
    &lt;div class="ltag__user__summary"&gt;
      &lt;a class="ltag__user__link" href="/cpu"&gt;&lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;



</description>
      <category>certificate</category>
      <category>bash</category>
      <category>dns</category>
    </item>
    <item>
      <title>My Terminal Setup</title>
      <dc:creator>Michael Scherr</dc:creator>
      <pubDate>Sun, 20 Jan 2019 16:30:23 +0000</pubDate>
      <link>https://dev.to/michaeldscherr/my-terminal-setup-l2k</link>
      <guid>https://dev.to/michaeldscherr/my-terminal-setup-l2k</guid>
      <description>&lt;p&gt;I thought I'd share my current &lt;code&gt;terminal&lt;/code&gt; setup. Over the years, I've been able to optimize my workflow. This allows me to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;find information quicker&lt;/li&gt;
&lt;li&gt;reduce the time I spend searching for commands&lt;/li&gt;
&lt;li&gt;become more comfortable with &lt;code&gt;cli's&lt;/code&gt; in general&lt;/li&gt;
&lt;li&gt;look like &lt;a href="https://www.youtube.com/watch?v=_czwLSCEKEM"&gt;a badass&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;My evolution has been interesting over the years. In the beginning, I was that developer who used every plugin imaginable (for the &lt;code&gt;['terminal', 'IDE', 'etc']&lt;/code&gt;). Then I watched &lt;a href="https://youtu.be/dIjKJjzRX_E?t=498"&gt;this video from MPJ&lt;/a&gt; about productivity in the workplace. I then started scaling back to where I am now.&lt;/p&gt;

&lt;h2&gt;
  
  
  App
&lt;/h2&gt;

&lt;p&gt;I use &lt;code&gt;macOS&lt;/code&gt; 99% of the time, so my go to app is &lt;a href="https://www.iterm2.com/"&gt;iTerm2&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;

&lt;p&gt;You can download it from &lt;a href="https://www.iterm2.com/"&gt;iTerm2's website&lt;/a&gt;, or install through &lt;a href="http://caskroom.io/"&gt;Brew Cask&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;pro tip: &lt;a href="http://caskroom.io/"&gt;Brew Cask&lt;/a&gt; is amazing, it can install and manage all of your Mac applications through a simple &lt;code&gt;cli&lt;/code&gt;.&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;brew cask &lt;span class="nb"&gt;install &lt;/span&gt;iterm2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;em&gt;Oh My&lt;/em&gt; ZSH
&lt;/h2&gt;

&lt;p&gt;I've experimented with other &lt;code&gt;shell's&lt;/code&gt; like &lt;a href="https://github.com/fish-shell/fish-shell"&gt;fish&lt;/a&gt;, but they were always too abstracted, or were too complicated.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# if you don't have zsh installed&lt;/span&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;zsh

&lt;span class="c"&gt;# install oh my zsh&lt;/span&gt;
sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Theme
&lt;/h3&gt;

&lt;p&gt;I use the &lt;a href="https://github.com/robbyrussell/oh-my-zsh/wiki/Themes#robbyrussell"&gt;robbyrussel&lt;/a&gt; theme. It gives me &lt;code&gt;git&lt;/code&gt; information, which is perfect.&lt;/p&gt;

&lt;p&gt;It also trims the &lt;code&gt;directory path&lt;/code&gt;, so you only see the &lt;code&gt;current directory&lt;/code&gt;. This avoids obnoxiously long folder paths taking up valuable space.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# ~/.zshrc&lt;/span&gt;
&lt;span class="nv"&gt;ZSH_THEME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"robbyrussell"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Plugins
&lt;/h3&gt;

&lt;p&gt;I only use a few plugins:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/robbyrussell/oh-my-zsh/wiki/Plugins#z"&gt;z&lt;/a&gt; - &lt;em&gt;native plugin, don't need to install&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/zsh-users/zsh-completions"&gt;zsh completions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/zsh-users/zsh-autosuggestions"&gt;zsh autosuggestions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Installation
&lt;/h4&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# zsh completions&lt;/span&gt;
git clone https://github.com/zsh-users/zsh-completions &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ZSH_CUSTOM&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="p"&gt;~/.oh-my-zsh/custom&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/plugins/zsh-completions

&lt;span class="c"&gt;# zsh autosuggestions&lt;/span&gt;
git clone https://github.com/zsh-users/zsh-autosuggestions &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ZSH_CUSTOM&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="p"&gt;~/.oh-my-zsh/custom&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/plugins/zsh-autosuggestions
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then update your &lt;code&gt;~/.zshrc&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# ~/.zshrc&lt;/span&gt;

&lt;span class="nv"&gt;plugins&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;
  z
  zsh-completions
  zsh-autosuggestions
&lt;span class="o"&gt;)&lt;/span&gt;

autoload &lt;span class="nt"&gt;-U&lt;/span&gt; compinit &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; compinit
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then &lt;code&gt;source&lt;/code&gt; your configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;source&lt;/span&gt; ~/.zshrc
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Bonus: Keyboard Bindings
&lt;/h2&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# ~/.zshrc&lt;/span&gt;

bindkey &lt;span class="nt"&gt;-e&lt;/span&gt;

&lt;span class="c"&gt;# allows you to use the `option + [left, right]` key&lt;/span&gt;
&lt;span class="c"&gt;# to skip words in a command you're writing.&lt;/span&gt;
bindkey &lt;span class="s1"&gt;'\e\e[C'&lt;/span&gt; forward-word
bindkey &lt;span class="s1"&gt;'\e\e[D'&lt;/span&gt; backward-word


&lt;span class="c"&gt;# allows you to use the `[up, down]` key on a partial command&lt;/span&gt;
&lt;span class="c"&gt;# to search your bash history for similar commands&lt;/span&gt;
bindkey &lt;span class="s1"&gt;'\e[A'&lt;/span&gt; history-search-backward
bindkey &lt;span class="s1"&gt;'\e[B'&lt;/span&gt; history-search-forward
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



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

&lt;p&gt;I do believe there is a happy medium between no tooling, and too much tooling. You can have enough to be dangerous, but not enough to overwhelm you.&lt;/p&gt;

&lt;p&gt;For a more in depth overview of what I use on a daily basis, checkout my &lt;a href="https://www.michaeldscherr.com/uses/"&gt;Uses page on my website&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>bash</category>
      <category>zsh</category>
      <category>mac</category>
      <category>terminal</category>
    </item>
    <item>
      <title>Drupal 8.x - What I've Learned</title>
      <dc:creator>Michael Scherr</dc:creator>
      <pubDate>Sun, 13 Jan 2019 19:39:07 +0000</pubDate>
      <link>https://dev.to/michaeldscherr/drupal-8x---what-ive-learned-3ldi</link>
      <guid>https://dev.to/michaeldscherr/drupal-8x---what-ive-learned-3ldi</guid>
      <description>&lt;p&gt;If anyone has solutions to any of the issues I describe below, please feel free to leave a comment.&lt;/p&gt;

&lt;p&gt;I've now developed two production sites with &lt;code&gt;Drupal 8.6&lt;/code&gt;. This post will cover what went wrong, how my team fixed them, and then some best practices from what we've learned through trial and error.&lt;/p&gt;

&lt;p&gt;The first site, in hindsight, was a complete disaster. There's nothing worse than learning Drupal (or anything for that matter) through trial and error while developing a production website. I knew nothing about &lt;code&gt;rendered entities&lt;/code&gt;, &lt;code&gt;view modes&lt;/code&gt;, etc. I constantly battled with the templating. We abused the &lt;code&gt;[theme].module&lt;/code&gt; hooks, instead of using &lt;code&gt;custom modules&lt;/code&gt; to handle functionality.&lt;/p&gt;

&lt;p&gt;The second site was much better, although I imagine there is so much more to learn. Our biggest issue was how to effectively use &lt;code&gt;views&lt;/code&gt; when using the &lt;code&gt;ajax&lt;/code&gt; functionality. Our decision to not make certain &lt;code&gt;taxonomies&lt;/code&gt; have actual pages (aka have an accessible &lt;code&gt;alias&lt;/code&gt;), led to many problems when trying to filter them in certain &lt;code&gt;views&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Localization continues to be a huge issue. If you've ever developed with Drupal, you know it's insanely frustrating configuring and outputting localized content correctly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Best Practices (from my experience)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Language Configuration
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Trust me when I say this:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Figure out your &lt;code&gt;localizations&lt;/code&gt; ahead of time.&lt;/p&gt;

&lt;p&gt;You should know before development:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;all the &lt;code&gt;languages&lt;/code&gt; + &lt;code&gt;language codes&lt;/code&gt; you'll be using&lt;/li&gt;
&lt;li&gt;what the &lt;code&gt;original language&lt;/code&gt; will be for all content&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Scenario&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;Say you are told the site will be in Canadian English (&lt;code&gt;en-CA&lt;/code&gt;) and Canadian French (&lt;code&gt;fr-CA&lt;/code&gt;). Then a couple weeks before deployment, once all content pop is mostly complete, they want to change the &lt;code&gt;language code&lt;/code&gt; from &lt;code&gt;en-CA =&amp;gt; en&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Here is the problem&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;There doesn't seem to be a way to change the &lt;code&gt;language code&lt;/code&gt; once it has been added. Additionally, all content that has the previous &lt;code&gt;language code&lt;/code&gt; as the &lt;code&gt;original language&lt;/code&gt; can't be changed either. Even if you delete the &lt;code&gt;orignal language&lt;/code&gt; node &lt;code&gt;translation&lt;/code&gt;, you still cannot assign another &lt;code&gt;translation&lt;/code&gt; as the &lt;code&gt;original language&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Ok fine, so you add the new &lt;code&gt;language code&lt;/code&gt;, content pop all of the nodes again with the new &lt;code&gt;language&lt;/code&gt;… and run into another problem. All content in a &lt;code&gt;translation&lt;/code&gt; that is not the &lt;code&gt;original language&lt;/code&gt; that has a repeatable number of values, cannot exceed the &lt;code&gt;original language's&lt;/code&gt; number of entries.&lt;/p&gt;

&lt;p&gt;How did we fix this? We haven't. We've thought about updating the database through &lt;code&gt;SQL&lt;/code&gt; statements, but please share your thoughts if you have a better way.&lt;/p&gt;

&lt;h3&gt;
  
  
  Install Drupal Core w/ Composer
&lt;/h3&gt;

&lt;p&gt;Do yourself a favor and use &lt;a href="https://www.drupal.org/docs/develop/using-composer/using-composer-to-manage-drupal-site-dependencies"&gt;Composer to install Drupal Core&lt;/a&gt;. Upgrading &lt;code&gt;core&lt;/code&gt; is now as simple as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;composer outdated drupal/&lt;span class="k"&gt;*&lt;/span&gt;

composer update drupal/core &lt;span class="nt"&gt;--with-dependencies&lt;/span&gt;
drush updatedb
drush cr
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;More information is available &lt;a href="https://www.drupal.org/docs/8/update/update-core-via-composer"&gt;on their website&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Install Modules w/ Composer
&lt;/h3&gt;

&lt;p&gt;Same deal as with &lt;code&gt;core&lt;/code&gt;, instead run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# example&lt;/span&gt;
&lt;span class="c"&gt;# composer require drupal/[module_name]&lt;/span&gt;

composer require drupal/better_exposed_filters
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  Bonus: Awesome Modules
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.drupal.org/project/paragraphs"&gt;Paragraphs&lt;/a&gt; - just like &lt;a href="https://www.advancedcustomfields.com/"&gt;ACF for Wordpress&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.drupal.org/project/better_exposed_filters"&gt;Better Exposed Filters&lt;/a&gt; - adds more options to the advanced section of &lt;code&gt;views&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.drupal.org/project/twig_xdebug"&gt;Twig XDebug&lt;/a&gt; - add debug points in your twig files (requires &lt;a href="https://xdebug.org/"&gt;XDEBUG&lt;/a&gt; to be installed and configured on the server; might do another blog post about this)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.drupal.org/project/pathauto"&gt;Path Auto&lt;/a&gt; - auto generate / update &lt;code&gt;node aliases&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.drupal.org/project/viewsreference"&gt;Views Reference Field&lt;/a&gt; - use with the &lt;code&gt;paragraphs&lt;/code&gt; module; allows a &lt;code&gt;view&lt;/code&gt; to be injected anywhere on the page&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.drupal.org/project/devel"&gt;Devel&lt;/a&gt; - for debugging&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.drupal.org/project/yoast_seo"&gt;Yoast SEO&lt;/a&gt; - for SEO; content pop for meta tags&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.drupal.org/project/media"&gt;Drupal Media&lt;/a&gt; - in core as of &lt;code&gt;8.4&lt;/code&gt;, just install it; adds a much better UX experience for handling media&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Use the &lt;code&gt;Drupal Console&lt;/code&gt; &amp;amp; &lt;code&gt;Drush&lt;/code&gt; Executables
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Installation Instructions (Use Composer)
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.drupalconsole.com/en/getting/composer.html"&gt;Drupal Console Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://docs.drush.org/en/master/install/"&gt;Drush Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To install via &lt;code&gt;composer&lt;/code&gt;, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# drupal console&lt;/span&gt;
composer require drupal/console:~1.0 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--prefer-dist&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--optimize-autoloader&lt;/span&gt;

&lt;span class="c"&gt;# drush&lt;/span&gt;
composer require drush/drush
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Common usage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# usage&lt;/span&gt;
./vendor/bin/drupal &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;command&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
./vendor/bin/drush &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;command&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;

&lt;span class="c"&gt;# cache rebuild&lt;/span&gt;
./vendor/bin/drush cr

&lt;span class="c"&gt;# show watchdog log&lt;/span&gt;
./vendor/bin/drupal watchdog:show

&lt;span class="c"&gt;# install module&lt;/span&gt;
./vendor/bin/drupal module:install &lt;span class="o"&gt;[&lt;/span&gt;module_name]

&lt;span class="c"&gt;# generate module&lt;/span&gt;
./vendor/bin/drupal generate:module
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Setup Drupal for Local Development
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.drupal.org/node/2598914"&gt;Follow this tutorial&lt;/a&gt; to make your local development so much easier.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use View Modes
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;View Modes&lt;/code&gt; allow you to define multiple ways (aka &lt;code&gt;templates&lt;/code&gt;) to show the same type of content. The two native view modes are &lt;code&gt;full&lt;/code&gt; and &lt;code&gt;teaser&lt;/code&gt;. You can accomplish &lt;code&gt;95%&lt;/code&gt; of what you need with those two &lt;code&gt;view modes&lt;/code&gt; alone.&lt;/p&gt;

&lt;p&gt;Imagine this scenario: You have a content type of &lt;code&gt;Case Study&lt;/code&gt;. A case study can appear as a single page (&lt;code&gt;full&lt;/code&gt;), inside a view grid (&lt;code&gt;teaser&lt;/code&gt;), and also as a featured case study on another page (&lt;code&gt;featured&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;You can customize the &lt;code&gt;display&lt;/code&gt; of each &lt;code&gt;view mode&lt;/code&gt; individually. For example, let's say the &lt;code&gt;teaser&lt;/code&gt; outputs the &lt;code&gt;title&lt;/code&gt; and &lt;code&gt;description&lt;/code&gt;, but hides the &lt;code&gt;image&lt;/code&gt; you've set up. However, the &lt;code&gt;image&lt;/code&gt; &lt;em&gt;should&lt;/em&gt; be shown on the &lt;code&gt;featured&lt;/code&gt; and &lt;code&gt;full&lt;/code&gt; &lt;code&gt;view modes&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This allows us to leverage 3 different templates and customize the output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node--case-study.html.twig
node--case-study--teaser.html.twig
node--case-study--featured.html.twig
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Use Rendered Entities
&lt;/h3&gt;

&lt;p&gt;Let me save you hours of frustration. Use &lt;code&gt;rendered entities&lt;/code&gt; whenever possible. As I understand it, a &lt;code&gt;rendered entity&lt;/code&gt; is the output of an entity through its selected &lt;code&gt;view mode&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Imagine the case study &lt;code&gt;featured&lt;/code&gt; example from above. You add a new field to the &lt;code&gt;Basic Page&lt;/code&gt; content type for a reference to a case study. In the &lt;code&gt;manage display&lt;/code&gt; of the &lt;code&gt;Basic Page&lt;/code&gt;, you can change the &lt;code&gt;format&lt;/code&gt; of the case study reference to &lt;code&gt;Rendered Entity&lt;/code&gt;, and select the &lt;code&gt;featured&lt;/code&gt; view mode.&lt;/p&gt;

&lt;p&gt;This allows for far less code duplication. You are keeping the rendering of entities separate from the nodes that reference them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pro tip&lt;/strong&gt;: you can also use the &lt;code&gt;rendered entity&lt;/code&gt; and a specific &lt;code&gt;view mode&lt;/code&gt; as the format for a &lt;code&gt;view&lt;/code&gt;. Additionally, in scenarios when you have cannot use the &lt;code&gt;content&lt;/code&gt; format, you can add a field called &lt;code&gt;Content: Rendered Entity&lt;/code&gt; that you can reference this way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rendered_entity&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Use the Media Module
&lt;/h3&gt;

&lt;p&gt;Yes, it's still experimental, but we haven't had any issues with it. It makes uploading, selecting, and deleting media much easier.&lt;/p&gt;

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

&lt;p&gt;Drupal is very powerful. It allows you to add complex relationships and fields to structure any kind of content. However, there is some higher level functionality that is paramount to understand and implement. Leveraging &lt;code&gt;view modes&lt;/code&gt;, &lt;code&gt;rendered entities&lt;/code&gt;, and other best practices will help you become a better Drupal developer.&lt;/p&gt;

&lt;p&gt;This post ending up being way longer than I intended. There are more best practices and tips that didn't make it into this post. Any comments with new insight or solutions are always welcome.&lt;/p&gt;

</description>
      <category>drupal</category>
    </item>
  </channel>
</rss>
