<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[39digits.com RSS Feed]]></title><description><![CDATA[A blog about web development and videogames]]></description><link>https://www.39digits.com</link><generator>GatsbyJS</generator><lastBuildDate>Wed, 24 May 2023 23:34:50 GMT</lastBuildDate><item><title><![CDATA[How to fix Bluetooth mouse stuttering on Surface Laptop 4]]></title><description><![CDATA[I recently switched to a Surface Laptop 4 as my primary web development laptop and it has been nearly flawless. Windows combined with WSL…]]></description><link>https://www.39digits.com/fix-bluetooth-mouse-stuttering-on-surface-laptop-4</link><guid isPermaLink="false">https://www.39digits.com/fix-bluetooth-mouse-stuttering-on-surface-laptop-4</guid><pubDate>Wed, 08 Sep 2021 18:11:26 GMT</pubDate><content:encoded>&lt;p&gt;I recently switched to a Surface Laptop 4 as my primary web development laptop and it has been nearly flawless. Windows combined with WSL2, winget, and the new Windows Terminal makes for a truly enjoyable workflow.&lt;/p&gt;&lt;p&gt;Unfortunately, any Bluetooth mouse connected to the Surface Laptop 4 randomly stuttered - thankfully there is a fix.&lt;/p&gt;&lt;div class=&quot;rounded-md bg-yellow-50 p-4 mt-3&quot;&gt;&lt;div class=&quot;flex&quot;&gt;&lt;div class=&quot;flex-shrink-0&quot;&gt;&lt;svg class=&quot;h-5 w-5 text-yellow-400&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 20 20&quot; fill=&quot;currentColor&quot; aria-hidden=&quot;true&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z&quot; clip-rule=&quot;evenodd&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/div&gt;&lt;div class=&quot;ml-3&quot;&gt;&lt;div class=&quot;text-sm text-yellow-700&quot;&gt;&lt;p class=&quot;mt-0&quot;&gt;The steps in this article fixed the mouse stuttering issue I was experiencing under Windows 10 but in turn created a new bug.&lt;/p&gt;&lt;p&gt;Bluetooth devices would not work when resuming Windows after being put into Sleep Mode. I had to turn Bluetooth off and back on every time I woke the laptop otherwise all Bluetooth devices (MX Master 2S, Bose QC-35s, etc) would not connect. This was a small price to pay to get the mouse input to work fluidly.&lt;/p&gt;&lt;p&gt;Thankfully the stuttering has been fixed under Windows 11 using the latest drivers from Intel. So these registry entries are not required under Windows 11 and devices will work when resuming from Sleep.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;h2 id=&quot;background-information&quot;&gt;Background information&lt;/h2&gt;&lt;p&gt;There is a noticeable stutter or one second lag to the movement of the mouse cursor when using a Bluetooth mouse on the Surface Laptop 4 with Intel i7-1185G. The same does not happen on an AMD Ryzen or older Intel chip and seems to be restricted to the 11th-generation Intel chips.&lt;/p&gt;&lt;p&gt;Frustratingly, it isn&amp;#x27;t easy to replicate due to the seemingly random nature of when the stuttering occurs.&lt;/p&gt;&lt;p&gt;It is worth noting that this only happens when using Bluetooth input devices and can be seen with both a mouse and keyboard. Other Bluetooth devices, such as audio, remain unaffected (my Bose QC Comfort headphones play music flawlessly). Furthermore, the mouse cursor in Windows 10 on the Surface Laptop 4 remains very responsive when using a USB dongle instead of Bluetooth - such as with my Razer Viper.&lt;/p&gt;&lt;p&gt;The Bluetooth mouse I use is a Logitech MX Master 2s but the same stuttering occurs on similar devices such as the MX Master 3 or Microsoft&amp;#x27;s Surface Precision Mouse. Using the MX Master 2s on both my MacBook Pro or a 9th generation i7 Razer Blade 15 Advanced produces zero lag or stuttering.&lt;/p&gt;&lt;p&gt;The cuplrit appears to be related to a bug in how Intel AX Wireless Bluetooth cards (either AX1650 or AX201) control suspending Low Energy Bluetooth input devices in Windows 10 when using an 11th generation Intel chips (particularly 1165G and 1185G).&lt;/p&gt;&lt;p&gt;It apparently also occurs under Linux but I have not been able to test this myself. As such I&amp;#x27;m not able to confirm a fix for any Linux distros. If it is purely a hardware issue and unrelated to operating system then it is plausible that it could occur under macOS too were Apple to use 11th-generation Intel chips in a hardware refresh.&lt;/p&gt;&lt;h2 id=&quot;fix-bluetooth-mouse-stuttering-in-windows-10&quot;&gt;Fix Bluetooth mouse stuttering in Windows 10&lt;/h2&gt;&lt;ol&gt;&lt;li&gt;Open Registry Editor by hitting &lt;code class=&quot;language-text&quot;&gt;Win + R&lt;/code&gt; and typing &lt;code class=&quot;language-text&quot;&gt;regedit&lt;/code&gt; (you will need to be an Administrator)&lt;/li&gt;&lt;li&gt;Navigate the tree to &lt;code class=&quot;language-text&quot;&gt;HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\USB\VID_8087&amp;amp;PID_0026&lt;/code&gt;. You will find an identifier that may be different from one device to another depending on the particular AX card in use. Some Dell users have an entry named &lt;code class=&quot;language-text&quot;&gt;5&amp;amp;31036e8e&amp;amp;0&amp;amp;10&lt;/code&gt; while on my Surface Laptop 4 this was &lt;code class=&quot;language-text&quot;&gt;5&amp;amp;1487bdb9&amp;amp;0&amp;amp;10&lt;/code&gt;. The important part is that you need to find the entry with a &lt;code class=&quot;language-text&quot;&gt;DeviceDesc&lt;/code&gt; containing &lt;code class=&quot;language-text&quot;&gt;Intel Wireless Bluetooth&lt;/code&gt;. Thankfully there is usually only one entry so it should be easy to find.&lt;/li&gt;&lt;li&gt;Now that you have the device entry, navigate to the &lt;code class=&quot;language-text&quot;&gt;Device Parameters&lt;/code&gt; folder.&lt;/li&gt;&lt;li&gt;Edit the &lt;code class=&quot;language-text&quot;&gt;DeviceSelectiveSuspended&lt;/code&gt; dword entry and set the value to 0.&lt;/li&gt;&lt;li&gt;Add a new dword32 entry named &lt;code class=&quot;language-text&quot;&gt;SelectiveSuspendEnabled&lt;/code&gt; and set the value to 0.&lt;/li&gt;&lt;li&gt;Add a new dword32 entry named &lt;code class=&quot;language-text&quot;&gt;SelectiveSuspendSupported&lt;/code&gt; and set the value to 0.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;You might need to reboot for everything to kick in but your mouse movements should now be smooth and lag-free.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[How to sign your commits to GitHub using Visual Studio Code on Windows 10 and WSL2]]></title><description><![CDATA[I have recently been spending more and more time doing web development on Windows 10 using WSL2 and  Windows Terminal . I haven't completely…]]></description><link>https://www.39digits.com/signed-git-commits-on-wsl2-using-visual-studio-code</link><guid isPermaLink="false">https://www.39digits.com/signed-git-commits-on-wsl2-using-visual-studio-code</guid><pubDate>Thu, 27 Aug 2020 19:27:14 GMT</pubDate><content:encoded>&lt;p&gt;I have recently been spending more and more time doing web development on Windows 10 using WSL2 and &lt;a href=&quot;https://github.com/microsoft/terminal&quot;&gt;Windows Terminal&lt;/a&gt;. I haven&amp;#x27;t completely moved away from MacOS but the experience is so enjoyable that I will likely switch to a Surface Book when it comes time to upgrade my MacBook Pro.&lt;/p&gt;&lt;p&gt;The one (minor) gripe holding me back was the inability to use signed commits from within Visual Studio Code when code, git and the gpg agent are running inside of WSL2. The issue? The passphrase prompt would not show up.&lt;/p&gt;&lt;p&gt;A pinentry to enter my passphrase for signed commits is displayed inside Windows Terminal if I set it up to display via TTY and do all of my git commands from within the terminal. But I couldn&amp;#x27;t find a way to display a pinentry within Windows 10 when using either the Visual Studio Code git interface or the built-in terminal. I don&amp;#x27;t mind using a separate terminal to run my git commands, but it&amp;#x27;s just so convenient to do it within my editor.&lt;/p&gt;&lt;p&gt;Thankfully, I have now solved it!&lt;/p&gt;&lt;h2 id=&quot;a-brief-intro-about-signed-commits&quot;&gt;A brief intro about signed commits&lt;/h2&gt;&lt;p&gt;Commits in git will be attributed to whatever values you have set against the name and email fields in your gitconfig. In theory, this would allow anybody to impersonate someone else when committing code by using the email address associated with the other person&amp;#x27;s GitHub account.&lt;/p&gt;&lt;p&gt;It may be worth noting that this only allows someone to change the author of commits. It won&amp;#x27;t grant access to any private repositories or elevate rights on GitHub.&lt;/p&gt;&lt;p&gt;Thankfully we can use GnuPG to sign our commits which in turn allows Github to verify that the changes were authored by us and not some imposter.&lt;/p&gt;&lt;p&gt;&lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1024px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:28.515625%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAYAAADDl76dAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAvElEQVQY062R2W7DIBBF/f8/2ST10sURDmDDsJ0KUPMQyepLkY6QBnTmamawdqeUwn+dQRtLpYpDiIgEYkrkXMg5n1Dan9TIxNTrNdiwfCy8XW6M08Ln18p6V2iz4yXifcDLC60WMceBbQh6F7yXLjTG8NAaY3diTORSqBMo7T6nJkqV1HkmnOeRy/XG+zgxzQvb9ng+nkEBiYIT19IpK4QQulCpO9/rilJbkznn/kz3u8Ta2Enk8H2eVfgDUJrWlOORDYIAAAAASUVORK5CYII=&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;Verified commits on GitHub&quot; title=&quot;Verified commits on GitHub&quot; src=&quot;/static/a7779da968c9ad157dc17b1fe01c84be/2bef9/verified-commits-on-github.png&quot; srcSet=&quot;/static/a7779da968c9ad157dc17b1fe01c84be/6f3f2/verified-commits-on-github.png 256w,/static/a7779da968c9ad157dc17b1fe01c84be/01e7c/verified-commits-on-github.png 512w,/static/a7779da968c9ad157dc17b1fe01c84be/2bef9/verified-commits-on-github.png 1024w,/static/a7779da968c9ad157dc17b1fe01c84be/71c1d/verified-commits-on-github.png 1536w,/static/a7779da968c9ad157dc17b1fe01c84be/a878e/verified-commits-on-github.png 2048w,/static/a7779da968c9ad157dc17b1fe01c84be/81386/verified-commits-on-github.png 2640w&quot; sizes=&quot;(max-width: 1024px) 100vw, 1024px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
    &lt;/span&gt;&lt;/p&gt;&lt;p&gt;A handy bonus of signing our commits is that GitHub will now also show a &lt;code class=&quot;language-text&quot;&gt;verified&lt;/code&gt; tag allowing everybody to see we&amp;#x27;re trustworthy.&lt;/p&gt;&lt;h2 id=&quot;update-windows-10&quot;&gt;Update Windows 10&lt;/h2&gt;&lt;p&gt;The complete setup worked on a fresh install of the current stable version of Windows 10 (2004 Build 19041.450). I cannot confirm if this will work on an older version so you may be required to update.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;You can check your version of Windows by hitting Win+R and running &amp;quot;winver&amp;quot;&lt;/p&gt;&lt;/blockquote&gt;&lt;h2 id=&quot;install-gpg&quot;&gt;Install GPG&lt;/h2&gt;&lt;p&gt;Start by &lt;a href=&quot;https://www.gpg4win.org/&quot;&gt;installing GPG4Win&lt;/a&gt; in Windows 10. GnuPG core is the only requirement but I usually also install Kleopatra - everything else can be left unchecked.&lt;/p&gt;&lt;p&gt;Despite most of the setup being inside WSL2 we still need a graphical pinentry app to be installed in Windows 10. Visual Studio Code is installed on the Windows 10 side and as such we need that graphical pinentry to enter a passphrase when committing code from the editor.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;The rest of the commands in this tutorial will be run inside WSL2.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Open a Terminal into WSL2 and install the required packages. These packages were already installed for me and this is only here for completeness sake.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apt-get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; gpg gnupg gpg-agent&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I use Ubuntu so your command may differ if your distribution of choice uses another package manager.&lt;/p&gt;&lt;h2 id=&quot;update-configuration&quot;&gt;Update configuration&lt;/h2&gt;&lt;p&gt;Inside WSL2, edit &lt;code class=&quot;language-text&quot;&gt;~/.gnupg/gpg-agent.conf&lt;/code&gt; (if this file does not exist, create it).&lt;/p&gt;&lt;div class=&quot;gatsby-code-title gatsby-remark-code-title&quot;&gt;~/.gnupg/gpg-agent.conf&lt;/div&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;vim&quot;&gt;&lt;pre class=&quot;language-vim&quot;&gt;&lt;code class=&quot;language-vim&quot;&gt;default&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;cache&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ttl &lt;span class=&quot;token number&quot;&gt;34560000&lt;/span&gt;
max&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;cache&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ttl &lt;span class=&quot;token number&quot;&gt;34560000&lt;/span&gt;
pinentry&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;program &lt;span class=&quot;token string&quot;&gt;&amp;quot;/mnt/c/Program Files (x86)/GnuPG/bin/pinentry-basic.exe&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;default-cache-ttl&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;max-cache-ttl&lt;/code&gt; is set to a really high value - 400 days to be precise. GnuPG will now cache our passphrase for that length of time or until we next restart our laptop. This saves us from being prompted for the passphrase every single commit given the default cache time is (if I recall) 10 minutes.&lt;/p&gt;&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;pinentry-program&lt;/code&gt; configuration explicitly tells GnuPG to use the pin entry app installed in Windows 10 to prompt the user to enter the passphrase set when generating the GPG key pair (see next section).&lt;/p&gt;&lt;p&gt;It is recommended to explicitly restart the gpg agent to force these changes. Alternatively, restart Windows at this point.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;gpgconf --kill gpg-agent&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&quot;generate-gpg-key-pair&quot;&gt;Generate GPG Key Pair&lt;/h2&gt;&lt;p&gt;The generated GPG key pair consists of a:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;private key&lt;/strong&gt; - As the name suggests it should not be shared. This key is used to sign our commits.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;public key&lt;/strong&gt; - It is safe to distribute this key and we&amp;#x27;ll upload to GitHub later. GitHub uses the public key to verify the signature in our commits to correctly identify us as the author.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Run the following command to generate the actual key pair.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;gpg --full-generate-key&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You will be prompted to confirm certain values:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Key Type&lt;/strong&gt;: Select &lt;code class=&quot;language-text&quot;&gt;RSA and RSA&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Key Size&lt;/strong&gt;: 4096&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Expiry&lt;/strong&gt;: Select a timeframe that is suitable to your requirements.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;You will then be asked to enter your name and email address. It is important that you enter the same email address that you use with your GitHub account. Ensure that this email address is also set in your local git config (&lt;code class=&quot;language-text&quot;&gt;~/.gitconfig&lt;/code&gt;).&lt;/p&gt;&lt;p&gt;&lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1024px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:57.8125%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAIAAADtbgqsAAAACXBIWXMAAAsTAAALEwEAmpwYAAACxklEQVQoz03Q3UtTARjH8fM/dGWmnr1vx/PidrYdz9u247azc6bZnNp0mmaROqfzZUaUpZFRRFkmYUQpBL3ZRCESjYIuypeL6kbRMAqD0JhM3aabF9pChQg+PHffi98DrLzrTYUnwvOv58cfLL25G519nAjPbK0tJNYWkhvfk9Gl7a3w9ubvZPRnMjKX+DUWnx38GroxPdD56WEL0H3lak9v/52+/u5rN+sb2+obmv1NQX9Tuz9wIOgPtO3fA22NgeBpX2tP19m5512AxSoaaJs+14LhDIYzGpQAFWg6qEnLUP5zOFOVnqXep8mQQIfS5CVHnVNDPYCY7+L4QiHfLRYU884igrbmUpyRYFEdheE0pqNQLQnn5EKo8QCcQ0hUOTWV5V8mBgFUR5Gs3eF0c/YCm+BCtGR1XfP7z3MfPk5OTk1PTc0sLn7ru3dfokA1iEEN6yHUACrQcs/xt6OPADbPyXKCXXSxnIgTZoUC9jReGPmRiKyGV1dX19c3UqlUaHgUlMMQalTDeg1ikKkwvsDdcf0WQHNOkrVRJt5AchhOS5Toxc7ueCy+srwcjUYjkcjuzs6LodD/sVSJFri9l24PAFaHi7E4aDPPWAQ9Yc6UZp/vuJxK/YnFYltbm/F4fHd352VoBJQjEGo4iCUKxFnkfTU+BlAmB2NxkKyd5QSCtkKo0S4cawmea2hsrfUFan3N/kCwqLRCCenUsH4vRg2gHHF7TjwdHgbsossmuOyiK89RyDuLHPnFpjzn3gQ9g+G0GtYfAdWgHFFn46psXAnhKhjPksEeb+WzoScALxYyFp5k7ZSJp0w8beEZs4NkbARj1ZMcoiU1iHHvz4hBgxiRHALTkSpIR5qF9o4uwMIyCqVKKZdKwcyMjCyZTC6RynRarLbWV3GyxV3W7Klq8dQESirPlFXXldX4S6t8rOilBC9ffOovdt4RzJCaFhEAAAAASUVORK5CYII=&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;Create the passphrase for your GPG key pair&quot; title=&quot;Create the passphrase for your GPG key pair&quot; src=&quot;/static/77665b51f9e1c3ecd4e3cd0feeb45d1b/2bef9/create-passphrase-for-gpg-keypair.png&quot; srcSet=&quot;/static/77665b51f9e1c3ecd4e3cd0feeb45d1b/6f3f2/create-passphrase-for-gpg-keypair.png 256w,/static/77665b51f9e1c3ecd4e3cd0feeb45d1b/01e7c/create-passphrase-for-gpg-keypair.png 512w,/static/77665b51f9e1c3ecd4e3cd0feeb45d1b/2bef9/create-passphrase-for-gpg-keypair.png 1024w,/static/77665b51f9e1c3ecd4e3cd0feeb45d1b/71c1d/create-passphrase-for-gpg-keypair.png 1536w,/static/77665b51f9e1c3ecd4e3cd0feeb45d1b/29114/create-passphrase-for-gpg-keypair.png 1920w&quot; sizes=&quot;(max-width: 1024px) 100vw, 1024px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
    &lt;/span&gt;&lt;/p&gt;&lt;p&gt;Finally, you will be asked for a passphrase. The passphrase will be used to encrypt the private key for added security. You will then be prompted to enter this passphrase when making a signed commit to decrypt the key.&lt;/p&gt;&lt;h2 id=&quot;retrieve-the-unique-signing-key-id&quot;&gt;Retrieve the unique signing key id&lt;/h2&gt;&lt;p&gt;Now that our key pair has been created we need to retrieve our unique signing key id.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;gpg --list-secret-keys --keyid-format LONG&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here is an example of the output you should see with fabricated ids.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;-----------------------------------
sec   rsa4096/00EF4D3F22885E4B 2019-11-20 [SC]
      1234567890ABCDEF1234567890ABCDEF12345678
uid                 [ultimate] Rick Deckard &amp;lt;deckard@example.com&amp;gt;
ssb   rsa4096/ABC123D7FAA52318 2019-11-20 [E]&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The key id is the 16-character sequence following &lt;code class=&quot;language-text&quot;&gt;rsa4096/&lt;/code&gt; in the &lt;code class=&quot;language-text&quot;&gt;sec&lt;/code&gt; (secret) row at the top. In our example above, our signing key id would be &lt;code class=&quot;language-text&quot;&gt;00EF4D3F22885E4B&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Alternatively, you could run &lt;code class=&quot;language-text&quot;&gt;gpg --list-signatures&lt;/code&gt; to find the same signing key id next to a &lt;code class=&quot;language-text&quot;&gt;sig&lt;/code&gt; tag.&lt;/p&gt;&lt;p&gt;Make note of this signing key as we will use it in the sections below.&lt;/p&gt;&lt;h2 id=&quot;git-settings&quot;&gt;Git Settings&lt;/h2&gt;&lt;p&gt;If you haven&amp;#x27;t already done so, make sure to add the same email used in GitHub and your GPG key pair to your gitconfig.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; config --global user.email &lt;span class=&quot;token string&quot;&gt;&amp;quot;deckard@example.com&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And if not yet done, be sure to add your name too.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; config --global user.name &lt;span class=&quot;token string&quot;&gt;&amp;quot;Rick Deckard&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Configure git to use your GPG signing key.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; config --global user.signingkey 00EF4D3F22885E4B&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can conveniently set git to sign every commit by default. If you don&amp;#x27;t then you will need to include the &lt;code class=&quot;language-text&quot;&gt;-s&lt;/code&gt; flag in the command line for every commit you wish to sign.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; config --global commit.gpgsign &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;There is also the option to automatically sign tags.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; config --global tag.gpgsign &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you don&amp;#x27;t want to set these globally, you can also set it per repository using a &lt;code class=&quot;language-text&quot;&gt;--local&lt;/code&gt; parameter when in a git controlled project directory.&lt;/p&gt;&lt;h2 id=&quot;add-the-gpg-public-key-to-github&quot;&gt;Add the GPG public key to GitHub&lt;/h2&gt;&lt;p&gt;Firstly, retrieve and copy your GPG public key using your signing key. You will add this to your GitHub account so that it can verify every signed commit as actually coming from you.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;gpg --armor --export 00EF4D3F22885E4B&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Open a browser and navigate to your &lt;a href=&quot;https://github.com/settings/keys&quot;&gt;GitHub SSH and GPG Keys settings&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Click on the &lt;code class=&quot;language-text&quot;&gt;New GPG Key&lt;/code&gt; button and paste in your previously copied public key. This should be all the text including the start &lt;code class=&quot;language-text&quot;&gt;-----BEGIN PGP PUBLIC KEY BLOCK-----&lt;/code&gt; and end &lt;code class=&quot;language-text&quot;&gt;-----END PGP PUBLIC KEY BLOCK-----&lt;/code&gt; tags.&lt;/p&gt;&lt;h2 id=&quot;enable-commit-signing-in-visual-studio-code&quot;&gt;Enable commit signing in Visual Studio Code&lt;/h2&gt;&lt;p&gt;We now need to set Visual Studio Code to append the &lt;code class=&quot;language-text&quot;&gt;-s&lt;/code&gt; flag to every commit that it runs from the built-in git user interface.&lt;/p&gt;&lt;p&gt;Thankfully, this is really easy to accomplish. Open the settings and search for &amp;quot;gpg&amp;quot; and check the box for &lt;code class=&quot;language-text&quot;&gt;Enables commit signing with GPG&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Alternatively, you can add this setting directly to your &lt;code class=&quot;language-text&quot;&gt;settings.json&lt;/code&gt; with the following line.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token property&quot;&gt;&amp;quot;git.enableCommitSigning&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&quot;make-a-few-signed-commits&quot;&gt;Make a few signed commits&lt;/h2&gt;&lt;p&gt;I recommend running a test commit from both the command-line as well as via the Visual Studio Code interface.&lt;/p&gt;&lt;p&gt;Regardless of which method you decide to test first you will see a window pop up on your first commit now that GPG signing is setup. This is the pinentry and it is asking for your passphrase that you set when creating the key pair. The passphrase is used to decrypt your private key before git will complete the commit.&lt;/p&gt;&lt;p&gt;&lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1024px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:28.515625%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAIAAABM9SnKAAAACXBIWXMAAAsTAAALEwEAmpwYAAABeUlEQVQY0wFuAZH+APTLif/rq4BoT4VtU//lqPfpyPzitPvnwPz25/347v358Pv26fz04Pz03vnhrO/GiteqgNCmgdCkfsWefQD1zpPw26lOUVFUT0b46Lr25r+81uHZ4ubk4uDl5eXp6ens6ub258j98trw1qnaupPKrZDApo3CpYq6oooAza6PaV1SHx8jHCIoxbKS+eC4pM7iwtDX0c7M1tbW2dnY5OPg9OPG9+zX7tm62cSmzrykzLigyrWcv62WAFxrdxYOCjAkICcjJYV5auHPt/Hs5uzs6+7v7fb29vf39/Pz8ebOrcGzoefUutzKsdLCqcW2nruumcOxmwBYcYc9Q0xISUxBRkpweYCvsKjm5+Xz8/Lr6+ro6OXq6+jl5eS1p5mEfn2lnI+yrqCioZeKkY18h4l1hYwAYHuUXHKJWV9oWV1lU3COWnSRjJypipqni5akmZeghoeTkJWes7Sul5mauruwkaCleo6ceo6bfo+aZXyTE0T1kkxevocAAAAASUVORK5CYII=&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;Enter your GPG passphrase&quot; title=&quot;Enter your GPG passphrase&quot; src=&quot;/static/291929d9c1f6f308df87333d421999fd/2bef9/enter-passphrase-for-gpg.png&quot; srcSet=&quot;/static/291929d9c1f6f308df87333d421999fd/6f3f2/enter-passphrase-for-gpg.png 256w,/static/291929d9c1f6f308df87333d421999fd/01e7c/enter-passphrase-for-gpg.png 512w,/static/291929d9c1f6f308df87333d421999fd/2bef9/enter-passphrase-for-gpg.png 1024w,/static/291929d9c1f6f308df87333d421999fd/71c1d/enter-passphrase-for-gpg.png 1536w,/static/291929d9c1f6f308df87333d421999fd/29114/enter-passphrase-for-gpg.png 1920w&quot; sizes=&quot;(max-width: 1024px) 100vw, 1024px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
    &lt;/span&gt;&lt;/p&gt;&lt;p&gt;At this stage the passphrase will be cached after the first successful entry as per our &lt;code class=&quot;language-text&quot;&gt;~/.gnupg/gpg-agent.conf&lt;/code&gt; settings.&lt;/p&gt;&lt;h2 id=&quot;verify-signed-commits&quot;&gt;Verify signed commits&lt;/h2&gt;&lt;p&gt;Locally, you can verify that the commits were signed with your signature.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; log --show-signature -1&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you now push any of these changes to your remote in GitHub you will see a verified status next to each signed commit.&lt;/p&gt;&lt;h2 id=&quot;remote-containers&quot;&gt;Remote Containers&lt;/h2&gt;&lt;p&gt;As an added bonus I can also confirm the above setup worked when running commits from within the &lt;a href=&quot;https://hub.docker.com/_/microsoft-vscode-devcontainers&quot;&gt;Microsoft pre-built VS Code Development Container Node image&lt;/a&gt;. This is helpful if you want to use &lt;a href=&quot;https://code.visualstudio.com/docs/remote/create-dev-container&quot;&gt;Remote Containers for development&lt;/a&gt; and make signed commits from the Visual Studio Code integrated terminal.&lt;/p&gt;&lt;p&gt;The only extra requirement when using Remote Containers is to install &lt;code class=&quot;language-text&quot;&gt;socat&lt;/code&gt; on your WSL2 instance otherwise the git commits won&amp;#x27;t work.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apt-get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; socat&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Keep in mind that the version of git that comes with the Microsoft development containers are a little behind so you might want to add an upgrade step to the Dockerfile. Or bring your own Dockerfile - you&amp;#x27;re the master of your own destiny.&lt;/p&gt;&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;&lt;p&gt;And that’s it! Enjoy your newly verified status on GitHub.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Customise the Spotify UI theme]]></title><description><![CDATA[I loved Rdio. Unfortunately, it was eclipsed by the popularity of Spotify and eventually had to close down forcing me to change music…]]></description><link>https://www.39digits.com/customise-spotify-theme</link><guid isPermaLink="false">https://www.39digits.com/customise-spotify-theme</guid><pubDate>Fri, 15 May 2020 00:07:33 GMT</pubDate><content:encoded>&lt;p&gt;I loved Rdio. Unfortunately, it was eclipsed by the popularity of Spotify and eventually had to close down forcing me to change music streaming services. Ever since switching I have missed that clean, flat and beautiful light themed app.&lt;/p&gt;&lt;p&gt;To my absolute joy, I recently discovered that there is an unofficial way to customise the look of Spotify using a tool called Spicetify.&lt;/p&gt;&lt;p&gt;&lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1024px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:61.71875%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAABYlAAAWJQFJUiTwAAACX0lEQVQoz3WSS08UQRSF+1+49Q/4A1y7YyN73Zi4RlduNGriIyG+goSQiBIkMaKJETC4cEFUwk5weEwPoyHADMNjmH5WP6qqu/lM1wDxEW9ycm/VTU6dc29ZLwYe02k38XYbNJtNthpN1u0K2/UFlha/cbCzSejsItwWcbhPEh+QpB5JEpAmEWmamKxkSmdvE+vj6FP0IaRSUYbO4drtO/RcuMS5voc4dg3298iCkMxxyVyvm4MAnWVorVFKURSw06hj3X0wSJFniCgyDaUkZ68+4VTfMD39b1hfWiHbbZG2muidbfTudjd32qgjMiUleXHIzlYVq//RIFEkCIKALMtM8974DL23Rui9MURjfp58tUKqlHGiiwJdHKLzwqg7VtglrGENDw2bCymlyVop/DDEc10SIVBOB3nQRoqQNAxI43Ju6QlkmhJHEULEtLbWsN6OjxrpSZJ0XywJPQ/hOsSuQ+Q4ROVZiP8iDHxEKGht2livxp6TpClRFB1ZUIShMGp0OXjfQ4vQPHRs8W9IWaqVNNZtrGdjr83KPc/D931jIwhD0pKoROCjPRdd2jdL+xPauItNPfd5Fmvi3Qyu59JxHGO7bBjCUt0xWSRONvr7Io7PcRyT5TnTU1NYLyfeQyZxXdfYLhWWdey5KM9FOgcoESKPFvcPlOoSZhmTk9NYl/uuc39ghPZWE3wHAg9EAGEAgQ9FzmGeY37uf6Kcexmzc1+wzpy/wumLN/nw6Str9horyyvU63VqNZuNjQ0qlQo126ZarbG8ukat/pNq7YepV6pdLH5fZtWus7Awzy+r8nUEYnapIgAAAABJRU5ErkJggg==&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;Spotify with Spicetify&amp;#x27;s default light theme&quot; title=&quot;Spotify with Spicetify&amp;#x27;s default light theme&quot; src=&quot;/static/513f991819b27d92327a91747f37b217/2bef9/spotify-light-theme-nirvana.png&quot; srcSet=&quot;/static/513f991819b27d92327a91747f37b217/6f3f2/spotify-light-theme-nirvana.png 256w,/static/513f991819b27d92327a91747f37b217/01e7c/spotify-light-theme-nirvana.png 512w,/static/513f991819b27d92327a91747f37b217/2bef9/spotify-light-theme-nirvana.png 1024w,/static/513f991819b27d92327a91747f37b217/71c1d/spotify-light-theme-nirvana.png 1536w,/static/513f991819b27d92327a91747f37b217/a878e/spotify-light-theme-nirvana.png 2048w,/static/513f991819b27d92327a91747f37b217/2abf7/spotify-light-theme-nirvana.png 3068w&quot; sizes=&quot;(max-width: 1024px) 100vw, 1024px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
    &lt;/span&gt;&lt;/p&gt;&lt;h2 id=&quot;install-spicetify&quot;&gt;Install spicetify&lt;/h2&gt;&lt;p&gt;I predominantly use MacOS and already have the &lt;a href=&quot;https://brew.sh/&quot;&gt;homebrew package manager&lt;/a&gt; installled making that the most convenient method to install Spicetify.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;brew &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; khanhas/tap/spicetify-cli&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Your own setup and requirements may differ. If so read up on the alternate &lt;a href=&quot;https://github.com/khanhas/spicetify-cli/wiki/Installation&quot;&gt;Spicetify installation instructions for Linux, Windows and Mac&lt;/a&gt;.&lt;/p&gt;&lt;h2 id=&quot;generate-the-config-file&quot;&gt;Generate the config file&lt;/h2&gt;&lt;p&gt;Once installation has completed you need to run Spicetify once without any parameters to generate the config file.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;spicetify&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To find out where the configuration file has been saved on your operating system you can use the &lt;code class=&quot;language-text&quot;&gt;-c&lt;/code&gt; parameter.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;spicetify -c&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;On MacOS it saves to &lt;code class=&quot;language-text&quot;&gt;~/spicetify_data/config.ini&lt;/code&gt;.&lt;/p&gt;&lt;h2 id=&quot;backup-the-current-spotify-configuration&quot;&gt;Backup the current Spotify configuration&lt;/h2&gt;&lt;p&gt;Before we can apply a theme we need to run a backup in case we need to rollback upon an error.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;spicetify backup&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&quot;apply-the-default-light-theme&quot;&gt;Apply the default light theme&lt;/h2&gt;&lt;p&gt;You&amp;#x27;re just one more command away from applying the default light theme that ships with Spicetify.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;spicetify apply&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Open Spotify and you will now be greeted with a truly beautiful light interface.&lt;/p&gt;&lt;p&gt;&lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1024px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:57.421875%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAABYlAAAWJQFJUiTwAAACOklEQVQoz3WSyWoUQRjH+3U8ePPiQQ+CR9/A5/ANRDypRDxEo8EcImRhlBGdgwGNIYmoYNZJT89MJpNlkunppbqru3qZ/klVYkDEgo//t9S3/aust6+nGAYBR8cnyNCjtdeiXl+isfSNgdMkGx4jfJfYPyMJh6TRiFT6pElImkpUmhrMsozRoIM1+XIGfVSqDK7Xf/Dk+n0e3n2M3fjE2N4lc2yKICAfQ16U5LmW4kJyU2xcwWl/C+vRs1ekiUSIGBjTqLV5fucj9648YK/2ndQ9Qng+cRyTZcok/yuKoqw47W9jTU/PkKYpUiZAhb3TY37iAzMvvnK4vgntpknSk/xPMqUQkeSkt431bmGOoizPO6mMsiyoKICCoipJiwKVKTNhkiQopYiiyAyhc7RfxrHZ8Hh/E6tef0+eKXNBd0tlQjA4ZXQ2JBIRUSRMku+6hK57rg+HiDAklhJ/5JpYkihOultYs3N18iTGu+ApzzKkCAnD8HKloiiMLaW81PW0ZVkaXUs5rvi59hlrYmqRRIq/CugVAv2qFy+oUcdNwzxHCGHW1jHtD3zf/JDa/BzWyuqK6eQHAXEsDcGR7xtbJ2jONPra5/vG1viHIn3P8zxTcGGxhjX5dJLGm1kKdwCRgFhQBR6VCEHGEEdGCt9jHPiQKTNAVVWmiKZA06TP8uoy1tVrt7lx8xYbvzZoOx3a3R6dThfHcWg5DrZt09zdpe206PUO6O4fsN/r0zvo0+8fGnunaRtcXfvCb3hKMCXF5SeFAAAAAElFTkSuQmCC&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;Spotify with Spicetify&amp;#x27;s default light theme&quot; title=&quot;Spotify with Spicetify&amp;#x27;s default light theme&quot; src=&quot;/static/133dad7a19be544b54063f4b00885812/2bef9/spotify-light-theme-synthwave.png&quot; srcSet=&quot;/static/133dad7a19be544b54063f4b00885812/6f3f2/spotify-light-theme-synthwave.png 256w,/static/133dad7a19be544b54063f4b00885812/01e7c/spotify-light-theme-synthwave.png 512w,/static/133dad7a19be544b54063f4b00885812/2bef9/spotify-light-theme-synthwave.png 1024w,/static/133dad7a19be544b54063f4b00885812/71c1d/spotify-light-theme-synthwave.png 1536w,/static/133dad7a19be544b54063f4b00885812/a878e/spotify-light-theme-synthwave.png 2048w,/static/133dad7a19be544b54063f4b00885812/832a2/spotify-light-theme-synthwave.png 3370w&quot; sizes=&quot;(max-width: 1024px) 100vw, 1024px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
    &lt;/span&gt;&lt;/p&gt;&lt;h2 id=&quot;add-new-themes&quot;&gt;Add new themes&lt;/h2&gt;&lt;p&gt;Perhaps you want to explore creating your own theme or applying themes created by others. There is a &lt;a href=&quot;https://github.com/morpheusthewhite/spicetify-themes&quot;&gt;repository of Spicetify themes&lt;/a&gt; if you wanted some inspiration.&lt;/p&gt;&lt;p&gt;Install any custom themes into a &lt;code class=&quot;language-text&quot;&gt;Themes&lt;/code&gt; directory within your Spicetify&amp;#x27;s data directory. For example, on MacOS you would install these into &lt;code class=&quot;language-text&quot;&gt;~/spicetify_data/Themes&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Once you have chosen a new look for Spotify you will need to run a command to set that in your config.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;spicetify config current_theme NAME_OF_THEME_HERE&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now that a new custom theme is set in your config its time to apply that to Spotify.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;spicetify apply&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Use &lt;code class=&quot;language-text&quot;&gt;SpicetifyDefault&lt;/code&gt; as the &lt;code class=&quot;language-text&quot;&gt;current_theme&lt;/code&gt; if you ever wish to revert back to the original Spicetify light theme.&lt;/p&gt;&lt;h2 id=&quot;restore-the-original-spotify-interface&quot;&gt;Restore the original Spotify interface&lt;/h2&gt;&lt;p&gt;You can quickly revert back to the default Spotify theme.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;spicetify config inject_css &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; replace_colors &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
spicetify apply&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&quot;no-such-file-error&quot;&gt;No Such File error&lt;/h2&gt;&lt;p&gt;You might get an error upon running spicetify.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;fatal open /Applications/Spotify.app/Contents/Resources/Apps/zlink/css/user.css: no such file or directory&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;You can fix this by reapplying your theme.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;spicetify apply&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Add Tailwind to a Gatsby project]]></title><description><![CDATA[Tailwind CSS is a highly customizable, low-level CSS framework that gives you all of the building blocks you need to build bespoke designs…]]></description><link>https://www.39digits.com/add-tailwind-to-gatsby</link><guid isPermaLink="false">https://www.39digits.com/add-tailwind-to-gatsby</guid><pubDate>Thu, 14 May 2020 07:32:09 GMT</pubDate><content:encoded>&lt;blockquote&gt;&lt;p&gt;Tailwind CSS is a highly customizable, low-level CSS framework that gives you all of the building blocks you need to build bespoke designs without any annoying opinionated styles you have to fight to override.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Tailwind CSS has completely changed the way I develop web apps thanks to the &lt;a href=&quot;https://tailwindcss.com/docs/utility-first&quot;&gt;utility first workflow&lt;/a&gt;. And the recent release of &lt;a href=&quot;https://tailwindcss.com/docs/release-notes/#tailwind-css-v1-4&quot;&gt;v1.4&lt;/a&gt; made it even easier to get a very small production build by including PurgeCSS as a built-in process. If you&amp;#x27;ve been on the fence about trying out this great framework now is the time to jump in.&lt;/p&gt;&lt;h2 id=&quot;create-the-gatsby-app&quot;&gt;Create the Gatsby app&lt;/h2&gt;&lt;p&gt;We&amp;#x27;ll keep things simple and generate a Gatsby app using &lt;code class=&quot;language-text&quot;&gt;npx&lt;/code&gt; and the very barebone Hello World starter.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;npx gatsby new gatsby-tailwind https://github.com/gatsbyjs/gatsby-starter-hello-world&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once that has installed be sure to change into the newly created &lt;code class=&quot;language-text&quot;&gt;gatsby-tailwind&lt;/code&gt; project directory.&lt;/p&gt;&lt;h2 id=&quot;add-tailwind-to-the-project&quot;&gt;Add Tailwind to the project&lt;/h2&gt;&lt;h3 id=&quot;install-the-required-dependencies&quot;&gt;Install the required dependencies&lt;/h3&gt;&lt;p&gt;Install the development dependencies from our project root.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; --save-dev tailwindcss autoprefixer&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Tailwind doesn&amp;#x27;t include any vendor prefixes out of the box so we&amp;#x27;ll be relying on &lt;strong&gt;autoprefixer&lt;/strong&gt; to generate those for our final build.&lt;/p&gt;&lt;h3 id=&quot;create-the-tailwind-css&quot;&gt;Create the Tailwind CSS&lt;/h3&gt;&lt;p&gt;I usually prefer to create a dedicated styles directory under &lt;code class=&quot;language-text&quot;&gt;src/styles&lt;/code&gt; when working on a project with Tailwind.&lt;/p&gt;&lt;p&gt;Create the directory and our global CSS file now.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;mkdir&lt;/span&gt; -p src/styles &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;touch&lt;/span&gt; src/styles/main.css&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To add Tailwind to our Gatsby project, we need simply add the &lt;code class=&quot;language-text&quot;&gt;@tailwind&lt;/code&gt; directives inside &lt;code class=&quot;language-text&quot;&gt;src/styles/main.css&lt;/code&gt;. These directives will be replaced with the actual Tailwind CSS during the build.&lt;/p&gt;&lt;div class=&quot;gatsby-code-title gatsby-remark-code-title&quot;&gt;src/styles/main.css&lt;/div&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@tailwind&lt;/span&gt; base&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;/* Your own custom base styles */&lt;/span&gt;

&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@tailwind&lt;/span&gt; components&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;/* Your own custom component styles */&lt;/span&gt;

&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@tailwind&lt;/span&gt; utilities&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;/* Your own custom utilities */&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This &lt;code class=&quot;language-text&quot;&gt;src/styles/main.css&lt;/code&gt; file will be our global CSS file for the entire app. It will contain all of our project&amp;#x27;s pre-generated CSS including any of our extra custom CSS. The beauty and strength of using Tailwind is that you won&amp;#x27;t be required to write much extra CSS though.&lt;/p&gt;&lt;h3 id=&quot;generate-the-tailwind-configuration&quot;&gt;Generate the Tailwind configuration&lt;/h3&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;./node_modules/.bin/tailwindcss init&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;An empty &lt;code class=&quot;language-text&quot;&gt;tailwind.config.js&lt;/code&gt; file is generated within the project root. This config file allows us to dramatically customise Tailwind and is one of the single biggest strengths of this excellent framework. To read more on how you can configure Tailwind check the &lt;a href=&quot;https://tailwindcss.com/docs/configuration&quot;&gt;official configuration documentation&lt;/a&gt;.&lt;/p&gt;&lt;h3 id=&quot;import-the-css-into-our-app&quot;&gt;Import the CSS into our App&lt;/h3&gt;&lt;p&gt;There are two options to import a global CSS file into our app:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Import it via a &lt;code class=&quot;language-text&quot;&gt;gatsby-browser.js&lt;/code&gt; configuration file.&lt;/li&gt;&lt;li&gt;Import it into a Layout higher order component.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;To keep things simple we&amp;#x27;ll cover the &lt;code class=&quot;language-text&quot;&gt;gatsby-browser.js&lt;/code&gt; option in this article.&lt;/p&gt;&lt;p&gt;Create a &lt;code class=&quot;language-text&quot;&gt;gatsby-browser.js&lt;/code&gt; file in your project root.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;touch&lt;/span&gt; gatsby-browser.js&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now open the &lt;code class=&quot;language-text&quot;&gt;gatsby-browser.js&lt;/code&gt; file you just created and simply import the pre-generated Tailwind CSS file to finish the setup.&lt;/p&gt;&lt;div class=&quot;gatsby-code-title gatsby-remark-code-title&quot;&gt;gatsby-browser.js&lt;/div&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;./src/styles/main.css&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Personally, I usually use a Layout Component as my preferred approach. If you&amp;#x27;re curious you can learn more about &lt;a href=&quot;https://www.gatsbyjs.org/docs/layout-components/&quot;&gt;using a layout component in Gatsby&lt;/a&gt;. If you go the layout component route you then need to import &lt;code class=&quot;language-text&quot;&gt;src/styles/main.css&lt;/code&gt; into that file only and not the &lt;code class=&quot;language-text&quot;&gt;gatsby-browser.js&lt;/code&gt; configuration file.&lt;/p&gt;&lt;h2 id=&quot;create-the-postcss-configuration&quot;&gt;Create the PostCSS configuration&lt;/h2&gt;&lt;p&gt;One of the strengths of Gatsby is the rich plugin ecosystem that already exists to easily add any extra functionality we may require.&lt;/p&gt;&lt;p&gt;Install the PostCSS plugin for Gatsby.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; --save gatsby-plugin-postcss&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Activate the plugin by adding &lt;code class=&quot;language-text&quot;&gt;&amp;#x27;gatsby-plugin-postcss&amp;#x27;&lt;/code&gt; to the &lt;em&gt;plugins&lt;/em&gt; array within the &lt;code class=&quot;language-text&quot;&gt;gatsby-config.js&lt;/code&gt; file in the project root.&lt;/p&gt;&lt;div class=&quot;gatsby-code-title gatsby-remark-code-title&quot;&gt;gatsby-config.js&lt;/div&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  plugins&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;gatsby-plugin-postcss&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Next we need to create a &lt;code class=&quot;language-text&quot;&gt;postcss.config.js&lt;/code&gt; file in the root of your project.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;touch&lt;/span&gt; postcss.config.js&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And populate the file with our PostCSS configuration.&lt;/p&gt;&lt;div class=&quot;gatsby-code-title gatsby-remark-code-title&quot;&gt;postcss.config.js&lt;/div&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  plugins&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;tailwindcss&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;autoprefixer&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We&amp;#x27;ll be using &lt;a href=&quot;https://tailwindcss.com/docs/installation#using-tailwind-with-postcss&quot;&gt;PostCSS to build our CSS&lt;/a&gt; by first running our application&amp;#x27;s CSS through the Tailwind CLI. This step not only replaces all of the &lt;code class=&quot;language-text&quot;&gt;@tailwind&lt;/code&gt; directives with the &lt;code class=&quot;language-text&quot;&gt;base&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;components&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;utilities&lt;/code&gt; from Tailwind but also applies your own &lt;code class=&quot;language-text&quot;&gt;tailwind.config.js&lt;/code&gt; customisations during the build. I cannot emphasise enough just how powerful the customisation options are and the massive amount of time it can save you. So please do spend time reading up on &lt;a href=&quot;https://tailwindcss.com/docs/configuration&quot;&gt;how to customise Tailwind&lt;/a&gt; to truly make it your own.&lt;/p&gt;&lt;p&gt;Since &lt;a href=&quot;https://tailwindcss.com/docs/browser-support#vendor-prefixes&quot;&gt;Tailwind does not include vendor prefixes&lt;/a&gt;, the output of the Tailwind build step is then sent into &lt;code class=&quot;language-text&quot;&gt;autoprefixer&lt;/code&gt; which will automatically add any vendor prefixes required by our browserlist (or a default set if none is explicitly set in our &lt;code class=&quot;language-text&quot;&gt;package.json&lt;/code&gt;).&lt;/p&gt;&lt;h2 id=&quot;optimise-the-production-build&quot;&gt;Optimise the production build&lt;/h2&gt;&lt;h3 id=&quot;test-the-build&quot;&gt;Test the build&lt;/h3&gt;&lt;p&gt;At this stage we have only imported the framework but aren&amp;#x27;t actually using any of the Tailwind classes in our application.&lt;/p&gt;&lt;p&gt;Replace &lt;code class=&quot;language-text&quot;&gt;src/pages/index.js&lt;/code&gt; with the below code. Notice that we have now made use of a few of Tailwind&amp;#x27;s classes such as &lt;code class=&quot;language-text&quot;&gt;text-5xl&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;mx-auto&lt;/code&gt;.&lt;/p&gt;&lt;div class=&quot;gatsby-code-title gatsby-remark-code-title&quot;&gt;src/pages/index.js&lt;/div&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; React &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;react&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Index&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;container mx-auto&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;main&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;text-center&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;text-5xl font-semibold my-5&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
          Welcome to&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27; &amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;https://gatsbyjs.org&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;text-purple-600&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
            Gatsby.js!
          &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;

        &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;description&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
          Get started by editing&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27; &amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;code&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;font-mono bg-gray-200 p-1 rounded&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
            src/pages/index.js
          &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;code&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; Index&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Lets run a quick production build.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; run build&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once that has completed open the generated CSS found in the newly created &lt;code class=&quot;language-text&quot;&gt;public&lt;/code&gt; directory. This generated CSS file is absolutely massive and contains the entirety of the framework including tons of styles we haven&amp;#x27;t used at all! Tailwind will even warn us that our styles are not being purged during the build process.&lt;/p&gt;&lt;p&gt;&lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1024px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:21.09375%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAYAAACOXx+WAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAs0lEQVQY02WO2Q6CMBBF/Qc0BcEKsrXstAVRFv//q65pRTDx4eTczGSWg8s7+HkPyjtcNEzBYwpO3IKENeyoAQkbYzuqV++cbg2OQW2sOVBWgYkBqZjB5IJMzWBiApcz8u4FLiek7WhqmVq2rPuacyph/S60/BoevyOoJtBsAIkkSCSMnUStWYDEEnasNnRf++9DOxGg+QC/eO6UH67FA0E5mkM665rHe7isN8OWX23Lvn4DMyR1kCqI1HsAAAAASUVORK5CYII=&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;Unoptimised Tailwind build&quot; title=&quot;Unoptimised Tailwind build&quot; src=&quot;/static/7dc3ebf2102be3ccb8f17f554d920de9/2bef9/unoptimised-build.png&quot; srcSet=&quot;/static/7dc3ebf2102be3ccb8f17f554d920de9/6f3f2/unoptimised-build.png 256w,/static/7dc3ebf2102be3ccb8f17f554d920de9/01e7c/unoptimised-build.png 512w,/static/7dc3ebf2102be3ccb8f17f554d920de9/2bef9/unoptimised-build.png 1024w,/static/7dc3ebf2102be3ccb8f17f554d920de9/71c1d/unoptimised-build.png 1536w,/static/7dc3ebf2102be3ccb8f17f554d920de9/701e9/unoptimised-build.png 1780w&quot; sizes=&quot;(max-width: 1024px) 100vw, 1024px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
    &lt;/span&gt;&lt;/p&gt;&lt;p&gt;This is expected behaviour at this point given our settings. It&amp;#x27;s important to note that the development build of Tailwind is large by design ensuring all of the framework&amp;#x27;s classes are available to you while you build the site. You will only ever use a small percentage of these and Tailwind provides a convenient way to trim that down for your production builds.&lt;/p&gt;&lt;h3 id=&quot;update-the-tailwind-configuration&quot;&gt;Update the Tailwind configuration.&lt;/h3&gt;&lt;p&gt;The setup for running &lt;a href=&quot;https://purgecss.com/&quot;&gt;PurgeCSS&lt;/a&gt; used to be a little more involved requiring us to install extra dependencies and adding a regex pattern to our PostCSS configuration. Thankfully this is now a built-in feature as of version 1.4 and requires nothing more than defining the file patterns that may contain our Tailwind classes.&lt;/p&gt;&lt;p&gt;Replace the contents of the &lt;code class=&quot;language-text&quot;&gt;tailwind.config.js&lt;/code&gt; file in your project&amp;#x27;s root to include an array of file patterns.&lt;/p&gt;&lt;div class=&quot;gatsby-code-title gatsby-remark-code-title&quot;&gt;tailwind.config.js&lt;/div&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  purge&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;./src/**/*.js&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  theme&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    extend&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  variants&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  plugins&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now that we have the purge settings in place, Tailwind will look through those files and treeshake any unused styles from the final production build. We have only set Tailwind to look through JavaScript files inside of our &lt;code class=&quot;language-text&quot;&gt;src&lt;/code&gt; directory. Be sure to tailor this to your own requirements such as adding &lt;code class=&quot;language-text&quot;&gt;posts/**/*.mdx&lt;/code&gt; if you start using MDX for your local posts.&lt;/p&gt;&lt;p&gt;If you&amp;#x27;re curious, read more about &lt;a href=&quot;https://tailwindcss.com/docs/controlling-file-size&quot;&gt;how Tailwind controls the file size for production builds&lt;/a&gt;.&lt;/p&gt;&lt;h3 id=&quot;test-the-optimised-production-build&quot;&gt;Test the optimised Production Build&lt;/h3&gt;&lt;p&gt;Now that we have set our purge options, we are ready to run the optimised production build. It will now automatically remove all unused classes from the final build.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; run build&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once that completes open the newly generated production CSS file. You should find it has shrunk down considerably and now only contains the styles we have thus far used in our project. Perfect!&lt;/p&gt;&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;&lt;p&gt;Tailwind CSS truly has had a huge impact on improving my workflow when developing web apps and I cannot imagine starting a new project without first reaching for this framework. Gatsby&amp;#x27;s rich plugin ecosystem makes it super easy to include Tailwind in your next build so be sure to give it a try.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Add Tailwind to a Next.js project]]></title><description><![CDATA[Tailwind CSS is a highly customizable, low-level CSS framework that gives you all of the building blocks you need to build bespoke designs…]]></description><link>https://www.39digits.com/add-tailwind-to-nextjs</link><guid isPermaLink="false">https://www.39digits.com/add-tailwind-to-nextjs</guid><pubDate>Wed, 13 May 2020 12:08:45 GMT</pubDate><content:encoded>&lt;blockquote&gt;&lt;p&gt;Tailwind CSS is a highly customizable, low-level CSS framework that gives you all of the building blocks you need to build bespoke designs without any annoying opinionated styles you have to fight to override.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Tailwind CSS has completely changed the way I develop web apps thanks to the &lt;a href=&quot;https://tailwindcss.com/docs/utility-first&quot;&gt;utility first workflow&lt;/a&gt;. And the recent release of &lt;a href=&quot;https://tailwindcss.com/docs/release-notes/#tailwind-css-v1-4&quot;&gt;v1.4&lt;/a&gt; made it even easier to get a very small production build by including PurgeCSS as a built-in process. If you&amp;#x27;ve been on the fence about trying out this great framework now is the time to jump in.&lt;/p&gt;&lt;h2 id=&quot;create-the-nextjs-app&quot;&gt;Create the Next.js app&lt;/h2&gt;&lt;p&gt;We&amp;#x27;ll start by creating a default Create Next App to keep things simple.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;npx create-next-app cna-tailwind &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; cna-tailwind&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When asked to pick a template simply select &lt;code class=&quot;language-text&quot;&gt;Default starter app&lt;/code&gt; and hit return to complete the installation of your Next app.&lt;/p&gt;&lt;h2 id=&quot;add-tailwind-to-the-project&quot;&gt;Add Tailwind to the project&lt;/h2&gt;&lt;h3 id=&quot;install-the-required-dependencies&quot;&gt;Install the required dependencies&lt;/h3&gt;&lt;p&gt;Install the development dependencies from our project root.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; --save-dev tailwindcss autoprefixer&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Tailwind doesn&amp;#x27;t include any vendor prefixes out of the box and that&amp;#x27;s why we install &lt;strong&gt;autoprefixer&lt;/strong&gt;.&lt;/p&gt;&lt;h3 id=&quot;create-the-tailwind-css&quot;&gt;Create the Tailwind CSS&lt;/h3&gt;&lt;p&gt;To keep things clean, I prefer to have a dedicated styles directory within my Tailwind projects. Create a &lt;code class=&quot;language-text&quot;&gt;styles&lt;/code&gt; directory from your project root (or wherever you like to keep it) and within that create a &lt;code class=&quot;language-text&quot;&gt;main.css&lt;/code&gt; file.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;mkdir&lt;/span&gt; -p styles &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;touch&lt;/span&gt; styles/main.css&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Next we add the &lt;code class=&quot;language-text&quot;&gt;@tailwind&lt;/code&gt; directives inside &lt;code class=&quot;language-text&quot;&gt;main.css&lt;/code&gt;. These directives will be replaced with the actual Tailwind CSS during the build.&lt;/p&gt;&lt;div class=&quot;gatsby-code-title gatsby-remark-code-title&quot;&gt;styles/main.css&lt;/div&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@tailwind&lt;/span&gt; base&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;/* Your own custom base styles */&lt;/span&gt;

&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@tailwind&lt;/span&gt; components&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;/* Your own custom component styles */&lt;/span&gt;

&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@tailwind&lt;/span&gt; utilities&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;/* Your own custom utilities */&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This &lt;code class=&quot;language-text&quot;&gt;main.css&lt;/code&gt; file contains all of our project&amp;#x27;s pre-generated CSS including any of our extra custom CSS. The beauty and strength of using Tailwind is that you won&amp;#x27;t be required to write much extra CSS though.&lt;/p&gt;&lt;h3 id=&quot;generate-the-tailwind-configuration&quot;&gt;Generate the Tailwind configuration&lt;/h3&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;./node_modules/.bin/tailwindcss init&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;An empty &lt;code class=&quot;language-text&quot;&gt;tailwind.config.js&lt;/code&gt; file is generated within the project root. This config file allows us to dramatically customise Tailwind and is one of the single biggest strengths of this excellent framework. To read more on how you can configure Tailwind check the &lt;a href=&quot;https://tailwindcss.com/docs/configuration&quot;&gt;official configuration documentation&lt;/a&gt;.&lt;/p&gt;&lt;h3 id=&quot;import-the-css-into-our-app&quot;&gt;Import the CSS into our App&lt;/h3&gt;&lt;p&gt;Tailwind will generate a global CSS file for our App. This will require a different setup within Next.js when compared to a component level solution like CSS-in-JS or styled-components. We therefore need to ensure we include &lt;code class=&quot;language-text&quot;&gt;main.css&lt;/code&gt; in the correct way.&lt;/p&gt;&lt;p&gt;Next.js will throw an error at build time if we try to import the global Tailwind CSS file anywhere other than within &lt;code class=&quot;language-text&quot;&gt;pages/_app.js&lt;/code&gt;.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Global CSS cannot be imported from files other than your Custom App. Please move all global CSS imports to pages/_app.js.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;First step is to create &lt;code class=&quot;language-text&quot;&gt;pages/_app.js&lt;/code&gt;.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;touch&lt;/span&gt; pages/_app.js&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We aren&amp;#x27;t doing anything special with the Custom App other than importing Tailwind so the default code will be fine.&lt;/p&gt;&lt;div class=&quot;gatsby-code-title gatsby-remark-code-title&quot;&gt;pages/_app.js&lt;/div&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;../styles/main.css&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;MyApp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Component&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pageProps &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Component&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token spread&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;pageProps&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; MyApp&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you&amp;#x27;re curious you can read more about the &lt;a href=&quot;https://nextjs.org/docs/advanced-features/custom-app&quot;&gt;Custom App component&lt;/a&gt; in the official docs.&lt;/p&gt;&lt;h2 id=&quot;create-the-postcss-configuration&quot;&gt;Create the PostCSS configuration&lt;/h2&gt;&lt;p&gt;Next.js will detect any custom &lt;code class=&quot;language-text&quot;&gt;postcss.config.js&lt;/code&gt; file in our project&amp;#x27;s root and use it as part of the build process. This is super convenient as it means we won&amp;#x27;t need to make any build changes beyond simply creating and populating the config file.&lt;/p&gt;&lt;p&gt;To start, create a &lt;code class=&quot;language-text&quot;&gt;postcss.config.js&lt;/code&gt; file in the root of your project.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;touch&lt;/span&gt; postcss.config.js&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Populate the config with the following setup.&lt;/p&gt;&lt;div class=&quot;gatsby-code-title gatsby-remark-code-title&quot;&gt;postcss.config.js&lt;/div&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  plugins&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;tailwindcss&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;autoprefixer&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We&amp;#x27;ll be using &lt;a href=&quot;https://tailwindcss.com/docs/installation#using-tailwind-with-postcss&quot;&gt;PostCSS to build our CSS&lt;/a&gt; by first running our application&amp;#x27;s CSS through the Tailwind CLI. This step not only replaces all of the &lt;code class=&quot;language-text&quot;&gt;@tailwind&lt;/code&gt; directives with the &lt;code class=&quot;language-text&quot;&gt;base&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;components&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;utilities&lt;/code&gt; from Tailwind but also applies your own &lt;code class=&quot;language-text&quot;&gt;tailwind.config.js&lt;/code&gt; customisations during the build. I cannot emphasise enough just how powerful the customisation options are and the massive amount of time it can save you. So please do spend time reading up on &lt;a href=&quot;https://tailwindcss.com/docs/configuration&quot;&gt;how to customise Tailwind&lt;/a&gt; to truly make it your own.&lt;/p&gt;&lt;p&gt;Since &lt;a href=&quot;https://tailwindcss.com/docs/browser-support#vendor-prefixes&quot;&gt;Tailwind does not include vendor prefixes&lt;/a&gt;, the output of the Tailwind build step is then sent into &lt;code class=&quot;language-text&quot;&gt;autoprefixer&lt;/code&gt; which will automatically add any vendor prefixes required by our browserlist (or a default set if none is explicitly set in our &lt;code class=&quot;language-text&quot;&gt;package.json&lt;/code&gt;).&lt;/p&gt;&lt;h2 id=&quot;optimise-the-production-build&quot;&gt;Optimise the production build&lt;/h2&gt;&lt;h3 id=&quot;test-the-build&quot;&gt;Test the build&lt;/h3&gt;&lt;p&gt;We aren&amp;#x27;t actually using Tailwind in our application yet so replace &lt;code class=&quot;language-text&quot;&gt;pages/index.js&lt;/code&gt; with a simplified version of the index page. Notice that we have now made use of a few of Tailwind&amp;#x27;s classes such as &lt;code class=&quot;language-text&quot;&gt;text-5xl&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;mx-auto&lt;/code&gt;.&lt;/p&gt;&lt;div class=&quot;gatsby-code-title gatsby-remark-code-title&quot;&gt;pages/index.js&lt;/div&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; Head &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;next/head&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Home&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;container mx-auto&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Head&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;Create Next App&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;icon&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;/favicon.ico&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Head&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;

      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;main&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;text-center&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;text-5xl font-semibold my-5&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
          Welcome to&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27; &amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;https://nextjs.org&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;text-blue-600&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
            Next.js!
          &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;

        &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;description&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
          Get started by editing&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27; &amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;code&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;font-mono bg-gray-200 p-1 rounded&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
            pages/index.js
          &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;code&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Lets run a quick production build of our Next.js app.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; run build&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once that has completed, open the generated CSS (it will be in the &lt;code class=&quot;language-text&quot;&gt;.next/static/css&lt;/code&gt; directory) and look at the styles that Tailwind has generated. That CSS file is absolutely massive and contains the entirety of the framework including tons of styles we haven&amp;#x27;t used at all!&lt;/p&gt;&lt;p&gt;&lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1024px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:48.04687499999999%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAABYlAAAWJQFJUiTwAAABOUlEQVQoz31S2ZKCQAz0H1hREAWGQznkEmYUxH3Y/f9f6q1EsYDCfejqqUkmnfRktU8kGKnExq+geyW+3BxrUTCGM/ESxjmElRk2MMIaZlhjd5Iwj/UkYf6ABAfMY1xw61fQnIwDxNqso+FMgiK7wwgucLMWQd1D9xYK+kXPyWP1ccJQkOzYx4rjm6CElTTLHZKql/esbic3fjjueGC6t2L57kpzPngYlN8IygfC6slU1DpJWJHCPlLs6+7YMFPs06e9C8bNDxL1C2LCIbk+xyL4FbMuCraFBOnu318mVffcsbqdPrubrIoooIkcOo0cKazJYxLxq+UOecRIQuR3OOmNvXTPLUTWMZz8DpuQdTikLeyihxErhv7qdlKQdtAILzjE17d3Fu9jw8z+vc5mpGBEEtvXri6N/AdOmR+5Fub8dgAAAABJRU5ErkJggg==&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;Unoptimised Tailwind build&quot; title=&quot;Unoptimised Tailwind build&quot; src=&quot;/static/81500d7b7dde743b1a6459efc68ca3a0/2bef9/unoptimised-build.png&quot; srcSet=&quot;/static/81500d7b7dde743b1a6459efc68ca3a0/6f3f2/unoptimised-build.png 256w,/static/81500d7b7dde743b1a6459efc68ca3a0/01e7c/unoptimised-build.png 512w,/static/81500d7b7dde743b1a6459efc68ca3a0/2bef9/unoptimised-build.png 1024w,/static/81500d7b7dde743b1a6459efc68ca3a0/71c1d/unoptimised-build.png 1536w,/static/81500d7b7dde743b1a6459efc68ca3a0/07d37/unoptimised-build.png 1790w&quot; sizes=&quot;(max-width: 1024px) 100vw, 1024px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
    &lt;/span&gt;&lt;/p&gt;&lt;p&gt;This is expected behaviour at this point given our settings though. It&amp;#x27;s important to note that the development build of Tailwind is large by design ensuring all of the framework&amp;#x27;s classes are available to you while you build the site. You will only ever use a small percentage of these and Tailwind provides a convenient way to trim that down for your production builds.&lt;/p&gt;&lt;h3 id=&quot;update-the-tailwind-configuration&quot;&gt;Update the Tailwind configuration.&lt;/h3&gt;&lt;p&gt;The setup for running &lt;a href=&quot;https://purgecss.com/&quot;&gt;PurgeCSS&lt;/a&gt; used to be a little more involved requiring us to install extra dependencies and adding a regex pattern to our PostCSS configuration. Thankfully this is now a built-in feature as of version 1.4 and requires nothing more than defining the file patterns that may contain our Tailwind classes.&lt;/p&gt;&lt;p&gt;Replace the contents of the &lt;code class=&quot;language-text&quot;&gt;tailwind.config.js&lt;/code&gt; file in your project&amp;#x27;s root to include an array of file patterns.&lt;/p&gt;&lt;div class=&quot;gatsby-code-title gatsby-remark-code-title&quot;&gt;tailwind.config.js&lt;/div&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  purge&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;./pages/**/*.js&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  theme&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    extend&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  variants&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  plugins&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now that we have the purge settings in place, Tailwind will look through those files and treeshake any unused styles from the final production build. We only need to look through JavaScript files within our &lt;code class=&quot;language-text&quot;&gt;pages&lt;/code&gt; directory in the default app but be sure to include any other locations where your app may be using Tailwind (e.g. &lt;code class=&quot;language-text&quot;&gt;./components/**/*.js&lt;/code&gt;) to ensure no classes are purged by accident.&lt;/p&gt;&lt;p&gt;If you&amp;#x27;re curious, read more about &lt;a href=&quot;https://tailwindcss.com/docs/controlling-file-size&quot;&gt;how Tailwind controls the file size for production builds&lt;/a&gt;.&lt;/p&gt;&lt;h3 id=&quot;test-the-optimised-production-build&quot;&gt;Test the optimised Production Build&lt;/h3&gt;&lt;p&gt;Now that we have set our purge options, we are ready to run the optimised production build. It will now automatically remove all unused classes from the final build.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; run build&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1024px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:45.70312499999999%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAABYlAAAWJQFJUiTwAAABKElEQVQoz21S2W6DQBDjG5qmnOEMdzgCLBACqqL+/ze5mqGrhpAHa3ZXxjP2oFhZDwkzFdCjFl/BFZ9ehaNfM+SZqsTBLTfvkqvY2QgtbBl62MKIBdRzsyHJj6gZ86IOVtrvGrOgk99gxB13lHglSUGvmEADqGGDSCzQ43YvSAS/nHky7dzwdK/2JGhCEqOzGl5xDOr3glHzjaR7IGkfCKoZZtJzA4LJtWOrXnmHmYhNk7cZZuKHQaIkLnOU2RIoL3Ji5yMOUuCNqEJkytG53DgfOmthg4+nTAl0N6JuXZhb/tsk0c1S/oRklu5lgl+tlZbgVwvs8g6nmnEqJjj1ArOYoGcDtLjbC1rpwGFTNmTHSvq1pj1nyYgFTukAk97yEVostr/Mk/VfbjgDcpkjLCsAAAAASUVORK5CYII=&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;Optimised Tailwind build&quot; title=&quot;Optimised Tailwind build&quot; src=&quot;/static/ad0417570377221fb349a6b2ba27da5e/2bef9/optimised-build.png&quot; srcSet=&quot;/static/ad0417570377221fb349a6b2ba27da5e/6f3f2/optimised-build.png 256w,/static/ad0417570377221fb349a6b2ba27da5e/01e7c/optimised-build.png 512w,/static/ad0417570377221fb349a6b2ba27da5e/2bef9/optimised-build.png 1024w,/static/ad0417570377221fb349a6b2ba27da5e/71c1d/optimised-build.png 1536w,/static/ad0417570377221fb349a6b2ba27da5e/07d37/optimised-build.png 1790w&quot; sizes=&quot;(max-width: 1024px) 100vw, 1024px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
    &lt;/span&gt;&lt;/p&gt;&lt;p&gt;Once that completes open the newly generated production CSS file. You should find it has shrunk down considerably and now only contains the styles we have thus far used in our project. Perfect!&lt;/p&gt;&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;&lt;p&gt;Tailwind CSS truly has had a huge impact on improving my workflow when developing web apps and I cannot imagine starting a new project without first reaching for this framework. Thanks to the helpful conveniences built into Next.js and the advancements in Tailwind v1.4 it has never been quicker or easier to give Tailwind a try.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Add Tailwind to Create React App without ejecting]]></title><description><![CDATA[Tailwind CSS is a highly customizable, low-level CSS framework that gives you all of the building blocks you need to build bespoke designs…]]></description><link>https://www.39digits.com/add-tailwind-to-create-react-app-without-ejecting</link><guid isPermaLink="false">https://www.39digits.com/add-tailwind-to-create-react-app-without-ejecting</guid><pubDate>Tue, 05 May 2020 19:53:27 GMT</pubDate><content:encoded>&lt;blockquote&gt;&lt;p&gt;Tailwind CSS is a highly customizable, low-level CSS framework that gives you all of the building blocks you need to build bespoke designs without any annoying opinionated styles you have to fight to override.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Tailwind CSS has completely changed the way I develop web apps thanks to the &lt;a href=&quot;https://tailwindcss.com/docs/utility-first&quot;&gt;utility first workflow&lt;/a&gt;. And with version 1.4 now including PurgeCSS as a built-in process it has never been easier to get a clean and lean build for your next project.&lt;/p&gt;&lt;p&gt;Best of all, you don&amp;#x27;t need to eject a Create React App to get the full benefits of adding Tailwind to your project. In fact, it&amp;#x27;s as simple as installing &lt;a href=&quot;https://www.tailwindcss.com&quot;&gt;Tailwind CSS&lt;/a&gt;, creating a &lt;a href=&quot;https://postcss.org/&quot;&gt;PostCSS&lt;/a&gt; config and making a few tweaks to your npm scripts.&lt;/p&gt;&lt;h2 id=&quot;create-the-react-app&quot;&gt;Create the React app&lt;/h2&gt;&lt;p&gt;We&amp;#x27;ll start with a default Create React App to keep things simple.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;npx create-react-app cra-tailwind &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; cra-tailwind&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&quot;default-project-clean-up&quot;&gt;Default project clean-up&lt;/h3&gt;&lt;p&gt;Now that we&amp;#x27;ve created our default React app lets quickly get some housekeeping out of the way before we dive into the real topic of this article.&lt;/p&gt;&lt;p&gt;Since all of our CSS ends up in a single file we can delete the provided CSS files.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;rm&lt;/span&gt; src/index.css src/App.css&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;src/index.js&lt;/code&gt; entrypoint references &lt;code class=&quot;language-text&quot;&gt;index.css&lt;/code&gt;. Open that file up and remove the import.&lt;/p&gt;&lt;div class=&quot;gatsby-code-title gatsby-remark-code-title&quot;&gt;src/index.js&lt;/div&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Remove this line&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;./index.css&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In a similar way, &lt;code class=&quot;language-text&quot;&gt;src/App.js&lt;/code&gt; is still importing &lt;code class=&quot;language-text&quot;&gt;App.css&lt;/code&gt;. Remove this reference.&lt;/p&gt;&lt;div class=&quot;gatsby-code-title gatsby-remark-code-title&quot;&gt;src/App.js&lt;/div&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Remove this line...&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;./App.css&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now that we have that out of the way its time for the fun stuff.&lt;/p&gt;&lt;h2 id=&quot;add-tailwind-to-the-project&quot;&gt;Add Tailwind to the project&lt;/h2&gt;&lt;h3 id=&quot;install-the-required-dependencies&quot;&gt;Install the required dependencies&lt;/h3&gt;&lt;p&gt;Install the development dependencies from our project root.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; --save-dev tailwindcss postcss-cli autoprefixer&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Briefly, &lt;strong&gt;tailwindcss&lt;/strong&gt; is the framework itself, &lt;strong&gt;postcss-cli&lt;/strong&gt; is used to build our generated CSS, and &lt;strong&gt;autoprefixer&lt;/strong&gt; is used to add all required vendor prefixes in our final built CSS.&lt;/p&gt;&lt;h3 id=&quot;create-the-tailwind-css&quot;&gt;Create the Tailwind CSS&lt;/h3&gt;&lt;p&gt;To keep things clean, I prefer to have a dedicated styles directory containing a &lt;code class=&quot;language-text&quot;&gt;tailwind.css&lt;/code&gt; file. It is also the directory within which Tailwind will generate the final build.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;mkdir&lt;/span&gt; -p src/styles &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;touch&lt;/span&gt; src/styles/tailwind.css&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now add the &lt;code class=&quot;language-text&quot;&gt;@tailwind&lt;/code&gt; directives for base, components and utilities to &lt;code class=&quot;language-text&quot;&gt;src/styles/tailwind.css&lt;/code&gt;.&lt;/p&gt;&lt;div class=&quot;gatsby-code-title gatsby-remark-code-title&quot;&gt;src/styles/tailwind.css&lt;/div&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@tailwind&lt;/span&gt; base&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;/* Your own custom base styles */&lt;/span&gt;

&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@tailwind&lt;/span&gt; components&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;/* Your own custom component styles */&lt;/span&gt;

&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@tailwind&lt;/span&gt; utilities&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;/* Your own custom utilities */&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This &lt;code class=&quot;language-text&quot;&gt;tailwind.css&lt;/code&gt; file constitutes our project&amp;#x27;s pre-generated CSS and is where we will also write any of our extra custom CSS.&lt;/p&gt;&lt;h3 id=&quot;generate-the-tailwind-configuration&quot;&gt;Generate the Tailwind configuration&lt;/h3&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;./node_modules/.bin/tailwindcss init&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will create an empty &lt;code class=&quot;language-text&quot;&gt;tailwind.config.js&lt;/code&gt; file in your project root that can be used to dramatically alter the final build.&lt;/p&gt;&lt;h4 id=&quot;a-brief-word-about-tailwind-customisation&quot;&gt;A brief word about Tailwind customisation&lt;/h4&gt;&lt;p&gt;Running the Tailwind build process will effectively replace &lt;code class=&quot;language-text&quot;&gt;@tailwind base;&lt;/code&gt; with all of the framework&amp;#x27;s &lt;code class=&quot;language-text&quot;&gt;base&lt;/code&gt; classes and do the same for &lt;code class=&quot;language-text&quot;&gt;components&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;utilities&lt;/code&gt;. This generated output will be saved in a separate file. But the build process can do way more than just inject those styles if you take the time to dive into the customisation options.&lt;/p&gt;&lt;p&gt;The truly powerful feature of this framework is the ability to fully customise the variables of the build which can dramatically change the generated CSS to meet your exact needs. Do you need new &lt;a href=&quot;https://tailwindcss.com/docs/breakpoints&quot;&gt;default breakpoints&lt;/a&gt;? Add it to the config and Tailwind will automatically generate all related classes. Need to &lt;a href=&quot;https://tailwindcss.com/docs/customizing-colors&quot;&gt;completely override some of the colours in your build&lt;/a&gt;? Add it to the config and Tailwind will automatically generate all related classes.&lt;/p&gt;&lt;p&gt;I cannot emphasise enough just how powerful the customisation options are and the massive amount of time it can save you. So please do spend time reading up on &lt;a href=&quot;https://tailwindcss.com/docs/configuration/&quot;&gt;how to customise Tailwind&lt;/a&gt;.&lt;/p&gt;&lt;h2 id=&quot;create-the-postcss-config&quot;&gt;Create the PostCSS config&lt;/h2&gt;&lt;p&gt;Start by creating the &lt;code class=&quot;language-text&quot;&gt;postcss.config.js&lt;/code&gt; configuration file in the project root.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;touch&lt;/span&gt; postcss.config.js&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Populate it with&lt;/p&gt;&lt;div class=&quot;gatsby-code-title gatsby-remark-code-title&quot;&gt;postcss.config.js&lt;/div&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  plugins&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;tailwindcss&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;autoprefixer&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When we run PostCSS through our build scripts it will first send our custom CSS (&lt;code class=&quot;language-text&quot;&gt;/src/styles/tailwind.css&lt;/code&gt;) through the Tailwind CLI to generate all of the classes required after applying any configuration customisations.&lt;/p&gt;&lt;p&gt;Once completed, PostCSS will then proceed to run &lt;code class=&quot;language-text&quot;&gt;autoprefixer&lt;/code&gt; on the output to automatically add any vendor prefixes to our CSS based upon our browserlist. Tailwind doesn&amp;#x27;t come with any vendor prefixes and this is one of the main reason we use PostCSS to generate our Tailwind build instead of relying solely on the Tailwind CLI.&lt;/p&gt;&lt;h2 id=&quot;create-build-scripts&quot;&gt;Create Build Scripts&lt;/h2&gt;&lt;p&gt;During a build, Create React App doesn&amp;#x27;t recognise the &lt;code class=&quot;language-text&quot;&gt;postcss.config.js&lt;/code&gt; from our project root so we have to change our npm scripts to cater for this.&lt;/p&gt;&lt;h3 id=&quot;install-dependency-for-cross-platform-environment-variables&quot;&gt;Install dependency for cross-platform environment variables&lt;/h3&gt;&lt;p&gt;This is potentially optional based on your use case but I&amp;#x27;m including it here to be kind to Windows users. The &lt;strong&gt;cross-env&lt;/strong&gt; package allows us to set environment variables in our npm scripts that works across platforms.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; --save-dev cross-env&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&quot;option-1-generate-once-at-start-of-build-process&quot;&gt;Option 1: Generate once at start of build process&lt;/h3&gt;&lt;p&gt;The best part about using Tailwind is that you&amp;#x27;ll likely not write any actual CSS once you have it setup in your project. You apply the already generated Tailwind classes to your HTML and JSX. So there&amp;#x27;s little reason to watch the CSS files and instead rely on Create React App watching the JS, JSX and other relevant files during development and reloading on those changes only.&lt;/p&gt;&lt;p&gt;Inside &lt;code class=&quot;language-text&quot;&gt;package.json&lt;/code&gt; change the scripts to include the following.&lt;/p&gt;&lt;div class=&quot;gatsby-code-title gatsby-remark-code-title&quot;&gt;package.json&lt;/div&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token property&quot;&gt;&amp;quot;scripts&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&amp;quot;build:css&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;postcss src/styles/tailwind.css -o src/styles/main.css&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&amp;quot;prestart&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;npm run build:css&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&amp;quot;start&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;cross-env BROWSER=none react-scripts start&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&amp;quot;prebuild&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;cross-env NODE_ENV=production npm run build:css&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&amp;quot;build&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;react-scripts build&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&amp;quot;test&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;react-scripts test&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&amp;quot;eject&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;react-scripts eject&amp;quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;build:css&lt;/code&gt; script runs PostCSS against &lt;code class=&quot;language-text&quot;&gt;src/styles/tailwind.css&lt;/code&gt; and applies any plugins we have setup in &lt;code class=&quot;language-text&quot;&gt;postcss.config.js&lt;/code&gt;. The output is then saved in &lt;code class=&quot;language-text&quot;&gt;src/styles/main.css&lt;/code&gt;. It is this generated file that we import into our application.&lt;/p&gt;&lt;p&gt;We are taking advantage of npm&amp;#x27;s &lt;code class=&quot;language-text&quot;&gt;pre&lt;/code&gt; hooks to kick off a sequential build of the Tailwind generated CSS.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Production Build&lt;/strong&gt;: If we run &lt;code class=&quot;language-text&quot;&gt;npm run build&lt;/code&gt; it will first run the command(s) from &lt;code class=&quot;language-text&quot;&gt;prebuild&lt;/code&gt;. Notice that we&amp;#x27;re setting the &lt;code class=&quot;language-text&quot;&gt;NODE_ENV&lt;/code&gt; variable on the &lt;code class=&quot;language-text&quot;&gt;prebuild&lt;/code&gt; to ensure Tailwind runs production optimisations.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Development Build&lt;/strong&gt;: When developing locally we run &lt;code class=&quot;language-text&quot;&gt;npm run start&lt;/code&gt; which will first run &lt;code class=&quot;language-text&quot;&gt;prestart&lt;/code&gt;. We do not set the environment to production here as we want access to the entire Tailwind framework. Furthermore, I don&amp;#x27;t like that Create React App automatically opens my default browser so I usually set &lt;code class=&quot;language-text&quot;&gt;BROWSER=none&lt;/code&gt; ahead of running &lt;code class=&quot;language-text&quot;&gt;react-scripts start&lt;/code&gt;. I run &lt;a href=&quot;https://www.choosyosx.com/&quot;&gt;Choosy&lt;/a&gt; and when the development server is ready I simply cmd-click on the link to select the browser within which I wish to test the site.&lt;/li&gt;&lt;/ul&gt;&lt;h3 id=&quot;option-2-watch-css-files-for-changes-do-not-open-default-browser&quot;&gt;Option 2: Watch CSS files for changes (do not open default browser)&lt;/h3&gt;&lt;p&gt;We first need to install &lt;code class=&quot;language-text&quot;&gt;npm-run-all&lt;/code&gt; to provide a cross-platform method of running multiple npm scripts in parallel. Windows, for example, does not support &lt;code class=&quot;language-text&quot;&gt;&amp;amp;&lt;/code&gt; when trying to chain npm run commands together.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; --save-dev npm-run-all&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We create a specific &lt;code class=&quot;language-text&quot;&gt;watch:css&lt;/code&gt; command that will constantly watch for any changes to the &lt;code class=&quot;language-text&quot;&gt;tailwind.css&lt;/code&gt; file and regenerate the &lt;code class=&quot;language-text&quot;&gt;main.css&lt;/code&gt; file upon save. These will in turn trigger Create React App to refresh our browser to show the new changes. It&amp;#x27;s a convenient workflow and works well but Tailwind releases us from having to do too many changes to the actual CSS so having a watcher running feels a bit wasteful.&lt;/p&gt;&lt;div class=&quot;gatsby-code-title gatsby-remark-code-title&quot;&gt;package.json&lt;/div&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token property&quot;&gt;&amp;quot;scripts&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&amp;quot;start:react&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;cross-env BROWSER=none react-scripts start&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&amp;quot;start&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;npm-run-all -p watch:css start:react&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&amp;quot;watch:css&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;postcss -w src/styles/tailwind.css -o src/styles/main.css&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&amp;quot;build:css&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;postcss src/styles/tailwind.css -o src/styles/main.css&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&amp;quot;prebuild&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;cross-env NODE_ENV=production npm run build:css&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&amp;quot;build&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;react-scripts build&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&amp;quot;test&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;react-scripts test&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&amp;quot;eject&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;react-scripts eject&amp;quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As with Option 1, I have it set so that the default browser does not automatically open when running the development server.&lt;/p&gt;&lt;h3 id=&quot;option-3-watch-css-files-for-changes-automatically-open-default-browser&quot;&gt;Option 3: Watch CSS files for changes (automatically open default browser)&lt;/h3&gt;&lt;p&gt;There is another reason I don&amp;#x27;t like the default browser to automatically open on a development build. It relates to a race condition if Tailwind hasn&amp;#x27;t completed compiling the css ahead of the page loading. The page will open but it will be blank and React will not load forcing you to have to manually refresh the page.&lt;/p&gt;&lt;p&gt;If you wish to still maintain the automatic opening of your default browser when developing the site you will either need to refresh the page after the browser loads or add a short delay ahead of &lt;code class=&quot;language-text&quot;&gt;react-scripts start&lt;/code&gt;.&lt;/p&gt;&lt;div class=&quot;gatsby-code-title gatsby-remark-code-title&quot;&gt;package.json&lt;/div&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token property&quot;&gt;&amp;quot;scripts&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&amp;quot;start:react&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;sleep 3 &amp;amp;&amp;amp; react-scripts start&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&amp;quot;start&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;npm-run-all -p watch:css start:react&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&amp;quot;watch:css&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;postcss -w src/styles/tailwind.css -o src/styles/main.css&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&amp;quot;build:css&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;postcss src/styles/tailwind.css -o src/styles/main.css&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&amp;quot;prebuild&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;cross-env NODE_ENV=production npm run build:css&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&amp;quot;build&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;react-scripts build&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&amp;quot;test&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;react-scripts test&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&amp;quot;eject&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;react-scripts eject&amp;quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You&amp;#x27;ll notice the only change is to the &lt;code class=&quot;language-text&quot;&gt;start:react&lt;/code&gt; script where we have added a &lt;code class=&quot;language-text&quot;&gt;sleep 3&lt;/code&gt; delay ahead of running &lt;code class=&quot;language-text&quot;&gt;react-scripts start&lt;/code&gt; and removed the &lt;code class=&quot;language-text&quot;&gt;BROWSER=none&lt;/code&gt; environment variable. Unlike trying to run npm scripts in parallel, &lt;code class=&quot;language-text&quot;&gt;&amp;amp;&amp;amp;&lt;/code&gt; is supported on Windows so no need to create a cascade of scripts for our delay.&lt;/p&gt;&lt;h3 id=&quot;where-to-import-the-generated-css&quot;&gt;Where to import the generated CSS&lt;/h3&gt;&lt;p&gt;You only need to import the generated file once. I usually import it into &lt;code class=&quot;language-text&quot;&gt;src/App.js&lt;/code&gt; in a Create React App but you could also import it into &lt;code class=&quot;language-text&quot;&gt;index.js&lt;/code&gt; or wherever makes sense within your own app. Be sure that it is high enough in the cascade to ensure the styles are available when adding Tailwind classes to your JSX components.&lt;/p&gt;&lt;h2 id=&quot;test-the-build&quot;&gt;Test the build&lt;/h2&gt;&lt;p&gt;I&amp;#x27;ve tried to replicate the default Create React App homepage using Tailwind classes with a default configuration.&lt;/p&gt;&lt;p&gt;Replace &lt;code class=&quot;language-text&quot;&gt;src/App.js&lt;/code&gt; with the below code.&lt;/p&gt;&lt;div class=&quot;gatsby-code-title gatsby-remark-code-title&quot;&gt;src/App.js&lt;/div&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; React &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;react&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; logo &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;./logo.svg&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;./styles/main.css&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;text-center&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;header&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;bg-gray-900 min-h-screen flex flex-col items-center justify-center text-4xl text-white&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt;
          &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;logo&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
          &lt;span class=&quot;token attr-name&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;App-logo h-64 pointer-events-none&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
          &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;logo&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
          Edit &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;code&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;src/App.js&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;code&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt; and save to reload.
        &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt;
          &lt;span class=&quot;token attr-name&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;text-blue-300&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
          &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;https://reactjs.org&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
          &lt;span class=&quot;token attr-name&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;_blank&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
          &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;noopener noreferrer&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
          Learn React
        &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;header&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; App&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We&amp;#x27;ll also want to recreate a few of the custom styles by using &lt;code class=&quot;language-text&quot;&gt;@apply&lt;/code&gt; for Tailwind classes or old-school CSS for the logo animation.&lt;/p&gt;&lt;p&gt;Replace &lt;code class=&quot;language-text&quot;&gt;src/styles/tailwind.css&lt;/code&gt; with the code below.&lt;/p&gt;&lt;div class=&quot;gatsby-code-title gatsby-remark-code-title&quot;&gt;src/styles/tailwind.css&lt;/div&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@tailwind&lt;/span&gt; base&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;/* Your own custom base styles */&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@apply&lt;/span&gt; antialiased&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@tailwind&lt;/span&gt; components&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;/* Your own custom component styles */&lt;/span&gt;
&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@media&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;prefers-reduced-motion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; no-preference&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;.App-logo&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;animation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; App-logo-spin infinite 20s linear&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@keyframes&lt;/span&gt; App-logo-spin&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rotate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0deg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rotate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;360deg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@tailwind&lt;/span&gt; utilities&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;/* Your own custom utilities */&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We can now start the development server to view our placeholder app.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; run start&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once that is ready, open &lt;code class=&quot;language-text&quot;&gt;src/styles/main.css&lt;/code&gt; and look at the styles that Tailwind has generated. That CSS file is absolutely massive with almost 70,000 lines!&lt;/p&gt;&lt;p&gt;The development build of Tailwind is large by design ensuring all of the framework&amp;#x27;s classes are available to you while you build the site. You will only ever use a small percentage of these and Tailwind provides a convenient way to trim that down for your production builds.&lt;/p&gt;&lt;h2 id=&quot;optimise-the-production-build&quot;&gt;Optimise the production build&lt;/h2&gt;&lt;h3 id=&quot;update-the-tailwind-configuration&quot;&gt;Update the Tailwind configuration.&lt;/h3&gt;&lt;p&gt;The setup for running &lt;a href=&quot;https://purgecss.com/&quot;&gt;PurgeCSS&lt;/a&gt; used to be a little more involved requiring installing dependencies and adding a regex pattern to our PostCSS configuration. Thankfully this is now a built-in feature as of version 1.4.0 and requires nothing more than defining the file patterns that may contain our Tailwind classes.&lt;/p&gt;&lt;p&gt;Replace the contents of the &lt;code class=&quot;language-text&quot;&gt;tailwind.config.js&lt;/code&gt; file in your project&amp;#x27;s root.&lt;/p&gt;&lt;div class=&quot;gatsby-code-title gatsby-remark-code-title&quot;&gt;tailwind.config.js&lt;/div&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  purge&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;./src/**/*.html&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;./src/**/*.js&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;./src/**/*.jsx&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  theme&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    extend&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  variants&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  plugins&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now that we have the purge settings in place, Tailwind will look through those files and treeshake any unused styles from the final production build. It was for this reason that we explicitly set &lt;code class=&quot;language-text&quot;&gt;NODE_ENV=production&lt;/code&gt; in our &lt;code class=&quot;language-text&quot;&gt;prebuild&lt;/code&gt; stage.&lt;/p&gt;&lt;p&gt;If you&amp;#x27;re curious, read more about &lt;a href=&quot;https://tailwindcss.com/docs/controlling-file-size&quot;&gt;how Tailwind controls the file size for production builds&lt;/a&gt;.&lt;/p&gt;&lt;h3 id=&quot;test-the-production-build&quot;&gt;Test the Production Build&lt;/h3&gt;&lt;p&gt;If you&amp;#x27;re still running the development server hit &lt;code class=&quot;language-text&quot;&gt;Ctrl+C&lt;/code&gt; to shut it down.&lt;/p&gt;&lt;p&gt;We&amp;#x27;re now ready to run a production build.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; run build&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once that completes if you open the newly generated &lt;code class=&quot;language-text&quot;&gt;src/styles/main.css&lt;/code&gt; file you should find it has shrunk down to a file containing only the styles we have thus far used in our project.&lt;/p&gt;&lt;h2 id=&quot;add-the-generated-css-file-to-gitignore&quot;&gt;Add the generated CSS file to gitignore&lt;/h2&gt;&lt;p&gt;Be sure to add the generated CSS file (&lt;code class=&quot;language-text&quot;&gt;src/styles/main.css&lt;/code&gt; in our example above) to your &lt;code class=&quot;language-text&quot;&gt;.gitignore&lt;/code&gt;. The generated file&amp;#x27;s contents completely depending on whether we are running a production or development build.&lt;/p&gt;&lt;p&gt;It might feel weird leaving a required file out of version control given we are importing the generated file into our &lt;code class=&quot;language-text&quot;&gt;App.js&lt;/code&gt;. This isn&amp;#x27;t an issue at all since we always generate the file ahead of the production build or running the development server.&lt;/p&gt;&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;&lt;p&gt;Tailwind CSS truly has had a huge impact on improving my workflow when developing web apps. I encourage anybody who is still skeptical of the syntax and adding so many classes to your HTML and JSX to just give it a try. The framework keeps going from strength to strength and I cannot imagine starting a project without first reaching for Tailwind.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[How to switch to Firefox on Android for improved privacy]]></title><description><![CDATA[Google's Chrome is everywhere these days. In the spirit of healthy competition and fuelled by a desire to greatly enhance my privacy while…]]></description><link>https://www.39digits.com/switch-from-chrome-to-firefox-android</link><guid isPermaLink="false">https://www.39digits.com/switch-from-chrome-to-firefox-android</guid><pubDate>Sun, 03 May 2020 19:26:44 GMT</pubDate><content:encoded>&lt;p&gt;Google&amp;#x27;s Chrome is everywhere these days. In the spirit of healthy competition and fuelled by a desire to greatly enhance my privacy while browsing the web, I recently migrated to Mozilla&amp;#x27;s Firefox on both desktop and Android.&lt;/p&gt;&lt;p&gt;This guide covers some tips and tricks you can use to enhance your own privacy and make the jump to Firefox on Android a breeze. I also have a post that you can read if you&amp;#x27;re interested in ways to &lt;a href=&quot;/switch-from-chrome-to-firefox-desktop&quot;&gt;use Firefox to vastly improve your Windows, MacOS or Linux experience&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;&lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1024px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:16.40625%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAIAAAAcOLh5AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAlElEQVQI1z2Nyw7BQBiF+468g42teI++ABbCe0gkhEW7JFWZyWBBm6i5dC7/IYae1TnJ9+Uk+MaRd+RjJ5AhG0DdbMnRf3oKHZkAqL0c8tlILGQwAPbq3CvT6WMViXm17p/SzasAoIMdi+WAT+6u+clN0JlimWIqtAAutt7J8mhu8e1grltZCFsBMGRzxXPFnl5/5DdbIKbZsafZtwAAAABJRU5ErkJggg==&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;Android robot logo in a banner&quot; title=&quot;Android robot logo in a banner&quot; src=&quot;/static/42594f7b161db8dbe3e24bd1f1f9cce8/2bef9/android-robot-logo-banner.png&quot; srcSet=&quot;/static/42594f7b161db8dbe3e24bd1f1f9cce8/6f3f2/android-robot-logo-banner.png 256w,/static/42594f7b161db8dbe3e24bd1f1f9cce8/01e7c/android-robot-logo-banner.png 512w,/static/42594f7b161db8dbe3e24bd1f1f9cce8/2bef9/android-robot-logo-banner.png 1024w,/static/42594f7b161db8dbe3e24bd1f1f9cce8/6fcb6/android-robot-logo-banner.png 1523w&quot; sizes=&quot;(max-width: 1024px) 100vw, 1024px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
    &lt;/span&gt;&lt;/p&gt;&lt;h2 id=&quot;my-workflow-and-requirements&quot;&gt;My workflow and requirements&lt;/h2&gt;&lt;p&gt;I own a Google Pixel and absolutely love how many beautiful conveniences are just a tap or two away in the Google Pixel Launcher.&lt;/p&gt;&lt;p&gt;My usual workflow when researching something is to perform a quick search in the Google bar on the home screen. It&amp;#x27;s so convenient to tap the search bar, type in what you&amp;#x27;re curious about and go. Any links from your Google searches will open in a built-in reader creating a temporary browsing session. If I find information I want to keep for later I&amp;#x27;ll then send the link to open in a new tab within the main version of Chrome. Once there I can easily send the open tab to my desktop instance of Chrome if I needed it available on my MacBook Pro.&lt;/p&gt;&lt;p&gt;I absolutely wanted to keep this quick and convenient workflow. I enjoy being able to do a quick throwaway search without needing to first open the main browser and open a new tab.&lt;/p&gt;&lt;p&gt;Therefore I had 2 important criteria for any possible browser change:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;I need the ability to do quick searches with minimal effort without creating new tabs in my main browser.&lt;/li&gt;&lt;li&gt;I need to be able to quickly share links between my devices.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Lets recreate this workflow using Firefox and improve our privacy at the same time.&lt;/p&gt;&lt;h2 id=&quot;what-to-install-on-android&quot;&gt;What to install on Android&lt;/h2&gt;&lt;p&gt;I have two Firefox browsers installed on my Pixel.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://play.google.com/store/apps/details?id=org.mozilla.focus&quot;&gt;Firefox Focus&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://play.google.com/store/apps/details?id=org.mozilla.firefox&quot;&gt;Firefox&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Firefox Focus will help us meet the quick search requirement. The ephemeral nature of Firefox Focus is emphasised by a trashcan icon visible in the lower right corner. Tapping it immediately erases all tabs, cookies and browsing history meaning a fresh, private session is always just seconds away. As an added bonus, it also automatically blocks a huge range of trackers and adverts while browsing.&lt;/p&gt;&lt;p&gt;In comparison, Firefox is the full featured browser I use for keeping tabs open for longer periods and for browsing sessions where being logged into certain accounts is beneficial. It is also the browser that allows us to have a seamless experience across devices by sharing tabs.&lt;/p&gt;&lt;h2 id=&quot;firefox-focus-as-quick-search&quot;&gt;Firefox Focus as quick search&lt;/h2&gt;&lt;p&gt;The easiest way to make Firefox Focus instantly accessible as your quick search replacement is to move the icon into your main app bar on Android.&lt;/p&gt;&lt;p&gt;&lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1024px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:37.109375%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAAsTAAALEwEAmpwYAAABlUlEQVQoz32RTWsTURiFrwiihLFNaiZtpqMuNGbmTgLiskIrFizVtuBHobQRopC0myadGIR0YXa6K111U3Thyi6Ky+66LIiCaSeQ4B/I5KPBX/DI3Aq2Ci4O55wXzrN5RaPRoO37rBYKXA5dxNAHuGYa6NEIkUEN0zSIxKIMjeiYpklYjzE8EseIx9GjQ2iaRrVapdVq0Ww2EQGwd3zMs8dziHCK6/PvEeI8qbvPmc5uIoSgnJ5nOTmj8oM7KcxYmIHBMJU37xg1r5LL5eh2uwSs38A+C09nCaUzLG7/RFwyyL76wMZuCyFC7L/9xM7algJ+nFlgyrJIOA6f9w4Yv3effD5/FhiUTGZJDcQ57cSVLpzK/2pYv6K8UCjSbrfPAheXToC3pYUtJdJOIq0EUjok7SRW0IO7tJDSVvnGzYTaFIt/Afv9Pg8fzfJkboraty/UDut4nofnBX5EXeXTOuL7YZ2vtR9MToyRffGSXq/3B9jpdFhZXsFJO6yWXVy3hOu6/1epxOvyGrZ1i0plHd/31Zd/AS0lN8f9l2UKAAAAAElFTkSuQmCC&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;Firefox Focus icon on Android screen&quot; title=&quot;Firefox Focus icon on Android screen&quot; src=&quot;/static/129e49914e5c03e5e24962759724eaf8/2bef9/android-firefox-focus.png&quot; srcSet=&quot;/static/129e49914e5c03e5e24962759724eaf8/6f3f2/android-firefox-focus.png 256w,/static/129e49914e5c03e5e24962759724eaf8/01e7c/android-firefox-focus.png 512w,/static/129e49914e5c03e5e24962759724eaf8/2bef9/android-firefox-focus.png 1024w,/static/129e49914e5c03e5e24962759724eaf8/71c1d/android-firefox-focus.png 1536w,/static/129e49914e5c03e5e24962759724eaf8/14747/android-firefox-focus.png 1914w&quot; sizes=&quot;(max-width: 1024px) 100vw, 1024px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
    &lt;/span&gt;&lt;/p&gt;&lt;p&gt;Now instead of tapping on the Google bar for your quick searches you simply tap on the Firefox Focus icon in the middle and you&amp;#x27;re ready to launch a quick search.&lt;/p&gt;&lt;p&gt;Firefox Focus will open with the addressbar in focus and ready for you to type a search. If the browser opens with the last viewed page open simply tap the trashcan button to clear all cookies and you&amp;#x27;re immediately back on the addressbar and ready to go.&lt;/p&gt;&lt;p&gt;&lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1024px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:30.859375%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAIAAABM9SnKAAAACXBIWXMAAAsTAAALEwEAmpwYAAABQElEQVQY00WNS07CUABFuwQYMIM4ZgvuxRUYq69MDGlSSJiYOHEFRgfGOBIGisintMbEoTEB+z7Svm8fr+XnBgwS480dnMm518IYI4QE54xLxqWQ6repEIoxwbhQSnMuuJAx5+k6E7kWUiKIMMYWxhhCqNKUMjlLOCZxwriQaUz514zGCSezZLsrFJyS14Pb99Pu3MwhhP+yMWa1/lapSeeZNtliucryZZYvTbYQUhuzyDcb8cGCvYu3/UvNFMJ/z4SQyWQShC+Dod/rD/uD0TgIR37w8Ngb+ePu07M/DrccBndn1zfnV1P0STDZylEUaa2bzab1m2KxWCgUdlwulyuVyo5LpVK1Wt2x53la6yiKLIQQpbTT6di2XavVXNet1+uO4wAAPM9rNBoAAMdxXNdttVrg+MQ+PGq37ymlCMIfSSD7jIJZ5loAAAAASUVORK5CYII=&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;Firefox Focus trashcan button&quot; title=&quot;Firefox Focus trashcan button&quot; src=&quot;/static/bc04a3d2b9abc0f26a0d028c65e499c1/2bef9/firefox-focus-trashcan.png&quot; srcSet=&quot;/static/bc04a3d2b9abc0f26a0d028c65e499c1/6f3f2/firefox-focus-trashcan.png 256w,/static/bc04a3d2b9abc0f26a0d028c65e499c1/01e7c/firefox-focus-trashcan.png 512w,/static/bc04a3d2b9abc0f26a0d028c65e499c1/2bef9/firefox-focus-trashcan.png 1024w,/static/bc04a3d2b9abc0f26a0d028c65e499c1/71c1d/firefox-focus-trashcan.png 1536w,/static/bc04a3d2b9abc0f26a0d028c65e499c1/8963a/firefox-focus-trashcan.png 1918w&quot; sizes=&quot;(max-width: 1024px) 100vw, 1024px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
    &lt;/span&gt;&lt;/p&gt;&lt;p&gt;Thanks to the tracker blocker you can now feel the fuzzy feeling of a slightly more private Android experience.&lt;/p&gt;&lt;p&gt;But we&amp;#x27;re not done yet - time to setup the main Firefox browser on Android.&lt;/p&gt;&lt;h2 id=&quot;firefox-share-menu&quot;&gt;Firefox Share menu&lt;/h2&gt;&lt;p&gt;We are now ready to use Firefox to solve the requirement of a seamless tab sharing experience across devices.&lt;/p&gt;&lt;p&gt;A Firefox account is required so open the browser and log into your account. If you don&amp;#x27;t have a Firefox account yet head to the Settings from the menu. It may be worth noting that Firefox Focus doesn&amp;#x27;t offer the ability to log into an account at all due to its temporary nature.&lt;/p&gt;&lt;p&gt;My workflow is to usually do a quick Google search within Firefox Focus while trying to find a solution for a development conundrum or researching a specific topic. If I find something I want to keep around on mobile or send to my Macbook Pro to reference I&amp;#x27;ll then tap the Share button in Firefox Focus. This brings up a convenient popup allowing you to quickly choose the desired destination - be it Firefox on Android or to send the page to one of my devices.&lt;/p&gt;&lt;p&gt;&lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1024px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:37.109375%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAAsTAAALEwEAmpwYAAABaUlEQVQoz3WRy07CUBCGeRwT38CFUZCLGxMVFPEJfAf3LL1DdUFpS4LuuCX4DpLQEtRQ44ZSKCAI1HApld+cUUlM8E8mMyfnzDdzZhyqqqJWq4HneQT29iFJCZRKJTwUCpBlBbIsk1cUZX4uFmWUy484PTtHKHSIZDIJTdPAWI5KpQJd1xGJRLDudCGTzaGq6eh0ujCMJvlms0XxYGCCybY/yXPXN1hbdyIej1NTf4DRaBSuDTdS6Qw9Hg5HGI3GC43dzWaAKIjw7/ohCMJioNfrQzabg2EY6PX6ME0TpvnxYyaBJhOLoEyCJGIn8B+Q4+D1bSKfv6fqLIklM7Os6by78XhCYKar4xO4l1chJqTFM/R4vEil0lBfXtFstWl+3e472u03AlmWTXAWM10chbHiWAIf56lDxnL8bjkWiyEYPKAvPz2zInU0GgYto15v0AjYUpj1e31Y9hRc+BLbri0k726hVb87/AKIKNiGCb8nVQAAAABJRU5ErkJggg==&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;Android Firefox share menu&quot; title=&quot;Android Firefox share menu&quot; src=&quot;/static/a48f24cb2e5206a3f8cb50748b18d969/2bef9/android-firefox-share.png&quot; srcSet=&quot;/static/a48f24cb2e5206a3f8cb50748b18d969/6f3f2/android-firefox-share.png 256w,/static/a48f24cb2e5206a3f8cb50748b18d969/01e7c/android-firefox-share.png 512w,/static/a48f24cb2e5206a3f8cb50748b18d969/2bef9/android-firefox-share.png 1024w,/static/a48f24cb2e5206a3f8cb50748b18d969/71c1d/android-firefox-share.png 1536w,/static/a48f24cb2e5206a3f8cb50748b18d969/14747/android-firefox-share.png 1914w&quot; sizes=&quot;(max-width: 1024px) 100vw, 1024px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
    &lt;/span&gt;&lt;/p&gt;&lt;p&gt;Similarly, if you&amp;#x27;re browsing in Firefox and want to send a tab to one of your devices you can quickly do so by opening the three dot menu and hitting the icon that looks like a paper airplane (as also used in the share menu above). This will instantly show a list of your devices linked to your Firefox account making sharing tabs between devices a seamless and easy experience.&lt;/p&gt;&lt;p&gt;&lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1024px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:37.109375%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAIAAACHqfpvAAAACXBIWXMAAAsTAAALEwEAmpwYAAABPklEQVQY003Q3U3DMBQFYMZiB4ZgAd4ZgBV4ZATGKEioihASTYttmtbOj+3rNLHjRM2PbZRIFM77d8/VuSKEIIQEF4JLSukhOWhtrLXtEjun1caAKAQvKGVFUTDGMMaEkKsFY1nKuql0pbUx53Pfth3nkKYcoBKyBAnxoUyyU0pTjBPGsj+8R3siyWe7q+tOKQUAWZbHMdpuMWMcE1oKeF6fXjZAMIneN3Q+cWnG6CSh3cUtO/oQnPPT5KztyHei1GkcptXqNU/ZOE0dF31jCiEQQgvGGB+PxUf0dnO9e7gPIQTvnXN9PzCWNY11zkVRVJbV6Hyfw2gaAfAPJwn/2q7vbsnT42J9CKGutVIlgBqG0fv5l3GYBucmF6SUv3hZWwKAsZXtLgFQWZZLKbTWda3nJY0xWjemyfMc4xn/AH8NeiW4Z5mgAAAAAElFTkSuQmCC&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;Android Firefox share menu&quot; title=&quot;Android Firefox share menu&quot; src=&quot;/static/403573877e027216c03dc294e189fe4a/2bef9/firefox-share-menu.png&quot; srcSet=&quot;/static/403573877e027216c03dc294e189fe4a/6f3f2/firefox-share-menu.png 256w,/static/403573877e027216c03dc294e189fe4a/01e7c/firefox-share-menu.png 512w,/static/403573877e027216c03dc294e189fe4a/2bef9/firefox-share-menu.png 1024w,/static/403573877e027216c03dc294e189fe4a/71c1d/firefox-share-menu.png 1536w,/static/403573877e027216c03dc294e189fe4a/14747/firefox-share-menu.png 1914w&quot; sizes=&quot;(max-width: 1024px) 100vw, 1024px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
    &lt;/span&gt;&lt;/p&gt;&lt;h2 id=&quot;firefox-extensions-for-increased-privacy&quot;&gt;Firefox Extensions for Increased Privacy&lt;/h2&gt;&lt;p&gt;Sending tabs between devices is a hugely important feature for me, but the biggest benefit to using Firefox on Android is that it supports extensions. In a huge contrast Chrome does not support any extensions on mobile. Microsoft&amp;#x27;s Edge is worth a mention as AdBlock Plus is built into the mobile version of their browser.&lt;/p&gt;&lt;p&gt;Inside Firefox, tap on the three dots to bring up the main menu and select &lt;code class=&quot;language-text&quot;&gt;Add-ons&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;For a more private browsing experience install:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;HTTPS Everywhere&lt;/li&gt;&lt;li&gt;uBlock Origin&lt;/li&gt;&lt;li&gt;Privacy Badger&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;If you&amp;#x27;re curious about what those extensions do you can read more in my &lt;a href=&quot;/switch-from-chrome-to-firefox-desktop#recommended-extensions&quot;&gt;Firefox for desktop article&lt;/a&gt;.&lt;/p&gt;&lt;h2 id=&quot;android-settings&quot;&gt;Android settings&lt;/h2&gt;&lt;h3 id=&quot;set-firefox-focus-as-the-default-browser&quot;&gt;Set Firefox Focus as the default browser&lt;/h3&gt;&lt;p&gt;Chrome is still the default browser on Android so its time to change that.&lt;/p&gt;&lt;p&gt;You may be surprised that we don&amp;#x27;t select the main Firefox browser as the default on Android. Instead, by setting Firefox Focus as the default, it allows other apps to use it as the browser to open links in an ephemeral private session.&lt;/p&gt;&lt;p&gt;To change the default browser on Android head to the phone&amp;#x27;s Settings, then &lt;code class=&quot;language-text&quot;&gt;Apps and notifications&lt;/code&gt;. Open the Advanced section and tap on &lt;code class=&quot;language-text&quot;&gt;Default apps&lt;/code&gt; where you will be able to change the browser to Firefox Focus.&lt;/p&gt;&lt;h3 id=&quot;pixel-launcher&quot;&gt;Pixel Launcher&lt;/h3&gt;&lt;p&gt;Now that we have Firefox Focus as the default browser on Android, we are ready to make some enhancements to the Pixel Launcher. Your mileage may vary at this stage depending on the type of Android phone you own and the specific launcher you use.&lt;/p&gt;&lt;p&gt;The Pixel Launcher has a handy news feed when swiping from the left on the home screen and any links opened will usually be in the built-in reader. Opening links in the built-in reader will register this activity against your Google Account if you haven&amp;#x27;t tweaked the privacy settings. For safety we&amp;#x27;ll simply change the Pixel Launcher to use our default browser (Firefox Focus) instead.&lt;/p&gt;&lt;p&gt;To change this open the Google App (either swipe left on home screen or search for the Google app). Tap on &lt;code class=&quot;language-text&quot;&gt;More&lt;/code&gt; at the bottom right of the screen and head into the settings. Under the General tab change &lt;code class=&quot;language-text&quot;&gt;Open web pages in the app&lt;/code&gt; to off.&lt;/p&gt;&lt;p&gt;I&amp;#x27;ve made similar settings changes to other apps such as Flamingo (my Twitter app) to prefer using Firefox Focus as the browser for links.&lt;/p&gt;&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;&lt;p&gt;My switch to Firefox on both desktop and Android has been very successful and I have enjoyed using it the last few months. If you have any tips of your own or ideas on how the settings can be further improved then please leave a comment below.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[How to switch to Firefox on desktop for improved privacy]]></title><description><![CDATA[Google's Chrome is everywhere these days. In the spirit of healthy competition and fuelled by a desire to greatly enhance my privacy while…]]></description><link>https://www.39digits.com/switch-from-chrome-to-firefox-desktop</link><guid isPermaLink="false">https://www.39digits.com/switch-from-chrome-to-firefox-desktop</guid><pubDate>Sat, 02 May 2020 13:34:08 GMT</pubDate><content:encoded>&lt;p&gt;Google&amp;#x27;s Chrome is everywhere these days. In the spirit of healthy competition and fuelled by a desire to greatly enhance my privacy while browsing the web, I recently migrated to Mozilla&amp;#x27;s Firefox on both desktop and Android.&lt;/p&gt;&lt;p&gt;This guide covers some tips and tricks you can use to enhance your own privacy and make the jump to Firefox on Windows, MacOS or Linux a breeze. I also have a post that you can read if you&amp;#x27;re interested in ways to &lt;a href=&quot;https://www.39digits.com/switch-from-chrome-to-firefox-android&quot;&gt;use Firefox to vastly improve your Android experience&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;&lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1024px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:34.375%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAIAAACHqfpvAAAACXBIWXMAAAsTAAALEwEAmpwYAAABmElEQVQY02M4aBzw3z7qr110u4anqKC5grCFnLCFjJCFlKCFjKCFppy1tIC5HFhQXsRCUtBcRcyyz9frQXPk7wVxDMoillHyjh4ydhJCIEXyYJ2qkpY6UlbqUlaX90WnhnsIspkqilrKCFkoiVgeiA3635/wd1rcv0XxDDLCFiKC5uJC5vIilnLCFooiFkK8ZnW2ni+nBC6JbH/Qu+znlbRQFxdJXnNhfrNOe8//DfE/WmP+TI77uyieQU7YAuRUITMZIVM5YXMlEUsBfvNd9j7/52TNqDk2xfHq/325s8oD+dlMNSWs7sSH/a+O/d0W83cKWLMsWJuquLOmpJe8iJWMoKGgoHmPptd/7+TegOXr0qf835Kc4+LFwWHip+rwJzv6T2XMn7ZYqGYlUXtHjaJo87kZ9pvT7TdZq2SICxhqiNhu0Qz55RTxMz52nluogqClAL9ZsaHr/4LYn+XRf9thmm1Vs8NNpsRbLs5y2FbpdaEx4L65UpKYgIG0oJWTiLO9kJMEr7mciIWYgPl2D///+TG/KhCaAaxOkPdjslaTAAAAAElFTkSuQmCC&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;Firefox logo in a banner&quot; title=&quot;Firefox logo in a banner&quot; src=&quot;/static/5cda054ca018a2f574e6a08a533ff251/2bef9/firefox-logo-banner.png&quot; srcSet=&quot;/static/5cda054ca018a2f574e6a08a533ff251/6f3f2/firefox-logo-banner.png 256w,/static/5cda054ca018a2f574e6a08a533ff251/01e7c/firefox-logo-banner.png 512w,/static/5cda054ca018a2f574e6a08a533ff251/2bef9/firefox-logo-banner.png 1024w,/static/5cda054ca018a2f574e6a08a533ff251/71c1d/firefox-logo-banner.png 1536w,/static/5cda054ca018a2f574e6a08a533ff251/a878e/firefox-logo-banner.png 2048w,/static/5cda054ca018a2f574e6a08a533ff251/7e508/firefox-logo-banner.png 3836w&quot; sizes=&quot;(max-width: 1024px) 100vw, 1024px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
    &lt;/span&gt;&lt;/p&gt;&lt;h2 id=&quot;what-to-install-on-desktop&quot;&gt;What to install on desktop&lt;/h2&gt;&lt;p&gt;Start by installing your preference of either normal &lt;a href=&quot;https://www.mozilla.org/en-GB/firefox/new/&quot;&gt;Firefox&lt;/a&gt; or the superb &lt;a href=&quot;https://www.mozilla.org/en-GB/firefox/developer/&quot;&gt;Developer Edition&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;I personally use standard Firefox as my day to day default browser but rely on the Developer Edition when building web apps. The Developer Edition is a few versions ahead of the standard build and includes a few helpful developer settings such as remote debugging turned on by default.&lt;/p&gt;&lt;p&gt;Nothing in this article depends on a specific edition so you are free to choose the one that best meets your needs.&lt;/p&gt;&lt;h2 id=&quot;create-a-firefox-account&quot;&gt;Create a Firefox account&lt;/h2&gt;&lt;p&gt;A Firefox account gives you the ability to sync your Firefox settings, bookmarks &amp;amp; extensions. It also enables sending tabs between desktop and mobile.&lt;/p&gt;&lt;p&gt;To create one simply open Firefox and head to the Preferences and then the Sync section.&lt;/p&gt;&lt;p&gt;&lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1024px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:30.46875%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAYAAADDl76dAAAACXBIWXMAABYlAAAWJQFJUiTwAAABJ0lEQVQY00WQvVLDMBCE/WrUPAOvRUFJzUtQ0KRghhAIk4SMx3Zwfojt+Fd3svMxEsEUJ+3MnnZXG1RVzddXSlGcEFGMEY7HzONhOGNt7/EfJ0OPANpb7CCg6sdxRpQgy3JWqzVlWaFqPZEkG6IoJo4Tb9a2HW3TIr2lWX/TPs04JTn7SHnJDJNcfk2NEJSnkjAMyfNiTLFYLJlOX5nN3pjPP1itPkk3WywQ372zub5l8bDk8V64eW64mnRYvQgeXcJ1xG5/YL8/0LYGl9p9201dNz6hGEVUqQ8dVVhQZw1lLhxqw66SMUxQ1EKSCU0rGGM84UScqOu168y47Du0ip571N1W6VUZLh26vcAdeindY7VezPWXplsa1538P/DCnYwGo9El4Q+0R8id36k/qAAAAABJRU5ErkJggg==&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;Firefox Sync&quot; title=&quot;Firefox Sync&quot; src=&quot;/static/a9a3a803b04dc671dd2b53532e2bcb83/2bef9/firefox-sync.png&quot; srcSet=&quot;/static/a9a3a803b04dc671dd2b53532e2bcb83/6f3f2/firefox-sync.png 256w,/static/a9a3a803b04dc671dd2b53532e2bcb83/01e7c/firefox-sync.png 512w,/static/a9a3a803b04dc671dd2b53532e2bcb83/2bef9/firefox-sync.png 1024w,/static/a9a3a803b04dc671dd2b53532e2bcb83/71c1d/firefox-sync.png 1536w,/static/a9a3a803b04dc671dd2b53532e2bcb83/a878e/firefox-sync.png 2048w&quot; sizes=&quot;(max-width: 1024px) 100vw, 1024px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
    &lt;/span&gt;&lt;/p&gt;&lt;h2 id=&quot;import-data-from-another-browser&quot;&gt;Import data from another browser&lt;/h2&gt;&lt;p&gt;You may wish to import bookmarks or other data over from another browser.&lt;/p&gt;&lt;p&gt;Click on File and then the perfectly named &lt;code class=&quot;language-text&quot;&gt;Import from Another Browser&lt;/code&gt; option. You will be shown a window with a list of browsers on your device and then asked the type of data you wish to import into Firefox.&lt;/p&gt;&lt;p&gt;I keep my browsers pretty lightweight and instead save my bookmarks in &lt;a href=&quot;https://raindrop.io&quot;&gt;Raindrop.io&lt;/a&gt; but your requirements may be different.&lt;/p&gt;&lt;h2 id=&quot;recommended-extensions&quot;&gt;Recommended Extensions&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://addons.mozilla.org/en-GB/firefox/addon/https-everywhere/&quot;&gt;HTTPS Everywhere&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://addons.mozilla.org/en-GB/firefox/addon/ublock-origin/&quot;&gt;uBlock Origin&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://addons.mozilla.org/en-GB/firefox/addon/privacy-badger17/&quot;&gt;Privacy Badger&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://addons.mozilla.org/en-GB/firefox/addon/multi-account-containers/&quot;&gt;Firefox Multi-Account Containers&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;HTTPS Everywhere&lt;/strong&gt; ensures that you browse the web using encryption whenever possible. Even if you type a URL with http this extension will ensure you automatically connect via https instead.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;uBlock Origin&lt;/strong&gt; and &lt;strong&gt;Privacy Badger&lt;/strong&gt; perform similar but complimentary roles in enhancing your privacy across the web.&lt;/p&gt;&lt;p&gt;uBlock Origin is an excellent ad blocker that also works to block trackers using verbose lists of known offenders that are regularly updated. Privacy Badger takes a different stance and learns to block trackers over time. If it finds the same tracker cookies across 3 sites it will automatically start blocking those. Privacy Badger also sends the Do Not Track signal and protects against click tracking on Facebook, Google and Twitter links.&lt;/p&gt;&lt;p&gt;It is for these reasons I recommend using both in tandem to ensure you get the absolute best protection without needing to dive too deep into the weeds of finetuning blockers by hand.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Firefox Multi-Account Containers&lt;/strong&gt; allows you to create special self-contained tabs within the single Firefox instance. It could, for example, be used to create both a Personal and a Work container allowing you to only need one instance of Firefox open and be logged into your work Google account in the Work container and your personal Google account in the Personal container. No more alt-tabbing between browser windows!&lt;/p&gt;&lt;p&gt;The last section of this article explains in detail how to use it to keep our Google account completely segregated from our day to day browsing. If you use Facebook, I recommend replicating it with a dedicated Facebook container to keep their creepy hands off of your data. Or better yet, just delete Facebook.&lt;/p&gt;&lt;h2 id=&quot;firefox-settings&quot;&gt;Firefox settings&lt;/h2&gt;&lt;h3 id=&quot;ensure-firefox-is-a-fully-colour-managed-browser&quot;&gt;Ensure Firefox is a Fully Colour Managed browser&lt;/h3&gt;&lt;p&gt;This isn&amp;#x27;t a privacy setting, but if you&amp;#x27;re switching to Firefox on a wide gamut display such as found on MacBooks, then I recommend checking your colour management settings.&lt;/p&gt;&lt;p&gt;As of Firefox 77 (the current Nightly version) the default setting for color management within the browser has been changed to be fully managed. If you are using a version prior to 77 or wish to check if it is set correctly then follow my blog post about &lt;a href=&quot;https://www.39digits.com/color-management-in-firefox&quot;&gt;Colour Management in Firefox&lt;/a&gt;.&lt;/p&gt;&lt;h3 id=&quot;turn-on-dns-over-https&quot;&gt;Turn on DNS over HTTPS&lt;/h3&gt;&lt;p&gt;I recommend turning on &lt;code class=&quot;language-text&quot;&gt;DNS over HTTPS&lt;/code&gt; which is only set by default for users in the United States.&lt;/p&gt;&lt;p&gt;Start by opening the Preferences and scroll down to the bottom of the General section. You should see a Network Settings area - click on the &lt;code class=&quot;language-text&quot;&gt;Settings...&lt;/code&gt; button to open up a panel.&lt;/p&gt;&lt;p&gt;At the very bottom you will find a checkbox &lt;code class=&quot;language-text&quot;&gt;Enable DNS over HTTPS&lt;/code&gt; which you should tick and then click OK to save the changes.&lt;/p&gt;&lt;p&gt;You can customise which provider to use but I trust the default (Cloudflare) and leave that set as is.&lt;/p&gt;&lt;p&gt;If you want to know more about DNS over HTTPS and the benefits it can provide you can read the &lt;a href=&quot;https://support.mozilla.org/en-US/kb/firefox-dns-over-https&quot;&gt;Mozilla blog article&lt;/a&gt; or &lt;a href=&quot;https://hacks.mozilla.org/2018/05/a-cartoon-intro-to-dns-over-https/&quot;&gt;Lin Clark&amp;#x27;s Cartoon Intro to DNS over HTTPS&lt;/a&gt;&lt;/p&gt;&lt;h3 id=&quot;block-all-third-party-cookies&quot;&gt;Block all third-party Cookies&lt;/h3&gt;&lt;p&gt;Firefox - like most of the popular browsers - does not block all third-party cookies by default. Mozilla takes privacy seriously and Firefox has great defaults which will try protect you from a large swathe of tracking and social media cookies.&lt;/p&gt;&lt;p&gt;Open Preferences and head to the &lt;em&gt;Privacy &amp;amp; Security&lt;/em&gt; section. You should see the current Tracking Protection set to Standard.&lt;/p&gt;&lt;p&gt;Standard and Strict both block:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Social media trackers&lt;/li&gt;&lt;li&gt;Cross-site tracking cookies&lt;/li&gt;&lt;li&gt;Cryptominers&lt;/li&gt;&lt;li&gt;Fingerprinters&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Furthermore, Standard blocks Tracking Content in Private Windows while Strict blocks in all windows.&lt;/p&gt;&lt;p&gt;When you visit a site in Firefox you will see a little shield icon in the toolbar. If you click on it that will show you what the Enhanced Tracking Protection is blocking on that particular site letting you dig into the details.&lt;/p&gt;&lt;p&gt;&lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1024px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:42.1875%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAABYlAAAWJQFJUiTwAAABrklEQVQoz5WRz2sTQRTH948rKHjxKBXBf8KDR496EnOoFw9CDWmEBE1t0yZFENTaNrZEzJrGxmy3m2rSzW662dnZnx/ZiSkIXhz48N7MPL7zvm+0zb1zbj444v7THzxcMXn8xOL5ik254LFREIytGUEo8P05MgyJopgoiubEMSII8IUgkCHaXm/Co3KPZ7UBr7Ysthsj3jUveF/32K3HSC/mf5Y2DSQQAgmQ/YlzBBF922PgBFhTiekGKh9ehliXIYYrOfciBq7E8UNcz0crfDAZD88Y/vzFaDQmjlPSdK6dxAn3tg1uvTa5s25yd8Pids1kudpnuXrCjVKPpdUuSy+6XCsec73YRTu1PU4NgzPLwnVd1XaWZSrGScLhiUWjpdNs6Wztf6X5+RvHF4K+G9GzA75PpKKXY0u0LI6YTqd4nnc1h4VgkiQctfZ526jT2HzDy+IqlXKJL4cHQPrvGea/NpvNlKCU8q/LXLBSqVJaW6NWW+fj7icOWi2aOzvKkapJU9Isu0ITQmAYhmIymZDvfd9XuW3bdDod2u02uq6rR/Nzx3FU3cLNwlHe4W+Fsko/KXD3RAAAAABJRU5ErkJggg==&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;Firefox Enhanced Tracking details&quot; title=&quot;Firefox Enhanced Tracking details&quot; src=&quot;/static/5dc556c12cd3cef2015f477fb1dcba31/2bef9/firefox-protection-details.png&quot; srcSet=&quot;/static/5dc556c12cd3cef2015f477fb1dcba31/6f3f2/firefox-protection-details.png 256w,/static/5dc556c12cd3cef2015f477fb1dcba31/01e7c/firefox-protection-details.png 512w,/static/5dc556c12cd3cef2015f477fb1dcba31/2bef9/firefox-protection-details.png 1024w,/static/5dc556c12cd3cef2015f477fb1dcba31/71c1d/firefox-protection-details.png 1536w,/static/5dc556c12cd3cef2015f477fb1dcba31/a878e/firefox-protection-details.png 2048w,/static/5dc556c12cd3cef2015f477fb1dcba31/922e6/firefox-protection-details.png 2140w&quot; sizes=&quot;(max-width: 1024px) 100vw, 1024px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
    &lt;/span&gt;&lt;/p&gt;&lt;p&gt;If you&amp;#x27;re curious, you can read more on what each of these &lt;a href=&quot;https://support.mozilla.org/en-US/kb/trackers-and-scripts-firefox-blocks-enhanced-track&quot;&gt;enhanced protection options block on the Mozilla blog&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Selecting either Standard or Strict is perfectly fine - especially when coupled with Privacy Badger and uBlock Origin. But I wanted to try take things a bit further by blocking all third-party cookies instead of just cross-site tracking and social media trackers.&lt;/p&gt;&lt;p&gt;To do this we have to use Custom settings. Ensure everything is ticked, Tracking Content is set to &lt;code class=&quot;language-text&quot;&gt;All Windows&lt;/code&gt; and set Cookies to be &lt;code class=&quot;language-text&quot;&gt;In all third-party cookies&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;&lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1024px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:70.3125%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAABYlAAAWJQFJUiTwAAACSklEQVQ4y3WTSU8bQRCF/f//QQ5RpByzoeSYIAIKOyGExcIeG7yMZ9/3mR6bL+o2NiQmh6eq7p5+Xe9NVcd1PSzLpqxqmkbgeT6zmYnjuOR5QV03CNGuIb9p2/k6l+cyrtAxDRPLNBFNQytaRqMxWl/j7u5O5fKBNM2I44QkSSmKkiiKFcqy2iDteGHB1EnwkoaiapEPjCc6g+GIwWCooGkDer0+uj5T5LOZgWGYSsEGoeWEOH5CXgnKStDUDV6Y4oaFqqCq6jWWl57kr4jq+pnksigpyxLRCIU4TlU1lmlh2y6W5ayjadp4XkCS5kTRUv5GhatktSl9mukGvhfge77yUMFdxiiMyLN87ecGoVysIDeCKEO715kYHroVMjF8pqav8vHMRbcCZYcT5GR59XKF0oNlewgGdsnepcHFKONWTxgaCdospjcNmfo1TgZOumDgCKxIMBf/ePicUP6QSTDnpB9w3PN4d2jwanvM652Jim/3Lb5cF2x153y+iHGiila8KPlvwlMt5ODGZv/G4rTvc6YFKn490dj5ec/BjcnBrY+TtLSi+b+HtSJs+T3KVIXXeonmQs9ZKMi87zzQt+d0rQV2LB4Jxfp+R/aTHCUJ+cI4mHOkJfzo+pxPaq5MuDQWG7jQ5zhrwid0ZOcvWyOgKAru/Qc+nSe8+Tbg/YnPh7PwETEfz1O2fiXqfLvfPkoW1M9nOctyRRgEIXVdK8nHo4a9bshuN+D7jc/OlctON2W3V3A0LDkclpyOGuyopq4qirJSUyUr/AMOXh71d7709gAAAABJRU5ErkJggg==&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;Firefox enhanced tracking settings&quot; title=&quot;Firefox enhanced tracking settings&quot; src=&quot;/static/c6738a389f36b49010835720b5b3ff56/2bef9/firefox-enhanced-tracking.png&quot; srcSet=&quot;/static/c6738a389f36b49010835720b5b3ff56/6f3f2/firefox-enhanced-tracking.png 256w,/static/c6738a389f36b49010835720b5b3ff56/01e7c/firefox-enhanced-tracking.png 512w,/static/c6738a389f36b49010835720b5b3ff56/2bef9/firefox-enhanced-tracking.png 1024w,/static/c6738a389f36b49010835720b5b3ff56/71c1d/firefox-enhanced-tracking.png 1536w,/static/c6738a389f36b49010835720b5b3ff56/a878e/firefox-enhanced-tracking.png 2048w,/static/c6738a389f36b49010835720b5b3ff56/97a96/firefox-enhanced-tracking.png 2400w&quot; sizes=&quot;(max-width: 1024px) 100vw, 1024px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
    &lt;/span&gt;&lt;/p&gt;&lt;p&gt;Be advised that restricting third-party cookies may cause issues on some sites. For example, I had to whitelist &lt;code class=&quot;language-text&quot;&gt;codesandbox.io&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;csb.app&lt;/code&gt; to ensure embedded code snippets were still viewable when browsing certain sites but your mileage may vary. You can whitelist sites under &lt;em&gt;Security &amp;amp; Privacy&lt;/em&gt; in the &lt;em&gt;Cookies and Site Data&lt;/em&gt; section by clicking on &lt;code class=&quot;language-text&quot;&gt;Manage Permissions&lt;/code&gt; and adding any domains as required.&lt;/p&gt;&lt;p&gt;If you find the settings aren&amp;#x27;t working for you then don&amp;#x27;t be afraid to change it back to Strict or Standard. Firefox will still give you some tracking protection regardless of the option you set.&lt;/p&gt;&lt;h2 id=&quot;set-firefox-as-your-default-browser&quot;&gt;Set Firefox as your default browser&lt;/h2&gt;&lt;p&gt;Now that you have everything setup and ready to go its time to make Firefox your default browser. You will find a handy &lt;code class=&quot;language-text&quot;&gt;Make Default...&lt;/code&gt; button at the top of the General panel in the Preferences.&lt;/p&gt;&lt;h2 id=&quot;keeping-our-google-account-seperate&quot;&gt;Keeping our Google Account seperate&lt;/h2&gt;&lt;blockquote&gt;&lt;p&gt;The Firefox Multi-Account Containers extension lets you keep parts of your online life separated into colour-coded tabs that preserve your privacy. Cookies are separated by container, allowing you to use the web with multiple identities or accounts simultaneously.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Lets be honest, Google Search has some of the best results out there - I still miss you, Alta Vista! They have also created some truly amazing web apps that have enhanced our lives. Can you imagine still travelling with a paper map thanks to Google Maps? Gmail provides both a slick interface and untouchable spam protection. The convenience of a trip automatically created in Google Travel based on flight and hotel bookings is a delight. I am willing to accept a balance in the ways my personal data is used and it would be naive to assume we could still surf the web in a purely anonymous way while taking advantage of these conveniences.&lt;/p&gt;&lt;p&gt;That doesn&amp;#x27;t mean I want all of my searches and history to be associated with my Google account for them to use to continue building an ad-targeting profile about myself.&lt;/p&gt;&lt;p&gt;Chrome is an absolute beast at collecting data about you and adding it to your Google account. Take this moment to view your own &lt;a href=&quot;https://myactivity.google.com/item&quot;&gt;activity logged against your Google Account&lt;/a&gt;. By default it will log every single Google search you perform and every single website you visit.&lt;/p&gt;&lt;p&gt;You can stop Google searches being logged by pausing your Web &amp;amp; App Activity settings against your Google Account. Furthermore, inside Chrome change your Sync Settings and turn off any items you don&amp;#x27;t want to sync such as History.&lt;/p&gt;&lt;p&gt;That&amp;#x27;s all fine but we&amp;#x27;re busy switching browsers here! And thankfully switching to a browser other than Chrome allows you to be signed into your Google Account and none of your browsing history is synced. Your Google searches, however, will still be synced with your Google account (unless you still pause Web &amp;amp; App Activity).&lt;/p&gt;&lt;p&gt;As such, the rest of this article is purely optional but I wanted to use the Firefox Multi-Account Containers extension and try take our privacy one step further by keeping our Google account completely separated from our daily browsing and general searches.&lt;/p&gt;&lt;p&gt;I don&amp;#x27;t use Facebook but that is another site that would be a perfect use case for a dedicated container. Google provides us with beneficial apps; Facebook is just plain creepy.&lt;/p&gt;&lt;h3 id=&quot;create-a-google-container&quot;&gt;Create a Google Container&lt;/h3&gt;&lt;p&gt;The Multi-Account Containers extension installs with a few blank containers already setup. We&amp;#x27;ll start by pruning this list.&lt;/p&gt;&lt;p&gt;Click on the extension&amp;#x27;s icon in the Firefox toolbar and then on &lt;code class=&quot;language-text&quot;&gt;Edit Containers&lt;/code&gt; at the bottom. You will now see the list of containers and next to each a pencil icon to edit and a trashcan icon to delete. Delete all of the containers you don&amp;#x27;t wish to keep (personally, I purge the entire list). Don&amp;#x27;t worry, there is a default container that you cannot edit or delete so it is safe to clear out the entire list.&lt;/p&gt;&lt;p&gt;Exit Edit Mode and click on the + symbol to add our Google container.&lt;/p&gt;&lt;p&gt;&lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1024px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:45.70312499999999%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAABYlAAAWJQFJUiTwAAABhklEQVQoz22Sy4rbMBiF84p9ob5DF91311WhfYJeltNZFArT0pBhmNqZxLIdybbuUr5i2UNCGcEB2eg/OhdtjDFIqVBqwBiLUmrFUP6P44T3Ae/9ioDWmklrrHVM00TXdlRVjRAtG+cCIcSCeb8ML3A+YJ3HOoe1vhA458tF1d9HOtES05mYMzHHImJTBp0vuN7PIAZIiRwTKWVSSsSYUFLy1J547GqCFeRJEWQoDjfXBNfIIfCzcXzcH6hawfGppRE9w6gLYS0k2+YBO1aEvkEfDVrbVeFscSZaFRrrgci79z2vXv/h9v6Bb19u+Pz1O3e/dvSnE61o2G13HA6CURvUqEq2RaF3F9JQ8ouEGNCto7u35JSJs90QiTHS930ZXiKI5DOkfEZfW56Vzo2VBqdpsU0GlqE5uxk5Z6SUWGu5rDOQSf4/QmNduUVrUxq9NOvWb1fOdV2HVApvDXvl+LSLfNgGfh/NkuFzu9dP5iU8nxmGkaYRHPc1d1XP29uBNzeKH7XmHxils4EIB/5DAAAAAElFTkSuQmCC&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;Create a Google container&quot; title=&quot;Create a Google container&quot; src=&quot;/static/03775e7dc8055dce072704d4f82569fd/2bef9/containers-create-google.png&quot; srcSet=&quot;/static/03775e7dc8055dce072704d4f82569fd/6f3f2/containers-create-google.png 256w,/static/03775e7dc8055dce072704d4f82569fd/01e7c/containers-create-google.png 512w,/static/03775e7dc8055dce072704d4f82569fd/2bef9/containers-create-google.png 1024w,/static/03775e7dc8055dce072704d4f82569fd/71c1d/containers-create-google.png 1536w,/static/03775e7dc8055dce072704d4f82569fd/a878e/containers-create-google.png 2048w,/static/03775e7dc8055dce072704d4f82569fd/aae30/containers-create-google.png 2080w&quot; sizes=&quot;(max-width: 1024px) 100vw, 1024px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
    &lt;/span&gt;&lt;/p&gt;&lt;p&gt;I name it Google, select red for the colour and a pair of snooping glasses as the icon. Click OK to complete the creation.&lt;/p&gt;&lt;h3 id=&quot;assign-google-apps-to-the-container&quot;&gt;Assign Google apps to the container&lt;/h3&gt;&lt;p&gt;Any tab you open will automatically open in the Default container. We want to keep this default container completely disassociated from our Google Account so do not log into your account within any of the normal tabs.&lt;/p&gt;&lt;p&gt;To actually open a tab in the Google container you have to click on the extension icon and click on &amp;quot;Google&amp;quot; (the name of our newly created container). You should now see a new tab with a visible red underline to clearly show it is a tab in a special container. It is within this tab that you want to log into your Google account. Do not do so in your Default container (identified by no underline on the tab).&lt;/p&gt;&lt;p&gt;Once you have logged into your Google Account we are ready to assign the numerous web apps to favour opening in that segregated container. Start by going to a Google App you use often such as mail.google.com or keep.google.com. Once the app loads click on the multi-account extension icon again and you should see a checkbox that tells Firefox you prefer opening it in the assigned container.&lt;/p&gt;&lt;p&gt;&lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1024px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:23.4375%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAYAAABFA8wzAAAACXBIWXMAABYlAAAWJQFJUiTwAAABIElEQVQY0zWQy0rDUBRF89EKBZs+YgvSgR/gzIEIDvwFR4Ig/oGKrWjT1jSv+743d0kibjjs0Vl7n5OMlxmrxZLLiyWrRcb5JOXm+ori+5Vyt8eJFicFt3f3zOcZWXrGLB0znc2YTidM0pTRaMTp6cngSWjfcEbSK/iAVpqyrNnmO6QQKClRUuFDIAJRSTpZUxR7lNLDnnUe6xzFoSDR+QN1sUGrhtgp6qaiblq0MWhjUdogtUZIhZCSWmqEOHLI17x/rKn2O6KzA3jz+UVS588020doXujKJ9Cbv1RraIXAWEvXdYQQBvfeY7XBO4/znhgC3jmqqsIYQ6JkQ3n8IQQHMRBjR4w90CGlGpr2kP8ZzmsbRFkM7fs39EG9tNb8AiaBbZ8Eqh1vAAAAAElFTkSuQmCC&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;Assign a Google app to the container&quot; title=&quot;Assign a Google app to the container&quot; src=&quot;/static/1d04b184fd9de2f5af0a605a1b7d8e1d/2bef9/containers-open-google-tab.png&quot; srcSet=&quot;/static/1d04b184fd9de2f5af0a605a1b7d8e1d/6f3f2/containers-open-google-tab.png 256w,/static/1d04b184fd9de2f5af0a605a1b7d8e1d/01e7c/containers-open-google-tab.png 512w,/static/1d04b184fd9de2f5af0a605a1b7d8e1d/2bef9/containers-open-google-tab.png 1024w,/static/1d04b184fd9de2f5af0a605a1b7d8e1d/71c1d/containers-open-google-tab.png 1536w,/static/1d04b184fd9de2f5af0a605a1b7d8e1d/a878e/containers-open-google-tab.png 2048w,/static/1d04b184fd9de2f5af0a605a1b7d8e1d/aae30/containers-open-google-tab.png 2080w&quot; sizes=&quot;(max-width: 1024px) 100vw, 1024px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
    &lt;/span&gt;&lt;/p&gt;&lt;p&gt;Now whenever you go to (for example) keep.google.com it will first ask if you want to open that web app in the preferred container (in this instance the Google container). You might like that Firefox always asks and there are some scenarios where that is preferred, but it would get pretty old pretty quickly if Firefox always asked you if you wanted to open mail.google.com in the Google container. Thankfully, we can tell Firefox to remember our decision and automatically open in the specified container without prompting us.&lt;/p&gt;&lt;p&gt;&lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1024px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:45.3125%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAABYlAAAWJQFJUiTwAAABkUlEQVQoz22RW2/TQBBG/fupEHETQkjLpRIvCIEQT3mteIGKPrVSWxAJudrxLbYT764v8UG7VoqpaunTeOyds9/MWO+GXY5f2rx6MeDN8ITTfp9Br8vbYYezkwF926b37Cl2d0DveZ9Op8OToyNGoxFhGOJ5Pr4fcHNzi20fY1U1VGVNVVbop8wLCpWjVMG+LCmUMrmUkjzPyfX/omS/rxFCmHy73ZFlzbulpEJKZZKrq2vG4wl+EJpbXXdNEEaNghDHcVmvPfN9tXIMSNcdpFSOpYyb5mYNW8wXpg1d2MQ10+mM5XJlQBqilaZbhJD3oIOsNr2uQcmc8e8Jd7c/jSbjP/heQBhExJuE3TYzZ3Tbh7q2S6tN19rtMlzHNe3ptrTLKNqYGEYxQuSN5L/O2tD/HOooVW4AWnGcGEVRTFUWgF5cea+6esThQ6COmZBmCXpmjl6M7zFxUj5epHz+kfLlMuX9t4RfK2Gg8jGH7eHq+WjgfL5gOpvjrpbczTacfU348D3h00XK6/OY65mAB8C//dqeoXR+k3wAAAAASUVORK5CYII=&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;Always open in specific container&quot; title=&quot;Always open in specific container&quot; src=&quot;/static/935b5dfe9553b9c8a0a75701054460d5/2bef9/containers-always-open.png&quot; srcSet=&quot;/static/935b5dfe9553b9c8a0a75701054460d5/6f3f2/containers-always-open.png 256w,/static/935b5dfe9553b9c8a0a75701054460d5/01e7c/containers-always-open.png 512w,/static/935b5dfe9553b9c8a0a75701054460d5/2bef9/containers-always-open.png 1024w,/static/935b5dfe9553b9c8a0a75701054460d5/71c1d/containers-always-open.png 1536w,/static/935b5dfe9553b9c8a0a75701054460d5/a878e/containers-always-open.png 2048w,/static/935b5dfe9553b9c8a0a75701054460d5/410b4/containers-always-open.png 2102w&quot; sizes=&quot;(max-width: 1024px) 100vw, 1024px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
    &lt;/span&gt;&lt;/p&gt;&lt;p&gt;The one URL you should not add to automatically open inside of the Google container is &lt;a href=&quot;http://www.google.com.&quot;&gt;www.google.com.&lt;/a&gt; This may seem counter-intuitive but if we set that domain to always use the Google container then any random search in the addressbar will open in the Google container. Since we&amp;#x27;re actually logged into our Google account in that container all of our search activity and history will still end up associated with our account.&lt;/p&gt;&lt;p&gt;Instead only register Google apps with the Google container.&lt;/p&gt;&lt;p&gt;My personal preferences:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;mail.google.com&lt;/li&gt;&lt;li&gt;calendar.google.com&lt;/li&gt;&lt;li&gt;drive.google.com&lt;/li&gt;&lt;li&gt;photos.google.com&lt;/li&gt;&lt;li&gt;keep.google.com&lt;/li&gt;&lt;li&gt;myaccount.google.com&lt;/li&gt;&lt;li&gt;youtube.com&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Once you have completed setting up your Google container you are free to do any Google searches in a new tab since it will be in the Default container - the one where we are not logged into our Google Account. Any searches you do from within a Google container tab will still be associated with your account.&lt;/p&gt;&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;&lt;p&gt;My switch to Firefox has been very successful and I have enjoyed using it the last few months - especially the Developer Edition! If you have any tips of your own or ideas on how the settings can be further improved then please leave a comment below.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Color Management in Firefox]]></title><description><![CDATA[I recently switched from Google Chrome to Mozilla's  Firefox  to use a more privacy friendly browser with more considered defaults. Mozilla…]]></description><link>https://www.39digits.com/color-management-in-firefox</link><guid isPermaLink="false">https://www.39digits.com/color-management-in-firefox</guid><pubDate>Fri, 03 Apr 2020 14:25:15 GMT</pubDate><content:encoded>&lt;p&gt;I recently switched from Google Chrome to Mozilla&amp;#x27;s &lt;a href=&quot;https://www.mozilla.org/en-GB/firefox/new/&quot;&gt;Firefox&lt;/a&gt; to use a more privacy friendly browser with more considered defaults. Mozilla have put a lot of effort into making Firefox an excellent choice and I was pleasantly surprised with how well it holds up - I cannot imagine switching back to a Chromium based browser anytime soon.&lt;/p&gt;&lt;p&gt;There is, however, one aspect to Firefox that did come as a surprise - the default colour management settings leads to dramatically oversaturated colours on a wide gamut monitor such as the MacBook Pro.&lt;/p&gt;&lt;p&gt;There is a way to fix it for general web content and images not tagged with ICC color profiles.&lt;/p&gt;&lt;h2 id=&quot;how-to-set-firefox-to-be-fully-color-managed&quot;&gt;How to set Firefox to be fully color managed&lt;/h2&gt;&lt;p&gt;Open Firefox and go to the configuration by entering &lt;code class=&quot;language-text&quot;&gt;about:config&lt;/code&gt; into the addressbar. You&amp;#x27;ll be warned about dragons behind the closed door but just click the button to proceed.&lt;/p&gt;&lt;p&gt;Search for &lt;code class=&quot;language-text&quot;&gt;color_management&lt;/code&gt; to filter the list of settings. You&amp;#x27;re looking for two in particular.&lt;/p&gt;&lt;p&gt;&lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1024px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:28.90625%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAYAAADDl76dAAAACXBIWXMAABYlAAAWJQFJUiTwAAABCElEQVQY042QyW6DMBRF+f/P64bS0JBAIAnBNqMx2M6p7CpqpW66OLp+um90klYDb4UgrUaq/sllgEo9KaX/P8rH2qJzJPe243p/RITsuT8Ew7SwWY/ZbNT43i1md38I3qINnVBoY0k+85z8cCBNU7LsnY8sY54nVq1Z1xWlJFIKzLqi9fILHTXkDMNAURRRk9PpxPF4JMs+OBxymqZh2zb2fcday7IsGGOw1sX4RfB/cjR1XTPPM0lVXSjLkvP5zO12o66bqNM0RaTq6YSk73uk/NZA8MZxik2UUrEuNE5C0LYPpFQ45/H+ifc+TvbOMWjLtRXxG4SQjOMYT3tt7JyLF4Qh27bzBSSLybKn7Km8AAAAAElFTkSuQmCC&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;Firefox color management settings&quot; title=&quot;Firefox color management settings&quot; src=&quot;/static/87e2a17f63b62a8d1174305bf445f2da/2bef9/firefox-settings.png&quot; srcSet=&quot;/static/87e2a17f63b62a8d1174305bf445f2da/6f3f2/firefox-settings.png 256w,/static/87e2a17f63b62a8d1174305bf445f2da/01e7c/firefox-settings.png 512w,/static/87e2a17f63b62a8d1174305bf445f2da/2bef9/firefox-settings.png 1024w,/static/87e2a17f63b62a8d1174305bf445f2da/71c1d/firefox-settings.png 1536w,/static/87e2a17f63b62a8d1174305bf445f2da/c9d77/firefox-settings.png 1964w&quot; sizes=&quot;(max-width: 1024px) 100vw, 1024px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
    &lt;/span&gt;&lt;/p&gt;&lt;p&gt;Change &lt;code class=&quot;language-text&quot;&gt;gfx.color_management.enablev4&lt;/code&gt; from &lt;strong&gt;false&lt;/strong&gt; to &lt;strong&gt;true&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;Change &lt;code class=&quot;language-text&quot;&gt;gfx.color_management.mode&lt;/code&gt; from &lt;strong&gt;2&lt;/strong&gt; to &lt;strong&gt;1&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;Restart Firefox and your browser will now be fully colour managed.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;A few days after I published this article Firefox Nightly released version 77. The default setting for gfx.color_management.mode in that Nightly build has been set to 1 meaning it will hopefully be set to fully managed in a future stable release.&lt;/p&gt;&lt;/blockquote&gt;&lt;h2 id=&quot;firefox-color-management-settings-explained&quot;&gt;Firefox color management settings explained&lt;/h2&gt;&lt;p&gt;On a default install the &lt;code class=&quot;language-text&quot;&gt;mode&lt;/code&gt; is set to &lt;strong&gt;2&lt;/strong&gt; meaning that colour management will only be applied to images tagged with an ICC color profile. Everything else is rendered without using your colour profile leading to over saturated text and images. Changing this to &lt;strong&gt;1&lt;/strong&gt; sets Firefox to be fully colour managed meaning everything rendered by Firefox (including page elements, CSS colours, tagged images and untagged images) will be colour corrected based on your monitor&amp;#x27;s colour profile.&lt;/p&gt;&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;enablev4&lt;/code&gt; setting simply allows the use of ICC V4 profiles instead of the default V2 profiles.&lt;/p&gt;&lt;p&gt;You can read up more on the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Releases/3.5/ICC_color_correction_in_Firefox&quot;&gt;color correction settings on the Mozilla blog&lt;/a&gt;. Note that the last information I could find regarding these settings stretches all the way back to Firefox 3.5 released on 30 June 2009 showing just how long nothing has changed.&lt;/p&gt;&lt;h2 id=&quot;examples-of-differences-between-firefox-and-chrome&quot;&gt;Examples of differences between Firefox and Chrome&lt;/h2&gt;&lt;h3 id=&quot;firefox-with-no-color-correction-default-settings&quot;&gt;Firefox with no color correction (default settings)&lt;/h3&gt;&lt;p&gt;&lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1024px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:20.3125%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAYAAACOXx+WAAAACXBIWXMAABYlAAAWJQFJUiTwAAABJElEQVQY012HzSuDcQCAf3/DZh81NqQ2H1vbyKLtZENqXKSs1UrLwWHKxT+glq+Mo1k0TZpMkrySIg447OogpXayOJh3s+0dj8tcHJ6e5xGKTk/N46FmdVA0WyiYmpAbNChCUBMC6lbq/Z/vOn8vqmo1FWc3pdkgBbeLihCUnFa+xnqRvXZyw/3IPhtlnw1pcJyUN0DaO0lyIITkGOHNrSU/pCPbZSRrVCEUg4Gq3YmcnOYzMcX7qJuXxQke90JkLjaJ3R6QlyIUziO0X74ipB/EYYHmxD3Ly0FO45087OvZmW8kseFCKBotVVMLxa0w5bMZcpkAVysd3ETNXC+1cRfto5gK8ZEO4zh+QmRKaLazrK/6ySxYiM+1crLbw/ORirWYn18NGsAi+ta5RwAAAABJRU5ErkJggg==&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;Nintendo site with no color correction settings on Firefox&quot; title=&quot;Nintendo site with no color correction settings on Firefox&quot; src=&quot;/static/b2ec92330e073859d3cd6c74ee369a93/2bef9/nintendo-firefox-no-color-correction.png&quot; srcSet=&quot;/static/b2ec92330e073859d3cd6c74ee369a93/6f3f2/nintendo-firefox-no-color-correction.png 256w,/static/b2ec92330e073859d3cd6c74ee369a93/01e7c/nintendo-firefox-no-color-correction.png 512w,/static/b2ec92330e073859d3cd6c74ee369a93/2bef9/nintendo-firefox-no-color-correction.png 1024w,/static/b2ec92330e073859d3cd6c74ee369a93/71c1d/nintendo-firefox-no-color-correction.png 1536w,/static/b2ec92330e073859d3cd6c74ee369a93/701e9/nintendo-firefox-no-color-correction.png 1780w&quot; sizes=&quot;(max-width: 1024px) 100vw, 1024px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
    &lt;/span&gt;&lt;/p&gt;&lt;p&gt;Note how oversaturated the colours are on Firefox&amp;#x27;s default settings - especially in the reds. If you look at the colour picker, you should see the red in the header is rendered at #E60012. This is the exact HEX value given in the CSS but on a wide gamut monitor it should be colour corrected to display the red the way it was meant to be shown.&lt;/p&gt;&lt;h3 id=&quot;chrome-default-settings&quot;&gt;Chrome (default settings)&lt;/h3&gt;&lt;p&gt;&lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1024px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:20.3125%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAYAAACOXx+WAAAACXBIWXMAABYlAAAWJQFJUiTwAAABN0lEQVQY0x3MTyhDARzA8V8hInIgB4W0HCymrdRWw94u2x5TauW+MmmRKAfODhIiB8YkpaFpDkhM7aAcxM1O/mTevG2Y7OL2FYfP9SN6fSvvSj85i5NXo5Xn5nZeagxoUk5ayslIGWmpQJMKvqSEghTzLcUUpIi8lJKWKt6kkvd/1chbrQG9o4vs2BApm0LqL7D1oPvcaB4nSW8fmqqgqw72VT+rrmG23AFCriCH1kGelA4eey0kzGYuDQZEb2gjY1HI7k6SCY3yOKCSXAlwtxfkOL7N5lWM1MUMufg0xriGnP5QFv3AFL5maWmUkw2Vm4NuwjOdRFa9iF7Xgt5kIhceJ38+xcPRCOcLDi7nuonP20nMechGgmSi41hi98jBJ43hW0KLfqKzTtYmrJzt+HiK2Vlf9vMLhIHChpFFvVQAAAAASUVORK5CYII=&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;Nintendo site with default color management settings on Chrome&quot; title=&quot;Nintendo site with default color management settings on Chrome&quot; src=&quot;/static/4dded379de24171f15b9ff412b13843c/2bef9/nintendo-chrome-color-managed.png&quot; srcSet=&quot;/static/4dded379de24171f15b9ff412b13843c/6f3f2/nintendo-chrome-color-managed.png 256w,/static/4dded379de24171f15b9ff412b13843c/01e7c/nintendo-chrome-color-managed.png 512w,/static/4dded379de24171f15b9ff412b13843c/2bef9/nintendo-chrome-color-managed.png 1024w,/static/4dded379de24171f15b9ff412b13843c/71c1d/nintendo-chrome-color-managed.png 1536w,/static/4dded379de24171f15b9ff412b13843c/701e9/nintendo-chrome-color-managed.png 1780w&quot; sizes=&quot;(max-width: 1024px) 100vw, 1024px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
    &lt;/span&gt;&lt;/p&gt;&lt;p&gt;Chrome uses my MacBook Pro&amp;#x27;s color profile to apply color correction to the contents rendered on a wide gamut display.&lt;/p&gt;&lt;h3 id=&quot;fully-color-managed-firefox&quot;&gt;Fully color managed Firefox&lt;/h3&gt;&lt;p&gt;&lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1024px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:20.3125%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAYAAACOXx+WAAAACXBIWXMAABYlAAAWJQFJUiTwAAABOElEQVQY0x3HSyjDARzA8V8h0yIXUQ5r5RHl1aYojP0v/G2khKtHzbMmTpwdJOaivCZCYoQcPBdJKcnRDmpLs+2/mffF7ascPoePaNmFxK3NvJoUXgorCBiLec7MJyR6wpJCVHSERf//T0niRxL5kkS+JYEPSSYsaUQklfi/dCSSkYNWWkPM6SCo1BMUHaFKC1qbSkhV8DXZCTVaidiseGzdzDf0sqY6cDcMclDZQUApw283c20ycZmbh2iGIqJmhdjmCNH1YQLtdh5dPTxsD3B0scrizQHB8zGi3nEKvGHk5BfdXpySlVtmXUMcu1Xudiwsj5ezMdeCaFn5aMYyYm4nb6ejPB32cTZdi3eymvOpKq4mVWJb/UR2nZTu+xDPOwb3PUsznexO1LEwUsHZeiv+/WrmZ7v4A5iWwnAOTmdtAAAAAElFTkSuQmCC&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;Nintendo site with full color management settings on Firefox&quot; title=&quot;Nintendo site with full color management settings on Firefox&quot; src=&quot;/static/8b14f368eb6b7c7a216d236b8f02be48/2bef9/nintendo-firefox-color-managed.png&quot; srcSet=&quot;/static/8b14f368eb6b7c7a216d236b8f02be48/6f3f2/nintendo-firefox-color-managed.png 256w,/static/8b14f368eb6b7c7a216d236b8f02be48/01e7c/nintendo-firefox-color-managed.png 512w,/static/8b14f368eb6b7c7a216d236b8f02be48/2bef9/nintendo-firefox-color-managed.png 1024w,/static/8b14f368eb6b7c7a216d236b8f02be48/71c1d/nintendo-firefox-color-managed.png 1536w,/static/8b14f368eb6b7c7a216d236b8f02be48/701e9/nintendo-firefox-color-managed.png 1780w&quot; sizes=&quot;(max-width: 1024px) 100vw, 1024px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
    &lt;/span&gt;&lt;/p&gt;&lt;p&gt;Here we have Firefox with the color management settings changed to ensure content Firefox renders is fully managed. This unfortunately does not include videos such as YouTube or Netflix. Sadly, Firefox does not yet apply proper color correction to video content the way Chromium browsers do.&lt;/p&gt;&lt;h3 id=&quot;firefox-comparison&quot;&gt;Firefox comparison&lt;/h3&gt;&lt;p&gt;&lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1024px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:20.3125%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAYAAACOXx+WAAAACXBIWXMAABYlAAAWJQFJUiTwAAABOUlEQVQY0x3HSyjDARzA8d9xuC6vlkYxI7T9ZYot5jGFUYqSciB5LJJclZNn2VFhB1E4LLlQfyIc3NxQDsxs7c92mXlt9FU+t4+kcnL4cTj4qbDyXmwmbswjkZlJSoRfERAhmlvEq9NNVGnk2VzNQ34ZgWwTIckgLGlooiMs6f+XpF7Pt6LwMdFH3G7jW4QPpYzPzioSzRaemmyEjaVo5Xa0yWGCThdB0RGqqSfS5SLU2sidu43ntgYirXVIymAgaVFIbA7xtjFArMPO42I3N9v9+E/W8KpbBBxOolYnEd842tYkDz3t3HoHud71cKD6WL/0EzieIapOI6msLJLGAt7XB/k6HCXo7+VsuYSLORPnS4VczVbyYqlEK7CirXqIHU1xvz+CulLPyYIDdamW0/kWtJ0xXvbG+ANf5cans+nOiwAAAABJRU5ErkJggg==&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;Nintendo site with full color management settings on Firefox&quot; title=&quot;Nintendo site with full color management settings on Firefox&quot; src=&quot;/static/7b34a3ca6fc45393c22d0d866f8f0827/2bef9/nintendo-firefox-comparison.png&quot; srcSet=&quot;/static/7b34a3ca6fc45393c22d0d866f8f0827/6f3f2/nintendo-firefox-comparison.png 256w,/static/7b34a3ca6fc45393c22d0d866f8f0827/01e7c/nintendo-firefox-comparison.png 512w,/static/7b34a3ca6fc45393c22d0d866f8f0827/2bef9/nintendo-firefox-comparison.png 1024w,/static/7b34a3ca6fc45393c22d0d866f8f0827/71c1d/nintendo-firefox-comparison.png 1536w,/static/7b34a3ca6fc45393c22d0d866f8f0827/701e9/nintendo-firefox-comparison.png 1780w&quot; sizes=&quot;(max-width: 1024px) 100vw, 1024px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
    &lt;/span&gt;&lt;/p&gt;&lt;p&gt;This side by side should show the dramatic difference between Firefox settings.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Automatically format JavaScript commits using Prettier, Husky and lint-staged]]></title><description><![CDATA[What we will accomplish. Automatically run the code formatter  Prettier  against all relevant files currently staged ahead of being…]]></description><link>https://www.39digits.com/automatically-format-your-javascript-commits-using-prettier-and-husky</link><guid isPermaLink="false">https://www.39digits.com/automatically-format-your-javascript-commits-using-prettier-and-husky</guid><pubDate>Mon, 08 Jan 2018 15:37:39 GMT</pubDate><content:encoded>&lt;h2 id=&quot;what-we-will-accomplish&quot;&gt;What we will accomplish.&lt;/h2&gt;&lt;p&gt;Automatically run the code formatter &lt;a href=&quot;https://prettier.io/&quot;&gt;Prettier&lt;/a&gt; against all relevant files currently staged ahead of being committed to your repository.&lt;/p&gt;&lt;p&gt;Equally important, our solution should ignore all files not currently staged and leave these untouched.&lt;/p&gt;&lt;p&gt;This will ensure you can spend more time checking actual code quality in pull requests by leaving the formatting to your tools.&lt;/p&gt;&lt;p&gt;If you want to learn more on &lt;a href=&quot;https://www.39digits.com/configure-prettier-and-eslint-in-visual-studio-code/&quot;&gt;setting up Prettier and ESLint&lt;/a&gt; then take a look at my previous article on the topic.&lt;/p&gt;&lt;h2 id=&quot;install-dependencies&quot;&gt;Install dependencies&lt;/h2&gt;&lt;p&gt;First install the required dependencies.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; --save-dev husky lint-staged&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Husky&lt;/strong&gt; provides the ability to create git commit hooks allowing us to run a few commands as a precommit sequence. Perhaps worth noting that you will need a git repository (even empty) already initiated within the project prior to installing Husky as it will setup git hooks during installation.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Lint-staged&lt;/strong&gt; on the other hand provides a way to pipe only the files currently staged for commit to Prettier as opposed to the entire codebase.&lt;/p&gt;&lt;h2 id=&quot;update-our-npm-scripts&quot;&gt;Update our npm scripts&lt;/h2&gt;&lt;p&gt;At its simplest this is all you need to add to your &lt;code class=&quot;language-text&quot;&gt;package.json&lt;/code&gt; to leverage these pre-commit hooks.&lt;/p&gt;&lt;div class=&quot;gatsby-code-title gatsby-remark-code-title&quot;&gt;package.json&lt;/div&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;&amp;quot;scripts&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&amp;quot;precommit&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;lint-staged&amp;quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&amp;quot;lint-staged&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&amp;quot;*.{js,jsx,json}&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;quot;prettier --write&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;git add&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;precommit&lt;/code&gt; script is handled by &lt;a href=&quot;https://github.com/typicode/husky&quot;&gt;Husky&lt;/a&gt; and will run lint-staged (or any other command we provide) ahead of the actual commit.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/okonet/lint-staged&quot;&gt;Lint-staged&lt;/a&gt; then looks at its own configuration in the &lt;code class=&quot;language-text&quot;&gt;lint-staged&lt;/code&gt; section of our &lt;code class=&quot;language-text&quot;&gt;package.json&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;In our example, all &lt;code class=&quot;language-text&quot;&gt;.js&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;.jsx&lt;/code&gt;, and &lt;code class=&quot;language-text&quot;&gt;.json&lt;/code&gt; files will automatically be formatted by Prettier before adding these newly formatted file changes back into our list of staged files.&lt;/p&gt;&lt;h2 id=&quot;why-lint-staged&quot;&gt;Why lint-staged?&lt;/h2&gt;&lt;p&gt;You might wonder why we don&amp;#x27;t just use Husky to run a few npm scripts.&lt;/p&gt;&lt;p&gt;The key concept is that we only want to apply the code formatter to the list of files currently staged - nothing else. I&amp;#x27;ve repeated that phrase a few times through this short tutorial because it&amp;#x27;s an important point to keep in mind.&lt;/p&gt;&lt;p&gt;You might already have an npm script to run prettier on your &lt;code class=&quot;language-text&quot;&gt;.js&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;.jsx&lt;/code&gt; files (similar to &lt;code class=&quot;language-text&quot;&gt;&amp;quot;format&amp;quot;: &amp;quot;prettier --write &amp;#x27;**/*.{js,jsx}&amp;#x27;&amp;quot;&lt;/code&gt;). If so, you might be tempted to add &lt;code class=&quot;language-text&quot;&gt;npm run format&lt;/code&gt; into the lint-staged configuration instead of &lt;code class=&quot;language-text&quot;&gt;prettier --write&lt;/code&gt; - but don&amp;#x27;t do this.&lt;/p&gt;&lt;p&gt;The npm script &lt;code class=&quot;language-text&quot;&gt;format&lt;/code&gt; is running prettier against all files matching the &lt;code class=&quot;language-text&quot;&gt;&amp;#x27;**/*.{js,jsx}&amp;#x27;&lt;/code&gt; pattern. This would mean lint-stage would run Prettier against your entire code base and not purely on files currently staged. To repeat - this setup has the side-effect of writing format changes to files not currently staged.&lt;/p&gt;&lt;p&gt;I mention the above as I have seen a few Husky / Prettier tutorials out there advocating the use of an npm script that is inadvertantly run against the entire codebase. Instead leverage lint-staged to pipe the matching files from the list of staged changes into the prettier command.&lt;/p&gt;&lt;h2 id=&quot;bonus-add-testing-to-your-precommits&quot;&gt;BONUS: Add testing to your precommits&lt;/h2&gt;&lt;p&gt;Husky can also be hooked up to your testing to ensure only code that passes tests are eventually committed.&lt;/p&gt;&lt;p&gt;Here&amp;#x27;s the most basic example of adding tests as a precommit check in your &lt;code class=&quot;language-text&quot;&gt;package.json&lt;/code&gt;&lt;/p&gt;&lt;div class=&quot;gatsby-code-title gatsby-remark-code-title&quot;&gt;package.json&lt;/div&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token property&quot;&gt;&amp;quot;scripts&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&amp;quot;test&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;exit 0&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&amp;quot;precommit&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;lint-staged &amp;amp;&amp;amp; npm test&amp;quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&amp;quot;lint-staged&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&amp;quot;*.{js,jsx,json}&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;quot;prettier --write&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;git add&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Obviously, this is the most foolish test pattern in existence but you get the idea how you can add something like &lt;a href=&quot;https://facebook.github.io/jest/&quot;&gt;Jest&lt;/a&gt;, Mocha or any other test framework that is currently already in use in your application.&lt;/p&gt;&lt;h2 id=&quot;further-reading&quot;&gt;Further Reading&lt;/h2&gt;&lt;p&gt;The &lt;a href=&quot;https://github.com/okonet/lint-staged#examples&quot;&gt;lint-staged documentation has some further examples&lt;/a&gt; on how it can be used with different file types.&lt;/p&gt;&lt;p&gt;Prettier lists some &lt;a href=&quot;https://prettier.io/docs/en/precommit.html&quot;&gt;alternatives to lint-staged&lt;/a&gt; to hook code formatting into your commits.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Migrate G Suite account to a Personal Google Account]]></title><description><![CDATA[The feature announcements of Google’s new  Pixel 2  phones has me excited and ready to switch over to Android from iOS. I have been using a…]]></description><link>https://www.39digits.com/migrate-g-suite-account-to-a-personal-google-account</link><guid isPermaLink="false">https://www.39digits.com/migrate-g-suite-account-to-a-personal-google-account</guid><pubDate>Sun, 12 Nov 2017 12:48:43 GMT</pubDate><content:encoded>&lt;p&gt;The feature announcements of Google’s new &lt;a href=&quot;https://www.blog.google/products/pixel/new-pixel-2/&quot;&gt;Pixel 2&lt;/a&gt; phones has me excited and ready to switch over to Android from iOS.&lt;/p&gt;&lt;p&gt;I have been using a G Suite account for many years - back when it was still called Google Apps For Your Domain - to allow me to use the excellent Google products behind my own email address instead of a gmail.com address.&lt;/p&gt;&lt;p&gt;Unfortunately, I learned that Google Assistant does not integrate deeply with G Suite accounts. This would mean I would miss out on many of the advantages the focus on &lt;a href=&quot;https://www.blog.google/topics/hardware/the-best-hardware-software-and-ai-together/&quot;&gt;Hardware + Software + Artificial Intelligence&lt;/a&gt; would bring from making the switch.&lt;/p&gt;&lt;p&gt;Not willing to accept a lesser experience, and holding a deep desire to try out the potentially truly smart mobile devices, I researched ways to migrate my G Suite account to a Personal Google Account and still keep the more professional vanity domain as my primary address.&lt;/p&gt;&lt;h2 id=&quot;table-of-contents&quot;&gt;Table of Contents&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;#key-concepts&quot;&gt;Key Concepts&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#preparation&quot;&gt;Preparation&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#google-domains&quot;&gt;Google Domains&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#chrome-bookmarks&quot;&gt;Chrome Bookmarks&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#contacts&quot;&gt;Contacts&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#calendar&quot;&gt;Calendar&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#photos&quot;&gt;Photos&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#google-drive&quot;&gt;Google Drive&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#gmail&quot;&gt;Gmail&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#sign-in-using-primary-email-address&quot;&gt;Sign in using primary email address&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#google-oauth---a-warning&quot;&gt;Google OAuth - A Warning&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;key-concepts&quot;&gt;Key Concepts&lt;/h2&gt;&lt;p&gt;The guide is separated into a section per Google product.&lt;/p&gt;&lt;p&gt;There are two types of email addresses in use:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;A primary email address using your own domain&lt;/li&gt;&lt;li&gt;A Personal Google Account email address ending in gmail.com (or googlemail.com)&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;There are two types of Google accounts in use:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;G Suite&lt;/li&gt;&lt;li&gt;Personal Google Account&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The &lt;strong&gt;G Suite&lt;/strong&gt; account relates to the paid account using your own domain for login and email. For the purposes of this guide we’ll assume the primary email address is &lt;code class=&quot;language-text&quot;&gt;mario.bros@mushroom-kingdom.com&lt;/code&gt; and is initially associated with your G Suite account.&lt;/p&gt;&lt;p&gt;The &lt;strong&gt;Personal Google Account&lt;/strong&gt; relates to what is also often referred to as a Gmail Account. It comes with access to calendar, drive, photos and many other Google products available to G Suite users. Personal Google Accounts have the extra benefits of a more integrated experience into Google Assistant. For the purposes of this guide we’ll assume the Personal Google Account is &lt;code class=&quot;language-text&quot;&gt;mario.bros@gmail.com&lt;/code&gt;&lt;/p&gt;&lt;p&gt;My G Suite account only had one User so this method is not tested against G Suite accounts with multiple Users.&lt;/p&gt;&lt;h2 id=&quot;preparation&quot;&gt;Preparation&lt;/h2&gt;&lt;p&gt;If you haven’t already done so register a Personal Google Account at &lt;a href=&quot;https://accounts.google.com/SignUp&quot;&gt;https://accounts.google.com/SignUp&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Open two instances of Chrome with one logged into your G Suite account and the other into your new Google Account.&lt;/p&gt;&lt;p&gt;If you aren’t sure how to do this, click the text on the very top right of Chrome and select &lt;em&gt;Manage People&lt;/em&gt; and select &lt;em&gt;Add Person&lt;/em&gt; on the window that pops up to add separate users for G Suite and the Personal Google Account.&lt;/p&gt;&lt;h2 id=&quot;google-domains&quot;&gt;Google Domains&lt;/h2&gt;&lt;p&gt;I transferred my primary domain to Google Domains some time ago and this turned out to be advantageous.&lt;/p&gt;&lt;p&gt;It’s worth noting that I am also using Google Domains nameservers which makes some of the setup easier - especially when we get to setting up email forwarding.&lt;/p&gt;&lt;p&gt;Initially the primary domain is owned only by the G Suite account and this ownership needs to be transferred to the Personal Google Account.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;G Suite&lt;/strong&gt;
&lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1024px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:39.453125%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAIAAAB2/0i6AAAACXBIWXMAABYlAAAWJQFJUiTwAAAA+0lEQVQY05XPwUrDQBAG4H3/58hJ6kHEi548efFooKgNTaTd7ZK0mSSbzM5sZoQGpaei32Fg4P8Zxnw5qIpiu3k/Nce2hfYqAPDe7/f7PM/zt9wM3QDO1u4whYCI0zThhWW9nCGE0+lYVVVVVkaZdOiFaNa/6vu+rmtENGPx6e9vD3c38PqiqmEYiGgJMXPXdcyMiPM8xxiZeTgHxnEkYjN9rNunh/b5cdxuVNU5Z6313gOAqlprU0plWRLRbreLMTrnELFpGiIyAaBpYRZZrsUYVXW1WmVZ9rsioojEGEWEiEQkpSQipjs/kJj1p6+qRLTUrjMppelM/+8bsBfKs7WaL+8AAAAASUVORK5CYII=&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;Google Domains share options&quot; title=&quot;Google Domains share options&quot; src=&quot;/static/80d37d51399012080e32a524ae25e643/2bef9/google-domains-01-transfer-permissions-1.png&quot; srcSet=&quot;/static/80d37d51399012080e32a524ae25e643/6f3f2/google-domains-01-transfer-permissions-1.png 256w,/static/80d37d51399012080e32a524ae25e643/01e7c/google-domains-01-transfer-permissions-1.png 512w,/static/80d37d51399012080e32a524ae25e643/2bef9/google-domains-01-transfer-permissions-1.png 1024w,/static/80d37d51399012080e32a524ae25e643/71c1d/google-domains-01-transfer-permissions-1.png 1536w,/static/80d37d51399012080e32a524ae25e643/29007/google-domains-01-transfer-permissions-1.png 1600w&quot; sizes=&quot;(max-width: 1024px) 100vw, 1024px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
    &lt;/span&gt;
Open &lt;a href=&quot;https://domains.google.com&quot;&gt;https://domains.google.com&lt;/a&gt; and click on the icon of two people to go to the Sharing settings. Alternatively, click on the gear icon next to your domain and then Permissions to expand the settings.&lt;/p&gt;&lt;p&gt;&lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1024px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:46.875%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAIAAAC9o5sfAAAACXBIWXMAABYlAAAWJQFJUiTwAAABRElEQVQoz32R2WrdQAyG/f5PkzSEEHrRJzgXgd4UTtokdbyNxp5F41k0soKPLxpOoB9CICT9aGmMRei6/m8bvAth9RcQMXziCA9vjNFaP59/v768NUJ5c6amxLITQogxyn8xxiilUkpN6lp4vIOHG/90EhHvvbU2xsjM27ZZa2utAFBr1XomImstESEiETXp5Xn+fr/8eFzPv0REKdV1nVJqXVcROVScc8wc0BPROI6FalgT1dok7xYNVIqIMDMAaD0rNR39KaXdx7imUlh4+zc8MzeIuCyLiGy8b6300nbjMAHMVmkDF5tgGUb92k79oLzVP9/St1N517khIu99COHQ8xiNxTUWqpIKH5YLe4+zhr4fYOqf/sTbE7eQm6tLIqIxBi9snziyzu1fZC41WWa6bs45O+eOWbYv5Jz3mkJ6MUT1A2sJA8N+fPTtAAAAAElFTkSuQmCC&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;Google Domains permissions&quot; title=&quot;Google Domains permissions&quot; src=&quot;/static/e7a03d07f90a4dff66d09d3ca8031856/2bef9/google-domains-02-add-user.png&quot; srcSet=&quot;/static/e7a03d07f90a4dff66d09d3ca8031856/6f3f2/google-domains-02-add-user.png 256w,/static/e7a03d07f90a4dff66d09d3ca8031856/01e7c/google-domains-02-add-user.png 512w,/static/e7a03d07f90a4dff66d09d3ca8031856/2bef9/google-domains-02-add-user.png 1024w,/static/e7a03d07f90a4dff66d09d3ca8031856/71c1d/google-domains-02-add-user.png 1536w,/static/e7a03d07f90a4dff66d09d3ca8031856/29007/google-domains-02-add-user.png 1600w&quot; sizes=&quot;(max-width: 1024px) 100vw, 1024px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
    &lt;/span&gt;
Enter the email address of your Personal Google Account and give it Full Permissions.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Personal Google Account&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;You should receive an email confirming at your Personal Google Account you have been granted domain management permissions for the domain.&lt;/p&gt;&lt;p&gt;Open &lt;a href=&quot;https://domains.google.com&quot;&gt;https://domains.google.com&lt;/a&gt; and you should now see your primary domain listed in the &lt;em&gt;My Domains&lt;/em&gt; section.&lt;/p&gt;&lt;p&gt;Feel free to remove the G Suite user’s permissions at this point.&lt;/p&gt;&lt;h2 id=&quot;chrome-bookmarks&quot;&gt;Chrome Bookmarks&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;G Suite&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Open the &lt;em&gt;Bookmarks Manager&lt;/em&gt; (&lt;a href=&quot;chrome://bookmarks/&quot;&gt;chrome://bookmarks/&lt;/a&gt;) and click &lt;code class=&quot;language-text&quot;&gt;Export Bookmarks to HTML file...&lt;/code&gt; in the &lt;em&gt;Organize&lt;/em&gt; drop down.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Personal Google Account&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Open the &lt;em&gt;Bookmarks Manager&lt;/em&gt; (&lt;a href=&quot;chrome://bookmarks/&quot;&gt;chrome://bookmarks/&lt;/a&gt;) and click &lt;code class=&quot;language-text&quot;&gt;Import Bookmarks from HTML file...&lt;/code&gt; in the &lt;em&gt;Organize&lt;/em&gt; drop down.&lt;/p&gt;&lt;h2 id=&quot;contacts&quot;&gt;Contacts&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;G Suite&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Open &lt;a href=&quot;https://contacts.google.com&quot;&gt;https://contacts.google.com&lt;/a&gt; and click &lt;code class=&quot;language-text&quot;&gt;Export&lt;/code&gt; in the &lt;em&gt;More&lt;/em&gt; drop down (not available in the newest look Google Contacts but you will be given the opportunity to switch to the old version if you click Export).&lt;/p&gt;&lt;p&gt;&lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1024px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:50%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAIAAAA7N+mxAAAACXBIWXMAABYlAAAWJQFJUiTwAAABPklEQVQoz32QW07DMBBFu352wDL4REJiBzQFBCRu05ebVIlLEyexPTMeG6mRoEWU8zs6917NpOl9Vez32zU6i0SISJcAUoz08Oxu7kxvnNZtVdfJNFFKTbxnMGboegSgk/wLImJPSsO6BgA0xgBA0zTGmEkMHBGYCIm01p8nQgjxCog4Xolows7SvnCFhL5TSu12u+12W5+oqso590smojMZwFvrzYCAeXn8SNPFYpFlmVLqeDwiYozxfMiFHEIgRPaeiJq2K4qybdvR+ZPLZmbvPTOTZ7EuZsnTLEnSNJVSbjZrKeVqtSrLcvzQ6DDzj+ycM8YgUam66es8X66EyLJMvL0LIeZCiOVymee5lBIAxqYLGQDY0/1LuH0MSlOnGyI+HOKp7N/Zzjlrbd/3XW8aPQzDYK01V7DWfqd8AY6/PUFD+feZAAAAAElFTkSuQmCC&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;Google Contacts export options&quot; title=&quot;Google Contacts export options&quot; src=&quot;/static/5304364616365acfdb6fd095b57c7984/2bef9/google-contacts-01-export.png&quot; srcSet=&quot;/static/5304364616365acfdb6fd095b57c7984/6f3f2/google-contacts-01-export.png 256w,/static/5304364616365acfdb6fd095b57c7984/01e7c/google-contacts-01-export.png 512w,/static/5304364616365acfdb6fd095b57c7984/2bef9/google-contacts-01-export.png 1024w,/static/5304364616365acfdb6fd095b57c7984/71c1d/google-contacts-01-export.png 1536w,/static/5304364616365acfdb6fd095b57c7984/29007/google-contacts-01-export.png 1600w&quot; sizes=&quot;(max-width: 1024px) 100vw, 1024px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
    &lt;/span&gt;
Customise the export to your preferences, but I used&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Contacts to Export&lt;/strong&gt;. Export only the &lt;em&gt;My Contacts&lt;/em&gt; Group to avoid also exporting &lt;em&gt;Other Contacts&lt;/em&gt; which Google might add to if you have it set to store any email address to which you reply.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Export Format&lt;/strong&gt;. Google CSV is the preferred option.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;Personal Google Account&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Open &lt;a href=&quot;https://contacts.google.com&quot;&gt;https://contacts.google.com&lt;/a&gt; and expand the &lt;em&gt;More&lt;/em&gt; drop down. Select the &lt;code class=&quot;language-text&quot;&gt;Import&lt;/code&gt; option and choose the CSV you created from your G Suite profile.&lt;/p&gt;&lt;h2 id=&quot;calendar&quot;&gt;Calendar&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;G Suite&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Open &lt;a href=&quot;https://calendar.google.com&quot;&gt;https://calendar.google.com&lt;/a&gt; in a new tab in Chrome.&lt;/p&gt;&lt;p&gt;&lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1024px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:46.484375%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAIAAAC9o5sfAAAACXBIWXMAABYlAAAWJQFJUiTwAAABB0lEQVQoz33QS47DIAwGYO5/tOkBummbaZOoAQw2gYDzIBk1aCq1Hc238sLyb1v0fY9EbdsiIjPHGJl5/AvvUkohBESs61pUl8vtenXkEBE02F2MMee8vMq/1nXdtm2eZ3E8Hg9fh5KstbbWeu8RsUzRWocQ0m57NU2TaJrmkexc6SYiRGyaRkoZX5XAp0eyUlJK2XUdABhjEJH2F7RtWxYxxiilATQzr+tazkkpjeMozjdVVd/n06mua2stIjpHzLwsyzRNnw97GoZBKHgcppRUSgEYIpQ49CEG70MIb6u+r21L3K6U5LzW0HXd/X43xiJS6QEA733OeZ4XCktKLOwHLCN29l8/KRL/OEor/5YAAAAASUVORK5CYII=&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;Google Calendar export options&quot; title=&quot;Google Calendar export options&quot; src=&quot;/static/ae2e2e581449607d3990f5198695aa4d/2bef9/google-calendar-01-export.png&quot; srcSet=&quot;/static/ae2e2e581449607d3990f5198695aa4d/6f3f2/google-calendar-01-export.png 256w,/static/ae2e2e581449607d3990f5198695aa4d/01e7c/google-calendar-01-export.png 512w,/static/ae2e2e581449607d3990f5198695aa4d/2bef9/google-calendar-01-export.png 1024w,/static/ae2e2e581449607d3990f5198695aa4d/71c1d/google-calendar-01-export.png 1536w,/static/ae2e2e581449607d3990f5198695aa4d/29007/google-calendar-01-export.png 1600w&quot; sizes=&quot;(max-width: 1024px) 100vw, 1024px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
    &lt;/span&gt;
If you are using the newer Google Calendar layout, click on the gear icon in the top right and select &lt;em&gt;Settings&lt;/em&gt;. Click on the &lt;em&gt;Import &amp;amp; Export&lt;/em&gt; sidebar menu option and then click on the &lt;em&gt;Export&lt;/em&gt; text in the bottom right of the Export panel.&lt;/p&gt;&lt;p&gt;This should download a ZIP file of your iCal events.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Personal Google Account&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Open &lt;a href=&quot;https://calendar.google.com&quot;&gt;https://calendar.google.com&lt;/a&gt; in a new tab in Chrome.&lt;/p&gt;&lt;p&gt;Similar to the G Suite procedure, click the gear icon, choose &lt;em&gt;Settings&lt;/em&gt;, click &lt;em&gt;Import &amp;amp; Export&lt;/em&gt; from the sidebar menu.&lt;/p&gt;&lt;p&gt;Inside the Import panel, select the iCal ZIP file previously downloaded and confirm which calendar you wish to add the events. I only have one primary calendar so it would be chosen by default.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Caveats&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Guests to calendar events in the G Suite account aren’t imported into the Personal Google Account calendar.&lt;/p&gt;&lt;p&gt;Later we will change it so you can also log into your Personal Google Account using the email address associated with your G Suite account. This will allow users to invite you to calendar events using your primary email (&lt;code class=&quot;language-text&quot;&gt;mario.bros@mushroom-kingdom.com&lt;/code&gt;) instead of the less professional Personal Google Account (&lt;code class=&quot;language-text&quot;&gt;mario.bros@gmail.com&lt;/code&gt;). Replies to invitations will, however, show as coming from your Personal Google Account.&lt;/p&gt;&lt;h2 id=&quot;photos&quot;&gt;Photos&lt;/h2&gt;&lt;p&gt;I had been running the Google Photos iOS app for some time and all of my photos were being backed up to my G Suite account. Luckily all of my photos were stored on my phone as it had 128Gb.&lt;/p&gt;&lt;p&gt;To transfer photos all I had to do was log out of my G Suite account in the iOS app and log into my new Personal Google Account. The app will then begin syncing photos automatically.&lt;/p&gt;&lt;p&gt;Once completed it can take a few days to properly crawl your images and the EXIF data.&lt;/p&gt;&lt;p&gt;If you don’t have all your photos on a mobile device you can still export and import photos using two alternatives.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Alternative 1&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Open &lt;a href=&quot;https://photos.google.com&quot;&gt;https://photos.google.com&lt;/a&gt; in the G Suite account and select the &lt;em&gt;Photos&lt;/em&gt; section from the sidebar.&lt;/p&gt;&lt;p&gt;Hover over the first image and click in the little circle to change it to a tick. Now hold down shift and click on the last photo to select all. You can then download all photos to your local computer.&lt;/p&gt;&lt;p&gt;Open Google Photos in the Personal Google Account and upload the images en masse.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Alternative 2&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Same procedure as above, select all your photos in the G Suite account. Share these with your Personal Google Account and from that account choose &lt;em&gt;Add to library&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;I haven’t tried either of these so I am unsure of any caveats - for example, what happens if you delete your G Suite account after sharing and adding photos to the Google Account library.&lt;/p&gt;&lt;h2 id=&quot;google-drive&quot;&gt;Google Drive&lt;/h2&gt;&lt;p&gt;You cannot export your Google Drive documents in the Google Document format. Instead these would be converted to MS Office formats during an export. This isn’t ideal as we’ll be importing it into our Google Account and expect it to be GDoc format.&lt;/p&gt;&lt;p&gt;Thankfully there’s a procedure you can follow using the &lt;em&gt;Backup and Sync&lt;/em&gt; (previously Google Drive Sync) app on your computer.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;G Suite&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;If you don’t have the &lt;a href=&quot;https://www.google.com/drive/download/backup-and-sync/&quot;&gt;Backup and Sync app&lt;/a&gt; installed then do this first.&lt;/p&gt;&lt;p&gt;Log into it using your G Suite account and allow it to sync all of your files to your local machine. This will download all files into a specially designated Google Drive folder.&lt;/p&gt;&lt;p&gt;Share all of your Google Documents (docs, sheets, etc) with your new Personal Google Account. You can speed this up by sharing on the folder level which will propagate to all subfolders and files. You don’t have to do this for normal files such as images, compressed archives, pdfs, etc and is only required for Google Documents due to owner permissions.&lt;/p&gt;&lt;p&gt;Using our examples, &lt;code class=&quot;language-text&quot;&gt;mario.bros@mushroom-kingdom.com&lt;/code&gt; would share all of their Google Drive files with &lt;code class=&quot;language-text&quot;&gt;mario.bros@gmail.com&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Personal Google Account&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Once all of your Google Drive contents has synced down to your laptop you’re ready for the next step.&lt;/p&gt;&lt;p&gt;Log out of your G Suite account in the &lt;em&gt;Preferences&lt;/em&gt; section of the &lt;em&gt;Backup and Sync&lt;/em&gt; app. The Google Drive folder will still be on your local computer with all of your files including those in Google Documents formats.&lt;/p&gt;&lt;p&gt;Now log into your Personal Google Account in the &lt;em&gt;Backup and Sync&lt;/em&gt; app. It will find the Google Drive folder and begin uploading all files to your new Google Account Drive.&lt;/p&gt;&lt;p&gt;If you didn’t share the Documents with your new user it will fail permissions and only upload files that are not Google Documents. This ensures only Google Documents that you have permission to view and edit are added to your account.&lt;/p&gt;&lt;p&gt;Once completed your new Personal Google Account will be the owner of all files (previously owned by your G Suite user) inside your Personal Google Drive.&lt;/p&gt;&lt;p&gt;It is now safe to remove the shares from inside the G Suite account.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Caveats&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Similar to Calendar Events these newly uploaded Google Documents will no longer have any of the previous shares. You will need to share folders and/or files manually once the uploads are complete.&lt;/p&gt;&lt;p&gt;You may also need to &lt;code class=&quot;language-text&quot;&gt;Add to Drive&lt;/code&gt; any files or folders that are under your &lt;em&gt;Shared With Me&lt;/em&gt; section.&lt;/p&gt;&lt;p&gt;Thankfully this wasn’t an issue for me but you should be aware of this impact on your files.&lt;/p&gt;&lt;h2 id=&quot;gmail&quot;&gt;Gmail&lt;/h2&gt;&lt;p&gt;Configuring mail was definitely the trickiest of the switchovers with quite a few steps.&lt;/p&gt;&lt;p&gt;The key here is that I’ve transferred my domain to Google Domains which allows me to setup email forwarding quite easily. If you’re hosting elsewhere you could probably setup an email redirect but I haven’t tested out the workflow.&lt;/p&gt;&lt;h3 id=&quot;step-1-import-emails-into-personal-google-account&quot;&gt;Step 1) Import emails into Personal Google Account&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;G Suite&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Log into &lt;a href=&quot;https://mail.google.com&quot;&gt;https://mail.google.com&lt;/a&gt; and go to &lt;em&gt;Settings&lt;/em&gt; (found under the gear drop down menu) and then navigate to the &lt;em&gt;Forwarding and POP/IMAP&lt;/em&gt; tab.&lt;/p&gt;&lt;p&gt;Enable &lt;em&gt;POP Download&lt;/em&gt; on your G Suite account if it is not already turned on.
&lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1024px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:21.875%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAIAAAABPYjBAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAsUlEQVQI11WN2Q6CMBRE+f+/0rigGOOSgEBbusQUsAVL70V4M2BM9GQyTzM5QZKp4yXTpcHXGCdkuV2vTtEi3G7Cw/kaE5LzgnPOm6btfO86cB3ODU+HAQAiYo8wDoMQPNrvCkal4AVjBaNVVQGAsbaxTdu2iADgp30/JcAfhFC3NE9TQumkk1IqpbTWjLGyLOu69t475+AjxPkMM4jIVZ1k94xqqbS1xhjz+DLbevjnDd682qLx2fH4AAAAAElFTkSuQmCC&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;Enable POP in Gmail&quot; title=&quot;Enable POP in Gmail&quot; src=&quot;/static/066abc1c0dd485920324130e419474a9/2bef9/google-mail-01-enable-POP.png&quot; srcSet=&quot;/static/066abc1c0dd485920324130e419474a9/6f3f2/google-mail-01-enable-POP.png 256w,/static/066abc1c0dd485920324130e419474a9/01e7c/google-mail-01-enable-POP.png 512w,/static/066abc1c0dd485920324130e419474a9/2bef9/google-mail-01-enable-POP.png 1024w,/static/066abc1c0dd485920324130e419474a9/71c1d/google-mail-01-enable-POP.png 1536w,/static/066abc1c0dd485920324130e419474a9/29007/google-mail-01-enable-POP.png 1600w&quot; sizes=&quot;(max-width: 1024px) 100vw, 1024px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
    &lt;/span&gt;
Be sure the setting for &lt;em&gt;When messages are accessed with POP&lt;/em&gt; is set to &lt;code class=&quot;language-text&quot;&gt;keep mail’s copy in the Inbox&lt;/code&gt; to avoid the messages being deleted from the G Suite account. This will be a life saver should anything go wrong.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Personal Google Account&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Log into &lt;a href=&quot;https://mail.google.com&quot;&gt;https://mail.google.com&lt;/a&gt; and go to the &lt;em&gt;Accounts and Import&lt;/em&gt; tab in &lt;em&gt;Settings&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;Click on &lt;code class=&quot;language-text&quot;&gt;Add a mail account&lt;/code&gt; in the &lt;em&gt;Check mail from other accounts&lt;/em&gt; option. This will trigger a popup where you’ll enter your G Suite credentials.&lt;/p&gt;&lt;p&gt;If asked to use Gmailify or &lt;code class=&quot;language-text&quot;&gt;Import emails from my other account (POP3)&lt;/code&gt; select using POP and continue.&lt;/p&gt;&lt;p&gt;&lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1024px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:51.953125%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAKABQDASIAAhEBAxEB/8QAGQAAAgMBAAAAAAAAAAAAAAAAAAECAwQF/8QAFQEBAQAAAAAAAAAAAAAAAAAAAQL/2gAMAwEAAhADEAAAAe0nVDoIFH//xAAZEAACAwEAAAAAAAAAAAAAAAAAAgEQETH/2gAIAQEAAQUCtjZF5//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABURAQEAAAAAAAAAAAAAAAAAAAEQ/9oACAECAQE/AWf/xAAWEAADAAAAAAAAAAAAAAAAAAAQIDH/2gAIAQEABj8CSj//xAAaEAACAgMAAAAAAAAAAAAAAAAAAREhMVFh/9oACAEBAAE/IXLxQp2xFUoo6Bh//9oADAMBAAIAAwAAABBIL//EABURAQEAAAAAAAAAAAAAAAAAAAEQ/9oACAEDAQE/ECf/xAAXEQADAQAAAAAAAAAAAAAAAAAAASEx/9oACAECAQE/EGosP//EABwQAQADAAIDAAAAAAAAAAAAAAEAESFBUXGB8f/aAAgBAQABPxBHtZeHMadX0w423TzGvanrJ9iIlVXzP//Z&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;Google Mail import account settings&quot; title=&quot;Google Mail import account settings&quot; src=&quot;/static/e2ae4c9958457babfed7cc4ce1d2228b/72e01/google-mail-02-import-account.jpg&quot; srcSet=&quot;/static/e2ae4c9958457babfed7cc4ce1d2228b/e4a55/google-mail-02-import-account.jpg 256w,/static/e2ae4c9958457babfed7cc4ce1d2228b/36dd4/google-mail-02-import-account.jpg 512w,/static/e2ae4c9958457babfed7cc4ce1d2228b/72e01/google-mail-02-import-account.jpg 1024w,/static/e2ae4c9958457babfed7cc4ce1d2228b/ac99c/google-mail-02-import-account.jpg 1536w,/static/e2ae4c9958457babfed7cc4ce1d2228b/b17f8/google-mail-02-import-account.jpg 1600w&quot; sizes=&quot;(max-width: 1024px) 100vw, 1024px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
    &lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;em&gt;Email&lt;/em&gt; will be your G Suite email address. &lt;em&gt;POP Server&lt;/em&gt; will be set to &lt;code class=&quot;language-text&quot;&gt;pop.gmail.com&lt;/code&gt; using &lt;code class=&quot;language-text&quot;&gt;Port 995&lt;/code&gt; with the username and password to match your usual login.&lt;/p&gt;&lt;p&gt;You have the option of labelling the incoming messages and to automatically Archive the emails (I didn’t do either).&lt;/p&gt;&lt;p&gt;Ensure you have checked the &lt;code class=&quot;language-text&quot;&gt;Always use a secure connection (SSL) when retrieving mail.&lt;/code&gt; selection.&lt;/p&gt;&lt;p&gt;After you’ve filled in all the details and click &lt;code class=&quot;language-text&quot;&gt;Add Account&lt;/code&gt; you will be presented with the option to also send mail as this address. Choose &lt;code class=&quot;language-text&quot;&gt;No&lt;/code&gt; for now as we’ll set this up later.&lt;/p&gt;&lt;p&gt;Click &lt;code class=&quot;language-text&quot;&gt;Finish&lt;/code&gt; and go do something for an hour or three (maybe finish off those last few Shrines in Zelda - Breath of the Wild you&amp;#x27;ve been putting off for ages?).&lt;/p&gt;&lt;p&gt;Click on the &lt;em&gt;View History&lt;/em&gt; link now appearing in the &lt;em&gt;Accounts&lt;/em&gt; tab to check progress. Wait until the feedback shows &lt;code class=&quot;language-text&quot;&gt;No mails fetched&lt;/code&gt; with regularity.&lt;/p&gt;&lt;p&gt;Once the import into your Personal Google Account has completed, send an email to your primary email address. This will allow us to test and verify emails sent to your current primary email address (&lt;code class=&quot;language-text&quot;&gt;mario.bros@mushroom-kingdom.com&lt;/code&gt;) will continue to be pulled in by your new Personal Google Account (&lt;code class=&quot;language-text&quot;&gt;mario.bros@gmail.com&lt;/code&gt;).&lt;/p&gt;&lt;h3 id=&quot;step-2-email-forwarding&quot;&gt;Step 2) Email Forwarding&lt;/h3&gt;&lt;p&gt;At this stage, any email sent to your preferred email address (&lt;code class=&quot;language-text&quot;&gt;mario.bros@mushroom-kingdom.com&lt;/code&gt;) is still connected with your G Suite account. These emails arrive into your G Suite mail first and your Personal Google Account (&lt;code class=&quot;language-text&quot;&gt;mario.bros@gmail.com&lt;/code&gt;) simply pulls new emails received since the last import.&lt;/p&gt;&lt;p&gt;That’s not ideal as we want to move completely away from the G Suite account. It’s now time to setup email forwarding under our domain in Google Domains.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Personal Google Account&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Open &lt;a href=&quot;https://domains.google.com&quot;&gt;https://domains.google.com&lt;/a&gt; and click on the &lt;em&gt;Email&lt;/em&gt; section next to the primary domain.&lt;/p&gt;&lt;p&gt;&lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1024px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:46.09375%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAIAAAC9o5sfAAAACXBIWXMAABYlAAAWJQFJUiTwAAABNElEQVQoz42Ry47UQAxF8//fwZIlezbsGRBI7GBGpJJKdyf1iMuvKhdKWpppxIazsCVL14/roWHRGLQ1M8s5q6r9N8P+7cvy/l349NF6zznHGEIIiNgfKGXf931d15wzAOScmVlEhv35Z/z+tcWgtY7j6E4ulwsRFUSgitwKVdEqImbWzh3vcQgxwTmntVZKoRPEe2ZkQzHk3qz/y6C1IrFZV1XvvXMupaSqRMxMTMhEfLQreHBEIlIurcrw2kZVp5NxHH8/4NxRdM5N8+zcOE5+vuYfL9lv9CY2MwCIMYpISmk+8d6nlOJp42lnPD4i8uFzffpVh8cbQgjTNF2vVwAQkd57jHGe52VZmNnsuBsRAUCltMp/ibdt894vyxJjVNW72Dl3u93u9vbeU0rrum5bAIA/3MQE2fqFkDsAAAAASUVORK5CYII=&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;Google Domains email forwarding settings&quot; title=&quot;Google Domains email forwarding settings&quot; src=&quot;/static/b53c12934256165bc2d4eca5cb7bda1b/2bef9/google-mail-03-email-forwarding.png&quot; srcSet=&quot;/static/b53c12934256165bc2d4eca5cb7bda1b/6f3f2/google-mail-03-email-forwarding.png 256w,/static/b53c12934256165bc2d4eca5cb7bda1b/01e7c/google-mail-03-email-forwarding.png 512w,/static/b53c12934256165bc2d4eca5cb7bda1b/2bef9/google-mail-03-email-forwarding.png 1024w,/static/b53c12934256165bc2d4eca5cb7bda1b/71c1d/google-mail-03-email-forwarding.png 1536w,/static/b53c12934256165bc2d4eca5cb7bda1b/11864/google-mail-03-email-forwarding.png 2034w&quot; sizes=&quot;(max-width: 1024px) 100vw, 1024px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
    &lt;/span&gt;&lt;/p&gt;&lt;p&gt;Under the &lt;em&gt;Email Forwarding&lt;/em&gt; section add any desired email addresses under your domain that you wish to forward to your Personal Google Account. You can, if you wish, add a wildcard (*) address to forward any emails sent to your primary domain to your Personal Google Account.&lt;/p&gt;&lt;p&gt;Using our examples we would set &lt;code class=&quot;language-text&quot;&gt;mario.bros@mushroom-kingdom.com&lt;/code&gt; to be an alias email address forwarding to &lt;code class=&quot;language-text&quot;&gt;mario.bros@gmail.com&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;As an aside, once you set an email forward inside Google Domains it automatically adds Email Forward entries to the &lt;em&gt;Synthetic Records&lt;/em&gt; section under the DNS configuration.&lt;/p&gt;&lt;p&gt;Test that the email forwarding is working by sending an email to your primary email address (&lt;code class=&quot;language-text&quot;&gt;mario.bros@mushroom-kingdom.com&lt;/code&gt;). You should now receive this email &lt;em&gt;only&lt;/em&gt; in your Personal Google Account inbox (&lt;code class=&quot;language-text&quot;&gt;mario.bros@gmail.com&lt;/code&gt;) and it shouldn’t even touch the inbox of your G Suite account. Verify this by checking both inboxes at &lt;a href=&quot;https://mail.google.com&quot;&gt;https://mail.google.com&lt;/a&gt;.&lt;/p&gt;&lt;h3 id=&quot;step-3-remove-import-into-personal-google-account&quot;&gt;Step 3) Remove Import into Personal Google Account&lt;/h3&gt;&lt;p&gt;Now that we have email forwarding setup and no emails hit the G Suite inbox it’s time to remove the import settings from the Personal Google Account.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Personal Google Account&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Open &lt;a href=&quot;https://mail.google.com&quot;&gt;https://mail.google.com&lt;/a&gt; if you don’t already have it open and navigate to the &lt;em&gt;Accounts and Import&lt;/em&gt; tab under &lt;em&gt;Settings&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;In &lt;em&gt;Check email from other accounts&lt;/em&gt; you will have the POP settings from the initial mail import still active. Click the &lt;code class=&quot;language-text&quot;&gt;delete&lt;/code&gt; link to remove this as it is no longer required. This will delete the link between accounts only and all emails in the original G Suite inbox are untouched.&lt;/p&gt;&lt;h3 id=&quot;step-4-send-mail-as-primary-email-address&quot;&gt;Step 4) Send mail as primary email address&lt;/h3&gt;&lt;p&gt;Currently if you were to reply to any emails received to your primary email address (&lt;code class=&quot;language-text&quot;&gt;mario.bros@mushroom-kingdom.com&lt;/code&gt;) it would come from your Personal Google Account (&lt;code class=&quot;language-text&quot;&gt;mario.bros@gmail.com&lt;/code&gt;). This is clearly far from an ideal solution.&lt;/p&gt;&lt;p&gt;Thankfully we are able to add multiple email addresses from which we can reply inside our Personal Google Account mail.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Personal Google Account&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Open &lt;a href=&quot;https://mail.google.com&quot;&gt;https://mail.google.com&lt;/a&gt; if you don’t still have it open from the previous step and navigate to the &lt;em&gt;Accounts and Import&lt;/em&gt; tab under &lt;em&gt;Settings&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;Click on &lt;code class=&quot;language-text&quot;&gt;Add another email address&lt;/code&gt; under &lt;em&gt;Send Mail As&lt;/em&gt; and a popup window will appear.&lt;/p&gt;&lt;p&gt;Enter the email address you wish to also reply from under the Personal Google Account. Using our examples we would enter &lt;code class=&quot;language-text&quot;&gt;mario.bros@mushroom-kingdom.com&lt;/code&gt; here. Keep the &lt;code class=&quot;language-text&quot;&gt;Treat As An Alias&lt;/code&gt; box ticked.
&lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1024px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:44.921875%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAJABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAEEBf/EABUBAQEAAAAAAAAAAAAAAAAAAAAC/9oADAMBAAIQAxAAAAHZc6lYBT//xAAZEAACAwEAAAAAAAAAAAAAAAAAIQECEDH/2gAIAQEAAQUCY8sR0//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEABj8CX//EABkQAAIDAQAAAAAAAAAAAAAAAAExABBBEf/aAAgBAQABPyEjJ5EZrMRX/9oADAMBAAIAAwAAABD0z//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABYRAQEBAAAAAAAAAAAAAAAAABEBEP/aAAgBAgEBPxCrn//EABwQAAICAgMAAAAAAAAAAAAAAAERAEEQMSGBof/aAAgBAQABPxC4pQ5gQi5C3AzsTfvPeMf/2Q==&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;Send from settings&quot; title=&quot;Send from settings&quot; src=&quot;/static/9f1f4131aabbe8e2b591bb530ea1bd90/72e01/google-mail-04-send-from-address.jpg&quot; srcSet=&quot;/static/9f1f4131aabbe8e2b591bb530ea1bd90/e4a55/google-mail-04-send-from-address.jpg 256w,/static/9f1f4131aabbe8e2b591bb530ea1bd90/36dd4/google-mail-04-send-from-address.jpg 512w,/static/9f1f4131aabbe8e2b591bb530ea1bd90/72e01/google-mail-04-send-from-address.jpg 1024w,/static/9f1f4131aabbe8e2b591bb530ea1bd90/ac99c/google-mail-04-send-from-address.jpg 1536w,/static/9f1f4131aabbe8e2b591bb530ea1bd90/b17f8/google-mail-04-send-from-address.jpg 1600w&quot; sizes=&quot;(max-width: 1024px) 100vw, 1024px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
    &lt;/span&gt;
The next screen will ask you to enter the SMTP details. Enter &lt;code class=&quot;language-text&quot;&gt;smtp.gmail.com&lt;/code&gt; for the SMTP Server and your Personal Google Account credentials for Username and Password. Be sure to use SSL connection on &lt;code class=&quot;language-text&quot;&gt;Port 465&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;The screen might also ask if you want to &lt;code class=&quot;language-text&quot;&gt;Send through Gmail&lt;/code&gt; or your own SMTP server as two checkboxes. If shown this simpler screen instead simply select to use Gmail.&lt;/p&gt;&lt;p&gt;After adding your primary email address (&lt;code class=&quot;language-text&quot;&gt;mario.bros@mushroom-kingdom.com&lt;/code&gt;) click on &lt;code class=&quot;language-text&quot;&gt;make default&lt;/code&gt; to ensure this is the default address.&lt;/p&gt;&lt;p&gt;You should now find you can reply to any received emails within your Personal Google Account as if it were from your preferred primary email address (and not the gmail.com one).&lt;/p&gt;&lt;h2 id=&quot;sign-in-using-primary-email-address&quot;&gt;Sign in using primary email address&lt;/h2&gt;&lt;p&gt;Emails to the primary email address are now being forwarded to the Personal Google Account, but it is still associated with the G Suite account as the login username.&lt;/p&gt;&lt;p&gt;The final piece to our migration is to add our primary email address as an alternative email on the Personal Google Account.&lt;/p&gt;&lt;p&gt;Unfortunately we cannot add an alternative email address that is already associated with a Google account. It is now time to completely disconnect the primary email address from G Suite and allow us to log into the Personal Google Account using that as the username.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;G Suite&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Open &lt;em&gt;G Suite Admin&lt;/em&gt; at &lt;a href=&quot;https://admin.google.com&quot;&gt;https://admin.google.com&lt;/a&gt; and navigate to &lt;em&gt;Users&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;Click to view the details of your User and expand the &lt;em&gt;Account&lt;/em&gt; section. Click &lt;code class=&quot;language-text&quot;&gt;Edit&lt;/code&gt; under &lt;em&gt;Basic Information&lt;/em&gt; and you should be presented with a popup allowing you to change the User’s email address. Change this email address to something unique and click &lt;code class=&quot;language-text&quot;&gt;Update User&lt;/code&gt; - this is so we can release our preferred primary email address from being used.&lt;/p&gt;&lt;p&gt;Using our example we could change &lt;code class=&quot;language-text&quot;&gt;mario.bros@mushroom-kingdom.com&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;luigi.bros@mushroom-kingdom.com&lt;/code&gt; thereby freeing up our original address.&lt;/p&gt;&lt;p&gt;Note that the original email address becomes an alias against the user account. It is safe to delete this alias.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Personal Google Account&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Open &lt;a href=&quot;https://myaccount.google.com/email&quot;&gt;https://myaccount.google.com/email&lt;/a&gt; and click to add a new address under &lt;em&gt;Alternative Emails&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;Enter your primary email address (&lt;code class=&quot;language-text&quot;&gt;mario.bros@mushroom-kingdom.com&lt;/code&gt;) which will add it to our Personal Google Account pending verification.&lt;/p&gt;&lt;p&gt;An email will be sent to our primary email address which should be forwarding seamlessly to your Personal Google Account’s inbox. Open your inbox at &lt;a href=&quot;https://mail.google.com&quot;&gt;https://mail.google.com&lt;/a&gt; and verify adding the primary email address as an alternative email to your gmail account.&lt;/p&gt;&lt;p&gt;Once verified you are now able to sign into your Personal Google Account using the more professional address associated with your own domain. Test this in an incognito tab to make sure everything is working.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Google Calendar&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;It&amp;#x27;s worth noting that adding an alternative email to your Personal Google Account will add it to the &lt;code class=&quot;language-text&quot;&gt;Allow responding to invitations forwarded through alternative email addresses&lt;/code&gt; list inside your Google Calendar settings for your primary calendar. This can be verified by looking at the &lt;em&gt;General Notification&lt;/em&gt; settings for your calendar.&lt;/p&gt;&lt;p&gt;The added benefit to adding the alternative email means you can also be invited to calendar events using your primary email address.&lt;/p&gt;&lt;h2 id=&quot;what-cannot-be-migrated&quot;&gt;What cannot be migrated?&lt;/h2&gt;&lt;p&gt;Unfortunately not everything can be migrated from G Suite to the Personal Google Account.&lt;/p&gt;&lt;p&gt;Potentially the biggest hurdle for some would be purchases made through Google Play - be it music, movies or apps. Thankfully I had been using iOS up until the pre-order of the Pixel 2 XL so I did not have a huge investment in the Android and Google marketplace.&lt;/p&gt;&lt;p&gt;I could not find a way to migrate YouTube subscriptions nor did I spend much time researching Google+ migration options.&lt;/p&gt;&lt;h2 id=&quot;google-oauth---a-warning&quot;&gt;Google OAuth - A Warning&lt;/h2&gt;&lt;p&gt;If you have created any accounts on external services using your G Suite account as your sign-in then you might have more work ahead of you. This is usually for services where you get the option to create an account or Sign In With Google.&lt;/p&gt;&lt;p&gt;You will potentially lose access to the third-party account if it does not also provide an alternate login method or a way to create an dedicated account after logging in with Google OAuth. &lt;a href=&quot;https://feedly.com&quot;&gt;Feedly&lt;/a&gt;, for example, allows you to associate your account with multiple Google Accounts making the migration from G Suite to my Personal Google Account a non-issue.&lt;/p&gt;&lt;p&gt;Sadly, I lost my Pokemon Go account due to this migration due to their inability to transfer accounts&lt;/p&gt;&lt;p&gt;Personally, I had not used my G Suite account to create single sign-on accounts with too many other services. Your mileage may vary so do tread cautiously.&lt;/p&gt;&lt;p&gt;You can take a look at the third-party apps with access to your G Suite account by looking in the Security tab under the User in the G Suite Admin. You &lt;a href=&quot;https://myaccount.google.com/permissions&quot;&gt;Google Account Permissions&lt;/a&gt; page may also provide the list of authorised third-party applications.&lt;/p&gt;&lt;h2 id=&quot;bonus-using-cloudflare-to-manage-dns&quot;&gt;BONUS: Using CloudFlare to manage DNS&lt;/h2&gt;&lt;p&gt;If you wish to use CloudFlare instead of Google for managing your DNS then there is a solution. You will still need to have your domain registered at Google Domains for the email forwarding to work this way.&lt;/p&gt;&lt;p&gt;Point your domain to CloudFlare&amp;#x27;s DNS and be sure to add the following as &lt;em&gt;MX Records&lt;/em&gt;:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;gmr-smtp-in.l.google.com (Priority 5)&lt;/li&gt;&lt;li&gt;alt1.gmr-smtp-in.l.google.com (Priority 10)&lt;/li&gt;&lt;li&gt;alt2.gmr-smtp-in.l.google.com (Priority 20)&lt;/li&gt;&lt;li&gt;alt3.gmr-smtp-in.l.google.com (Priority 30)&lt;/li&gt;&lt;li&gt;alt4.gmr-smtp-in.l.google.com (Priority 40)&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Test the emails are forwarding by emailing your primary email address (&lt;code class=&quot;language-text&quot;&gt;mario.bros@mushroom-kingdom.com&lt;/code&gt;).&lt;/p&gt;&lt;p&gt;THE END!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Configure Prettier and ESLint in Visual Studio Code]]></title><description><![CDATA[What will we be accomplishing? We will cover how to install and configure both  Prettier  and  ESLint  within a Node project. This post will…]]></description><link>https://www.39digits.com/configure-prettier-and-eslint-in-visual-studio-code</link><guid isPermaLink="false">https://www.39digits.com/configure-prettier-and-eslint-in-visual-studio-code</guid><pubDate>Sun, 06 Aug 2017 19:16:59 GMT</pubDate><content:encoded>&lt;h2 id=&quot;what-will-we-be-accomplishing&quot;&gt;What will we be accomplishing?&lt;/h2&gt;&lt;p&gt;We will cover how to install and configure both &lt;a href=&quot;https://github.com/prettier/prettier&quot;&gt;Prettier&lt;/a&gt; and &lt;a href=&quot;http://eslint.org/&quot;&gt;ESLint&lt;/a&gt; within a Node project.&lt;/p&gt;&lt;p&gt;This post will also cover how to integrate these tools into &lt;a href=&quot;https://code.visualstudio.com/&quot;&gt;Visual Studio Code&lt;/a&gt;.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Updated on 19 September 2017 to include Prettier 1.6&amp;#x27;s new support for configuration files.
Updated on 13 April 2020 to remove Airbnb config and reflect my current default setup.&lt;/p&gt;&lt;/blockquote&gt;&lt;h3 id=&quot;tldr-the-list-of-commands-to-run&quot;&gt;TL;DR The list of commands to run&lt;/h3&gt;&lt;p&gt;If you just want the commands to setup Prettier and ESLint.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; --save-dev prettier eslint eslint-config-prettier&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Create a &lt;code class=&quot;language-text&quot;&gt;.eslintrc.json&lt;/code&gt; in project root&lt;/p&gt;&lt;div class=&quot;gatsby-code-title gatsby-remark-code-title&quot;&gt;.eslintrc.json&lt;/div&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&amp;quot;extends&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&amp;quot;eslint:recommended&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Good standard base of rules to use by default&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// The prettier shareable configs need to come last&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&amp;quot;prettier&amp;quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&amp;quot;rules&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// My preferred rules&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&amp;quot;no-console&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;warn&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&amp;quot;no-var&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;error&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// preference for let and const only&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&amp;quot;prefer-const&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;error&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&amp;quot;eqeqeq&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;warn&amp;quot;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// warn to use === instead of ==&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&amp;quot;parserOptions&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&amp;quot;ecmaVersion&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2018&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Use the latest version ESLint understands&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&amp;quot;sourceType&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;module&amp;quot;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// We&amp;#x27;re using import/export, etc&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&amp;quot;env&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&amp;quot;es6&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Since we&amp;#x27;re using ES6+ features like Async/Await&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&amp;quot;browser&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&amp;quot;node&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Install the VSCode extensions&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;code --install-extension dbaeumer.vscode-eslint
code --install-extension esbenp.prettier-vscode&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Change VSCode settings to include &lt;code class=&quot;language-text&quot;&gt;&amp;quot;editor.formatOnSave&amp;quot;: true,&lt;/code&gt; to automatically format on save.&lt;/p&gt;&lt;h3 id=&quot;what-is-prettier&quot;&gt;What is Prettier?&lt;/h3&gt;&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Prettier&lt;/strong&gt; is an opinionated code formatter&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;A tool that will format code against some opinionated and standard default formatting rules to ensure all final code maintains a consistent style. It is particularly advantageous when working on projects in teams.&lt;/p&gt;&lt;p&gt;Prettier currently supports JavaScript (including ES2017), JSX, Flow, TypeScript, CSS, LESS, SCSS, JSON and GraphQL.&lt;/p&gt;&lt;h3 id=&quot;what-is-eslint&quot;&gt;What is ESLint?&lt;/h3&gt;&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;ESLint&lt;/strong&gt; - the pluggable linting utility for JavaScript and JSX&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Code linting is a form of static analysis that will find both code errors as well as highlight syntax not adhering to formatting styles. This means ESLint can help us find problematic code before we commit to version control and (more importantly) before the code finds its way onto Production.&lt;/p&gt;&lt;h3 id=&quot;why-not-just-use-eslint&quot;&gt;Why not just use ESLint?&lt;/h3&gt;&lt;p&gt;Good question.&lt;/p&gt;&lt;p&gt;It&amp;#x27;s true that ESLint could very well handle both the code quality errors and syntax format violations. But we will rely on ESLint purely to help alert us to problematic code and defer the format handling to Prettier.&lt;/p&gt;&lt;p&gt;Prettier&amp;#x27;s sole specialisation is to handle formatting of code and it does this job extremely well. By focusing primarily on the formatting rules, Prettier can do more sophisticated things under the hood, but due to being opinionated doesn&amp;#x27;t require time fine-tuning these style rules.&lt;/p&gt;&lt;p&gt;In short, ESLint is the de facto for static analysis and Prettier has quickly become the go-to tool for formatting code.&lt;/p&gt;&lt;h2 id=&quot;setup-the-test-project&quot;&gt;Setup the test project&lt;/h2&gt;&lt;p&gt;Begin by creating a very simple Node project in a blank directory.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; init -y &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;mkdir&lt;/span&gt; src &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;touch&lt;/span&gt; src/index.js&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Edit the &lt;code class=&quot;language-text&quot;&gt;src/index.js&lt;/code&gt; file and paste the below code - be sure to include the weird tabbing.&lt;/p&gt;&lt;div class=&quot;gatsby-code-title gatsby-remark-code-title&quot;&gt;src/index.js&lt;/div&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;logToConsole&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;msg&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;msg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; message &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;my message to log&amp;quot;&lt;/span&gt;
          &lt;span class=&quot;token function&quot;&gt;logToConsole&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;message&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&quot;configure-prettier&quot;&gt;Configure Prettier&lt;/h2&gt;&lt;h3 id=&quot;install-the-dependencies&quot;&gt;Install the dependencies&lt;/h3&gt;&lt;p&gt;Begin by installing Prettier as a dev dependency in our project.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; --save-dev prettier&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We install all the dependencies local to the project and &lt;a href=&quot;https://www.smashingmagazine.com/2016/01/issue-with-global-node-npm-packages/&quot;&gt;avoid installing Node packages globally on our system&lt;/a&gt;.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Rule of thumb: If your project depends on a package, it should be listed in your package.json file as a dependency and installed locally in your project, rather than globally.&lt;/p&gt;&lt;/blockquote&gt;&lt;h3 id=&quot;test-prettier-on-our-code&quot;&gt;Test Prettier on our code&lt;/h3&gt;&lt;p&gt;At its simplest Prettier can be run as a command-line tool. We&amp;#x27;ll be doing that now to test everything is correctly installed.&lt;/p&gt;&lt;p&gt;Out of the box Prettier already has excellent defaults, including:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;indenting using spaces and not tabs&lt;/li&gt;&lt;li&gt;indents are 2 spaces&lt;/li&gt;&lt;li&gt;semicolons inserted at the end of every line&lt;/li&gt;&lt;li&gt;setting all quotes to double quotes&lt;/li&gt;&lt;li&gt;formatting the code against a print width of 80&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;I personally prefer using single-quotes and a print width of 100. Thankfully, Prettier allows us to override any defaults using various command-line options passed at run-time. Learn about the defaults and decide on your own overrides by reading the &lt;a href=&quot;https://github.com/prettier/prettier&quot;&gt;Options available in the Prettier documentation&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Run a test of Prettier against our code from the command-line.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;./node_modules/.bin/prettier --single-quote --print-width&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;**/*.{js,jsx}&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1024px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:16.40625%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAYAAACTWi8uAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAdUlEQVQI132O4QqCMBRGfYi0pjkIwtpSN6drSO//XCfuQDAIfxzu5YPv8BXaJe7Lh1tYaceIsoHzc0LZmYsJKMHOKDNRW09tPGXnqB7+L4UeE3pItMM7IwIRnjqXi3u2TIrl7v8RNn3k2kea15KvyISjFUcLv/3uVnQ9fogXAAAAAElFTkSuQmCC&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;Prettier output&quot; title=&quot;Prettier output&quot; src=&quot;/static/a40fe22d43be1574bf7864d65a90d0c5/2bef9/prettier_output.png&quot; srcSet=&quot;/static/a40fe22d43be1574bf7864d65a90d0c5/6f3f2/prettier_output.png 256w,/static/a40fe22d43be1574bf7864d65a90d0c5/01e7c/prettier_output.png 512w,/static/a40fe22d43be1574bf7864d65a90d0c5/2bef9/prettier_output.png 1024w,/static/a40fe22d43be1574bf7864d65a90d0c5/71c1d/prettier_output.png 1536w,/static/a40fe22d43be1574bf7864d65a90d0c5/e8950/prettier_output.png 2000w&quot; sizes=&quot;(max-width: 1024px) 100vw, 1024px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
    &lt;/span&gt;&lt;/p&gt;&lt;p&gt;Running Prettier this way on the command-line outputs to the screen the format changes that would have been made to our code based on the rules defined. It&amp;#x27;s important to note that the format changes have not been written to the &lt;code class=&quot;language-text&quot;&gt;src/index.js&lt;/code&gt; file - you can verify this by opening the project in your code editor and see it hasn&amp;#x27;t changed. If you want Prettier to write the formatting changes you need to add &lt;code class=&quot;language-text&quot;&gt;--write&lt;/code&gt; to the options list.&lt;/p&gt;&lt;p&gt;We&amp;#x27;ve installed Prettier as a dependency in our local project which requires us to include the full &lt;code class=&quot;language-text&quot;&gt;./node_modules/.bin/&lt;/code&gt; path when running it from the command-line.&lt;/p&gt;&lt;p&gt;The last part of the command is where we define which files to run Prettier against. This could be a single file or a group of files. If you look closely we&amp;#x27;ve specified the path as &lt;code class=&quot;language-text&quot;&gt;**/*&lt;/code&gt; and not &lt;code class=&quot;language-text&quot;&gt;src/**/*&lt;/code&gt; where our &lt;code class=&quot;language-text&quot;&gt;index.js&lt;/code&gt; file resides. Despite running against all js and jsx files from the project root, the output was only for our &lt;code class=&quot;language-text&quot;&gt;src/index.js&lt;/code&gt; file. I wanted to show that Prettier smartly ignores the &lt;code class=&quot;language-text&quot;&gt;node_modules&lt;/code&gt; directory and concentrates only on our code.&lt;/p&gt;&lt;h3 id=&quot;create-a-prettier-config-file&quot;&gt;Create a Prettier config file&lt;/h3&gt;&lt;p&gt;We could continue to just run Prettier from the command-line but supplying our preferred list of overrides would quickly get boring.&lt;/p&gt;&lt;p&gt;Thankfully, as of Prettier 1.6+ we can create a &lt;code class=&quot;language-text&quot;&gt;.prettierrc&lt;/code&gt; file to store our preferred settings and overrides.&lt;/p&gt;&lt;p&gt;Adding a Prettier config file explicitly documents our formatting preferences within the project.&lt;/p&gt;&lt;p&gt;Create a &lt;code class=&quot;language-text&quot;&gt;.prettierrc&lt;/code&gt; file in the project root.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;touch&lt;/span&gt; .prettierrc&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The configuration uses the &lt;a href=&quot;https://prettier.io/docs/en/options.html#content&quot;&gt;Prettier API Overrides&lt;/a&gt; to set values (visit the link to find your own preferences).&lt;/p&gt;&lt;p&gt;If following along, add the following JSON to our &lt;code class=&quot;language-text&quot;&gt;.prettierrc&lt;/code&gt; config.&lt;/p&gt;&lt;div class=&quot;gatsby-code-title gatsby-remark-code-title&quot;&gt;.prettierrc&lt;/div&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&amp;quot;printWidth&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&amp;quot;singleQuote&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Read up more on the &lt;a href=&quot;https://github.com/prettier/prettier#configuration-file&quot;&gt;Prettier configuration file&lt;/a&gt;.&lt;/p&gt;&lt;h3 id=&quot;add-prettier-to-our-npm-scripts&quot;&gt;Add Prettier to our npm scripts&lt;/h3&gt;&lt;p&gt;At this point we could rerun the Prettier CLI command to test out our new config file.&lt;/p&gt;&lt;p&gt;Instead, we&amp;#x27;ll add a line to the scripts section of our &lt;code class=&quot;language-text&quot;&gt;package.json&lt;/code&gt; file to allow us to easily run the formatter manually but also hook into other scripts as pre-requisites.&lt;/p&gt;&lt;p&gt;Open the &lt;code class=&quot;language-text&quot;&gt;package.json&lt;/code&gt; file and add a line to the &lt;code class=&quot;language-text&quot;&gt;&amp;quot;scripts&amp;quot;&lt;/code&gt; section&lt;/p&gt;&lt;div class=&quot;gatsby-code-title gatsby-remark-code-title&quot;&gt;package.json&lt;/div&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token property&quot;&gt;&amp;quot;scripts&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&amp;quot;format&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;prettier --write &amp;#x27;**/*.{js,jsx}&amp;#x27;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We no longer specify the print width and single quotes as this is covered by our &lt;code class=&quot;language-text&quot;&gt;.prettierrc&lt;/code&gt; file. The &lt;code class=&quot;language-text&quot;&gt;--write&lt;/code&gt; flag is also used to save the changes to our file when run.&lt;/p&gt;&lt;p&gt;Now all you need to do is run &lt;code class=&quot;language-text&quot;&gt;npm run format&lt;/code&gt; to have Prettier format our code.&lt;/p&gt;&lt;p&gt;Note that since we&amp;#x27;re running Prettier &lt;a href=&quot;https://docs.npmjs.com/misc/scripts#path&quot;&gt;via an npm script&lt;/a&gt; we no longer need to provide the full &lt;code class=&quot;language-text&quot;&gt;./node_modules/.bin/&lt;/code&gt; path.&lt;/p&gt;&lt;p&gt;Now that we have the command as an entry in our npm scripts, we can use Prettier as a pre-commit hook using the excellent &lt;a href=&quot;https://github.com/typicode/husky&quot;&gt;Husky&lt;/a&gt; package. I&amp;#x27;ll leave that as extra reading for you to keep this tutorial on target. [Update 2018/01/08 - I have just published an article to &lt;a href=&quot;https://www.39digits.com/automatically-format-your-javascript-commits-using-prettier-and-husky/&quot;&gt;automatically format your JavaScript commits using Prettier and Husky&lt;/a&gt;]&lt;/p&gt;&lt;p&gt;The creation of the npm script still doesn&amp;#x27;t solve having to manually run Prettier each time we want to format our code. Lets take the opportunity to automate applying any format changes upon saving a supported file type in Visual Studio Code.&lt;/p&gt;&lt;h2 id=&quot;integrate-prettier-with-visual-studio-code&quot;&gt;Integrate Prettier with Visual Studio Code&lt;/h2&gt;&lt;h3 id=&quot;install-the-prettier-extension-in-visual-studio-code&quot;&gt;Install the Prettier extension in Visual Studio Code&lt;/h3&gt;&lt;p&gt;Open the extension sidebar (Cmd-Shift-X) inside Visual Studio Code and search for &lt;code class=&quot;language-text&quot;&gt;Prettier - JavaScript formatter&lt;/code&gt;. There are a few extensions with that name so be sure to install the one by &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode&quot;&gt;Esben Petersen&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;You will need to reload the Visual Studio Code window after installing the extension. You should now see a Prettier notification in the status bar when opening up a file type supported by Prettier.&lt;/p&gt;&lt;h3 id=&quot;visual-studio-code-settings&quot;&gt;Visual Studio Code settings&lt;/h3&gt;&lt;p&gt;The only change required to Visual Studio Code&amp;#x27;s &lt;code class=&quot;language-text&quot;&gt;settings.json&lt;/code&gt; file is to ensure the Format On Save functionality is triggered. This ensures the Prettier extension applies the format rules on each save of a supported file type.&lt;/p&gt;&lt;p&gt;To do this open settings and add Visual Studio Code&amp;#x27;s &lt;code class=&quot;language-text&quot;&gt;formatOnSave&lt;/code&gt; setting.&lt;/p&gt;&lt;div class=&quot;gatsby-code-title gatsby-remark-code-title&quot;&gt;settings.json&lt;/div&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token property&quot;&gt;&amp;quot;editor.formatOnSave&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Prior to version 1.6 of Prettier we were required to duplicate our override preferences in the Visual Studio Code settings. Thankfully this duplication is no longer required thanks to our &lt;code class=&quot;language-text&quot;&gt;.prettierrc&lt;/code&gt; config file (documenting and setting these preferences in a single location is a big victory).&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;If you are using an earlier version read the &lt;a href=&quot;https://github.com/prettier/prettier-vscode&quot;&gt;Prettier extension&amp;#x27;s documentation for available settings&lt;/a&gt; to add to your Visual Studio Code settings.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Now when you save a supported file it will automatically apply your formatting preferences. In fact, do this now to &lt;code class=&quot;language-text&quot;&gt;/src/index.js&lt;/code&gt; to see it in action.&lt;/p&gt;&lt;h2 id=&quot;configure-eslint&quot;&gt;Configure ESLint&lt;/h2&gt;&lt;h3 id=&quot;install-the-dependencies-1&quot;&gt;Install the dependencies&lt;/h3&gt;&lt;p&gt;Start by installing ESLint.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; --save-dev eslint&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&quot;create-the-eslint-config-file&quot;&gt;Create the ESLint config file&lt;/h3&gt;&lt;p&gt;Create a &lt;code class=&quot;language-text&quot;&gt;.eslintrc.json&lt;/code&gt; file in the project root.&lt;/p&gt;&lt;p&gt;It is inside this file that you apply project-wide and ESLint configuration and linting rules. Read more on &lt;a href=&quot;http://eslint.org/docs/user-guide/configuring&quot;&gt;configuring ESLint&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Add the following configuration to your newly created &lt;code class=&quot;language-text&quot;&gt;.eslintrc.json&lt;/code&gt; file.&lt;/p&gt;&lt;div class=&quot;gatsby-code-title gatsby-remark-code-title&quot;&gt;.eslintrc.json&lt;/div&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&amp;quot;extends&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&amp;quot;eslint:recommended&amp;quot;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Good standard base of rules to use by default&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&amp;quot;rules&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// My preferred rules&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&amp;quot;no-console&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;warn&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&amp;quot;no-var&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;error&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// preference for let and const only&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&amp;quot;prefer-const&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;error&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&amp;quot;eqeqeq&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;warn&amp;quot;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// warn to use === instead of ==&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&amp;quot;parserOptions&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&amp;quot;ecmaVersion&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2018&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Use the latest version ESLint understands&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&amp;quot;sourceType&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;module&amp;quot;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// We&amp;#x27;re using import/export, etc&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&amp;quot;env&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&amp;quot;es6&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Since we&amp;#x27;re using ES6+ features like Async/Await&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&amp;quot;browser&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&amp;quot;node&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&quot;a-little-more-about-eslint-rules&quot;&gt;A little more about ESLint Rules&lt;/h3&gt;&lt;p&gt;ESLint makes use of rules to determine whether certain code should be allowed or disallowed. No rules are enabled by default so it&amp;#x27;s up to us to define these rules based on our own requirements.&lt;/p&gt;&lt;p&gt;You can see a &lt;a href=&quot;http://eslint.org/docs/rules/&quot;&gt;full list of ESLint rules&lt;/a&gt; in the online docs.&lt;/p&gt;&lt;p&gt;At the most simple, we could define the rules in the &lt;code class=&quot;language-text&quot;&gt;.eslintrc.json&lt;/code&gt; under a &lt;code class=&quot;language-text&quot;&gt;&amp;quot;rule&amp;quot;&lt;/code&gt; node. Thankfully, there is the ability to create shareable configurations of rules. These shareable configurations take the naming convention of &lt;code class=&quot;language-text&quot;&gt;eslint-config-&amp;lt;ConfigName&amp;gt;&lt;/code&gt; and are simply npm packages that can be easily distributed. Once installed these configurations are added to the &lt;code class=&quot;language-text&quot;&gt;&amp;quot;extended&amp;quot;&lt;/code&gt; node and the order of inclusion is important so make sure you understand what could be overridden when adding new configs to the extends array.&lt;/p&gt;&lt;p&gt;Read more on &lt;a href=&quot;http://eslint.org/docs/user-guide/configuring.html#extending-configuration-files&quot;&gt;extending configuration files&lt;/a&gt; in the online docs.&lt;/p&gt;&lt;p&gt;ESLint does come with a set of recommended rules for your convenience so you could easily just install eslint and add &lt;code class=&quot;language-text&quot;&gt;&amp;quot;eslint:recommended&amp;quot;&lt;/code&gt; to the extends list. These recommended rules are great and are all you really need to get great linting defaults.&lt;/p&gt;&lt;p&gt;If required, you can override any of these by adding explicit entries in the &lt;code class=&quot;language-text&quot;&gt;&amp;quot;rules&amp;quot;&lt;/code&gt; node or by including further shareable configurations in the &lt;code class=&quot;language-text&quot;&gt;&amp;quot;extends&amp;quot;&lt;/code&gt; array.&lt;/p&gt;&lt;h3 id=&quot;about-explicitly-enabling-es6-syntax-support-and-environments&quot;&gt;About explicitly enabling ES6 syntax support and environments&lt;/h3&gt;&lt;p&gt;If we do not set the parser options our ES6+ syntax will throw linting errors. This is because ES6 syntax support is not enabled inside ESLint - it currently lints against ES5 syntax by default.&lt;/p&gt;&lt;p&gt;We specify the environment options to allow ESLint to be aware of the contexts within which our code will run. For example, setting &lt;code class=&quot;language-text&quot;&gt;browser&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;node&lt;/code&gt; allows use of &lt;code class=&quot;language-text&quot;&gt;console.log&lt;/code&gt; without linting issues.&lt;/p&gt;&lt;p&gt;You are able to explicitly enable ES6 support inside &lt;code class=&quot;language-text&quot;&gt;.eslintrc.json&lt;/code&gt;.&lt;/p&gt;&lt;div class=&quot;gatsby-code-title gatsby-remark-code-title&quot;&gt;.eslintrc.json&lt;/div&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;&amp;quot;parserOptions&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&amp;quot;ecmaVersion&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&amp;quot;sourceType&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;module&amp;quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&amp;quot;env&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&amp;quot;es6&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&amp;quot;browser&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&amp;quot;node&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Read more on &lt;a href=&quot;http://eslint.org/docs/user-guide/configuring#specifying-parser-options&quot;&gt;specifying parser options&lt;/a&gt; in the ESLint documentation.&lt;/p&gt;&lt;h3 id=&quot;test-eslint-on-our-code&quot;&gt;Test ESLint on our code&lt;/h3&gt;&lt;p&gt;Run a test of ESLint against our code from the command-line. The configuration defined in &lt;code class=&quot;language-text&quot;&gt;.eslintrc.json&lt;/code&gt; will be automatically used by ESLint.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;./node_modules/.bin/eslint &lt;span class=&quot;token string&quot;&gt;&amp;quot;**/*.{js,jsx}&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As with Prettier, ESLint will ignore the contents of the &lt;code class=&quot;language-text&quot;&gt;node_modules&lt;/code&gt; directory by default.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;You might see warnings such as
&lt;code class=&quot;language-text&quot;&gt;can&amp;#x27;t resolve reference #/definitions/basicConfig from id #&lt;/code&gt;&lt;/p&gt;&lt;p&gt;This relates to a bug in &lt;code class=&quot;language-text&quot;&gt;eslint-config-react&lt;/code&gt; and can be safely ignored. A pull request is in the process of being merged as defined at &lt;a href=&quot;https://github.com/yannickcr/eslint-plugin-react/pull/1292&quot;&gt;https://github.com/yannickcr/eslint-plugin-react/pull/1292&lt;/a&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1024px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:24.609375%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAYAAABFA8wzAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAzklEQVQY032Q226DMBAF8w9pS0NCuNnGQGpSrwkkbaX+/09Nhas8UCl9GO15ObPa3WT9RDl8ULxdSW1g345kbSCzwsEK28LxVA48V+fIo3xnc+xnKrcIb1FauhvFaaZ2V/J+IutmdkZi+S74m1fCRAt1F8i7QO2/qYYvcvcZ58IiXEiU50V59nZkp9cLVsLSCK67YJpA28803cSpnzk2I0d7IVWeVEskM4FX5aP88cnKo02gNoI2gjGCbQKl8r9oIVeeQ/1OpYWkOrP9548/R6SQ0ziZkgUAAAAASUVORK5CYII=&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;ESLint command line output&quot; title=&quot;ESLint command line output&quot; src=&quot;/static/3914a35113fc709f0413dba91b7a07e3/2bef9/eslint_output.png&quot; srcSet=&quot;/static/3914a35113fc709f0413dba91b7a07e3/6f3f2/eslint_output.png 256w,/static/3914a35113fc709f0413dba91b7a07e3/01e7c/eslint_output.png 512w,/static/3914a35113fc709f0413dba91b7a07e3/2bef9/eslint_output.png 1024w,/static/3914a35113fc709f0413dba91b7a07e3/71c1d/eslint_output.png 1536w,/static/3914a35113fc709f0413dba91b7a07e3/6fa81/eslint_output.png 1856w&quot; sizes=&quot;(max-width: 1024px) 100vw, 1024px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
    &lt;/span&gt;&lt;/p&gt;&lt;p&gt;I have intentionally included 2 errors and 1 warning in the code to see ESLint in action.&lt;/p&gt;&lt;p&gt;We could fix those errors and re-run the command manually from the terminal but where&amp;#x27;s the fun in that? We&amp;#x27;ll soon integrate ESLint into Visual Studio and use these to identify and fix the errors.&lt;/p&gt;&lt;h3 id=&quot;add-eslint-to-our-npm-scripts&quot;&gt;Add ESLint to our npm scripts&lt;/h3&gt;&lt;p&gt;Before we move onto actually integrating ESLint into our editor, add a line to the scripts section of &lt;code class=&quot;language-text&quot;&gt;package.json&lt;/code&gt;.&lt;/p&gt;&lt;div class=&quot;gatsby-code-title gatsby-remark-code-title&quot;&gt;package.json&lt;/div&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token property&quot;&gt;&amp;quot;scripts&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&amp;quot;lint&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;eslint &amp;#x27;**/*.{js,jsx}&amp;#x27; --quiet&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&amp;quot;format&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;prettier --write &amp;#x27;**/*.{js,jsx}&amp;#x27;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;--quiet&lt;/code&gt; option suppresses warnings and reports only errors. This makes the command especially handy as a pre-commit hook to avoid committing code with errors.&lt;/p&gt;&lt;p&gt;Test this on the command line by running &lt;code class=&quot;language-text&quot;&gt;npm run lint&lt;/code&gt;. You should see the same output as before but without the warning.&lt;/p&gt;&lt;h3 id=&quot;setting-eslint-to-ignore-files-or-folders&quot;&gt;Setting ESLint to ignore files or folders&lt;/h3&gt;&lt;p&gt;ESLint conveniently ignores the contents of the &lt;code class=&quot;language-text&quot;&gt;node_modules&lt;/code&gt; directory by default but you can also set other files and folders that should be ignored.&lt;/p&gt;&lt;p&gt;To use this feature you need simply create a &lt;code class=&quot;language-text&quot;&gt;.eslintignore&lt;/code&gt; file in your project root and fill it with the files/directories to ignore. The patterns used are the same as within a &lt;code class=&quot;language-text&quot;&gt;.gitignore&lt;/code&gt; file.&lt;/p&gt;&lt;p&gt;A good use case is a build directory if transpiling code using something like Babel or a bundler like Webpack. We aren&amp;#x27;t doing any of that in this example so we won&amp;#x27;t require a &lt;code class=&quot;language-text&quot;&gt;.eslintignore&lt;/code&gt; file.&lt;/p&gt;&lt;p&gt;You can ready more on &lt;a href=&quot;http://eslint.org/docs/user-guide/configuring#ignoring-files-and-directories&quot;&gt;ESLint&amp;#x27;s ability to ignore files and folders&lt;/a&gt; in the official documentation.&lt;/p&gt;&lt;h2 id=&quot;integrate-eslint-with-visual-studio-code&quot;&gt;Integrate ESLint with Visual Studio Code&lt;/h2&gt;&lt;h3 id=&quot;install-the-eslint-extension-in-visual-studio-code&quot;&gt;Install the ESLint extension in Visual Studio Code&lt;/h3&gt;&lt;p&gt;Once again open the extension sidebar (Cmd-Shift-X) inside Visual Studio Code and search for &lt;code class=&quot;language-text&quot;&gt;ESLint&lt;/code&gt;. There should be only 1 result but in case that has changed be sure to install the extension by &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint&quot;&gt;Dirk Baeumer&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;You will need to reload the Code window after installing the extension.&lt;/p&gt;&lt;h3 id=&quot;verify-eslint-is-working-in-visual-studio-code&quot;&gt;Verify ESLint is working in Visual Studio Code&lt;/h3&gt;&lt;p&gt;Open &lt;code class=&quot;language-text&quot;&gt;src/index.js&lt;/code&gt; inside Visual Studio Code and you should now see ESLint in the status bar.&lt;/p&gt;&lt;p&gt;There will also be indicators within the code identifying the errors breaking our defined rules. Under my editor theme the red lines indicate errors and green lines indicate warnings.&lt;/p&gt;&lt;p&gt;&lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1024px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:24.21875%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAYAAABFA8wzAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAvElEQVQY043Ou07DQBCFYb//m6UjDh0SEZisL+u1d3b2lhjpR5gGCgLFp9OcGZ2m616xs0VTYBFBU2IYDX0/ICGgGlHVOyIhBLwI4zjRrN7zcn7j/Hyh7wbWySJuJeVCqVdKqV/5i1zqrm7vPB0fPh8GnDEspsd1hihKSZVSfh78pd42Hg8HmkUz3gnBzvgcGYLDJc8SZS/eW/d95fW2cWpbGucD68UivWWaLKObkRh3MeV/0ZhJuXJsT3wAXZp+wbymuBUAAAAASUVORK5CYII=&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;ESLint errors shown in Visual Studio Code&quot; title=&quot;ESLint errors shown in Visual Studio Code&quot; src=&quot;/static/881373cbd4d43f4c2f7bc6a25371a88e/2bef9/vscode_eslint_errors.png&quot; srcSet=&quot;/static/881373cbd4d43f4c2f7bc6a25371a88e/6f3f2/vscode_eslint_errors.png 256w,/static/881373cbd4d43f4c2f7bc6a25371a88e/01e7c/vscode_eslint_errors.png 512w,/static/881373cbd4d43f4c2f7bc6a25371a88e/2bef9/vscode_eslint_errors.png 1024w,/static/881373cbd4d43f4c2f7bc6a25371a88e/71c1d/vscode_eslint_errors.png 1536w,/static/881373cbd4d43f4c2f7bc6a25371a88e/71ba4/vscode_eslint_errors.png 1870w&quot; sizes=&quot;(max-width: 1024px) 100vw, 1024px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
    &lt;/span&gt;&lt;/p&gt;&lt;p&gt;These are just some of the types of errors ESLint may identify and we&amp;#x27;ll now see how we can resolve it.&lt;/p&gt;&lt;h2 id=&quot;fixing-keyword-errors&quot;&gt;Fixing keyword errors&lt;/h2&gt;&lt;p&gt;Hover over the first &lt;code class=&quot;language-text&quot;&gt;var&lt;/code&gt; keyword and you should see a text popup.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;[eslint] Unexpected var, use let or const instead. (no-var)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The initial text provides details on the issue detected and where possible suggestions for a fix. The text in parens tells us which specific ESLint rule is currently being broken (no-var).&lt;/p&gt;&lt;p&gt;Change the &lt;code class=&quot;language-text&quot;&gt;var&lt;/code&gt; keyword to &lt;code class=&quot;language-text&quot;&gt;const&lt;/code&gt; and the error indicator should immediately disappear.&lt;/p&gt;&lt;p&gt;Hovering over &lt;code class=&quot;language-text&quot;&gt;message&lt;/code&gt; should display similar text in the popup suggesting the use of &lt;code class=&quot;language-text&quot;&gt;const&lt;/code&gt; but look carefully at the rule inside the parens - (prefer-const) in this instance.&lt;/p&gt;&lt;p&gt;This time, instead of just changing the &lt;code class=&quot;language-text&quot;&gt;let&lt;/code&gt; keyword click on the &lt;code class=&quot;language-text&quot;&gt;message&lt;/code&gt; variable and a little lightbulb will appear in the gutter next to the row number. Click on the lightbulb and you&amp;#x27;ll be presented with some fix options - select &lt;code class=&quot;language-text&quot;&gt;Fix this prefer-const option&lt;/code&gt; and the error will be automatically corrected by replacing &lt;code class=&quot;language-text&quot;&gt;let&lt;/code&gt; with the &lt;code class=&quot;language-text&quot;&gt;const&lt;/code&gt; keyword.&lt;/p&gt;&lt;h2 id=&quot;ignoring-expected-rule-violations&quot;&gt;Ignoring expected rule violations&lt;/h2&gt;&lt;p&gt;Sometimes we might write code that breaks a rule but we want ESLint to ignore this error - such as with the &lt;code class=&quot;language-text&quot;&gt;console.log&lt;/code&gt; in our example code.&lt;/p&gt;&lt;p&gt;Normally you wouldn&amp;#x27;t want to use &lt;code class=&quot;language-text&quot;&gt;console.log&lt;/code&gt; throughout your application and the warning is justified. But for the purpose of this example let&amp;#x27;s assume we want to allow the use without ESLint alerting.&lt;/p&gt;&lt;p&gt;Hover over &lt;code class=&quot;language-text&quot;&gt;console.log&lt;/code&gt; and you&amp;#x27;ll see an ESLint warning in a familiar format.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;[eslint] Unexpected console statement. (no-console)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The rule name (no-console) is what is most important here. To disable linting for code we can add a comment before it using the &lt;code class=&quot;language-text&quot;&gt;eslint-disable-next-line&lt;/code&gt; trigger.&lt;/p&gt;&lt;p&gt;Add the comment to &lt;code class=&quot;language-text&quot;&gt;src/index.js&lt;/code&gt; above the console command.&lt;/p&gt;&lt;div class=&quot;gatsby-code-title gatsby-remark-code-title&quot;&gt;src/index.js&lt;/div&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// eslint-disable-next-line no-console&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;msg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can read more on &lt;a href=&quot;http://eslint.org/docs/user-guide/configuring.html#disabling-rules-with-inline-comments&quot;&gt;disabling ESLint rules&lt;/a&gt; in the online documentation.&lt;/p&gt;&lt;h2 id=&quot;when-format-rules-collide&quot;&gt;When format rules collide...&lt;/h2&gt;&lt;p&gt;There may be instances where Prettier will format your code upon save but make a change that conflicts with a formatting rule within an ESLint configuration we are extending. If this is the case, Prettier will change the code and ESLint will throw a linting error/warning. Fixing the linting issue and saving the file will just trigger Prettier and the linting warning will return in a loop.&lt;/p&gt;&lt;p&gt;Our tools are in conflict!&lt;/p&gt;&lt;p&gt;It&amp;#x27;s time to configure Prettier and ESLint to work together. We want our tools to really focus on their specialisations. To do this we need to disable all formatting rules inside ESLint and let Prettier take over managing the code style.&lt;/p&gt;&lt;h3 id=&quot;install-dependencies&quot;&gt;Install dependencies&lt;/h3&gt;&lt;p&gt;Install the &lt;a href=&quot;https://github.com/prettier/eslint-config-prettier&quot;&gt;Prettier config for ESLint&lt;/a&gt;.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; --save-dev eslint-config-prettier&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We rely on ESLint being a code quality linter and defer all formatting to Prettier.&lt;/p&gt;&lt;h3 id=&quot;enable-the-eslint-prettier-config&quot;&gt;Enable the ESLint Prettier config&lt;/h3&gt;&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;eslint-config-prettier&lt;/code&gt; shareable configuration disables all of the formatting rules within ESLint previously set by the &lt;code class=&quot;language-text&quot;&gt;eslint:recommended&lt;/code&gt; ruleset (as well as our explicit rules). We definitely still want to keep all of the excellent code quality rules provided by that configuration though.&lt;/p&gt;&lt;p&gt;Edit &lt;code class=&quot;language-text&quot;&gt;.eslintrc.json&lt;/code&gt; and add &lt;code class=&quot;language-text&quot;&gt;&amp;quot;prettier&amp;quot;&lt;/code&gt; after the &lt;code class=&quot;language-text&quot;&gt;&amp;quot;eslint:recommended&amp;quot;&lt;/code&gt; entry.&lt;/p&gt;&lt;div class=&quot;gatsby-code-title gatsby-remark-code-title&quot;&gt;.eslintrc.json&lt;/div&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token property&quot;&gt;&amp;quot;extends&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;quot;eslint:recommended&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;prettier&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The order of configs are important to ensure the new shareable config rules override and disable all formatting rules already set within ESLint.&lt;/p&gt;&lt;p&gt;Prettier will now automatically format our code on each file save and these changes won&amp;#x27;t conflict with ESLint.&lt;/p&gt;&lt;h3 id=&quot;optional-enable-the-eslint-prettier-plugin&quot;&gt;(Optional) Enable the ESLint Prettier plugin&lt;/h3&gt;&lt;p&gt;There is a &lt;a href=&quot;https://github.com/prettier/eslint-plugin-prettier&quot;&gt;Prettier plugin for ESLint&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;This plugin leverages ESLint to show formatting errors as you type and can allow ESLint to auto-fix these. We&amp;#x27;ve set Prettier to run on each file save so we would only benefit from the added visual cues in our editor. If implementing the plugin you will benefit from ESLint reporting Prettier violations when run from the command-line.&lt;/p&gt;&lt;p&gt;If this is something you&amp;#x27;d be interested in implementing, install the plugin as a dependency.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; --save-dev eslint-plugin-prettier&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Next, add a new &lt;code class=&quot;language-text&quot;&gt;&amp;quot;plugins&amp;quot;&lt;/code&gt; node to your &lt;code class=&quot;language-text&quot;&gt;.eslintrc.json&lt;/code&gt; to enable the Prettier ESLint plugin. We also need to define the prettier rule to show errors.&lt;/p&gt;&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;.eslintrc.json&lt;/code&gt; file should now contain these extra lines.&lt;/p&gt;&lt;div class=&quot;gatsby-code-title gatsby-remark-code-title&quot;&gt;.eslintrc.json&lt;/div&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&amp;quot;extends&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;quot;eslint:recommended&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;prettier&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&amp;quot;plugins&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;quot;prettier&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&amp;quot;rules&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&amp;quot;prettier/prettier&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;quot;error&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;p&gt;In Prettier versions prior to the configuration file support, the rule needed to duplicate our Prettier preferences once more &lt;code class=&quot;language-text&quot;&gt;&amp;quot;prettier/prettier&amp;quot;: [&amp;quot;error&amp;quot;, { &amp;quot;singleQuote&amp;quot;: true, &amp;quot;printWidth&amp;quot;: 100 }]&lt;/code&gt;. Thankfully this is no longer required.&lt;/p&gt;&lt;/blockquote&gt;&lt;h2 id=&quot;bonus-tip-fix-all-auto-fixable-problems&quot;&gt;Bonus Tip: Fix all auto-fixable problems&lt;/h2&gt;&lt;p&gt;ESLint has a useful &lt;code class=&quot;language-text&quot;&gt;--fix&lt;/code&gt; option that can be supplied to the command when run in the terminal. Similar to the &lt;code class=&quot;language-text&quot;&gt;--write&lt;/code&gt; option in Prettier, this will attempt to fix the issues ESLint discovers and save these against the original file.&lt;/p&gt;&lt;p&gt;This functionality is also available inside Visual Studio Code. Open the command palette (Cmd-Shift-P) and type &lt;code class=&quot;language-text&quot;&gt;fix&lt;/code&gt; and you should be given the option &lt;code class=&quot;language-text&quot;&gt;ESLint: Fix all auto-fixable problems&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;The important takeaway is that ESLint cannot fix all of the issues it discovers and using &lt;code class=&quot;language-text&quot;&gt;--fix&lt;/code&gt; or the extension&amp;#x27;s command will only fix those which ESLint is capable of automatically fixing.&lt;/p&gt;&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;&lt;p&gt;We have just integrated both Prettier and ESLint into Visual Studio Code. More importantly we have ensured that Prettier controls all format linting and ESLint concentrates on detecting code problems.&lt;/p&gt;&lt;p&gt;Now go write beautiful code!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Permanently add ssh passphrase to keychain]]></title><description><![CDATA[Store passphrases in your keychain On MacOS (and OS X) there is a handy option for  ssh-add  to store your key's passphrase in your keychain…]]></description><link>https://www.39digits.com/permanently-add-ssh-passphrase-to-keychain</link><guid isPermaLink="false">https://www.39digits.com/permanently-add-ssh-passphrase-to-keychain</guid><pubDate>Mon, 08 May 2017 13:14:41 GMT</pubDate><content:encoded>&lt;h2 id=&quot;store-passphrases-in-your-keychain&quot;&gt;Store passphrases in your keychain&lt;/h2&gt;&lt;p&gt;On MacOS (and OS X) there is a handy option for &lt;code class=&quot;language-text&quot;&gt;ssh-add&lt;/code&gt; to store your key&amp;#x27;s passphrase in your keychain.
​&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;ssh-add -K ~/.ssh/name-of-your-key-here&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;​
You will be prompted to enter your passphrase after which it will be stored in keychain.
​&lt;/p&gt;&lt;h2 id=&quot;load-passphrase-from-keychain-on-macos-by-default&quot;&gt;Load passphrase from keychain on MacOS by default&lt;/h2&gt;&lt;p&gt;​
In MacOS (Sierra) your ssh key&amp;#x27;s passphrase is no longer automatically retrieved from keychain across sessions.
​
This means that you will be reprompted for the passphrase if you restart. Being required to re-enter your passphrase each time could be the behaviour you want for added safety, but if not we have a solution.
​
Add the following to your &lt;code class=&quot;language-text&quot;&gt;~/.ssh/config&lt;/code&gt; file.
​&lt;/p&gt;&lt;div class=&quot;gatsby-code-title gatsby-remark-code-title&quot;&gt;~/.ssh/config&lt;/div&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;vim&quot;&gt;&lt;pre class=&quot;language-vim&quot;&gt;&lt;code class=&quot;language-vim&quot;&gt;Host &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;
  UseKeychain yes&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;​
This was originally answered in a &lt;a href=&quot;https://apple.stackexchange.com/questions/48502/how-can-i-permanently-add-my-ssh-private-key-to-keychain-so-it-is-automatically&quot;&gt;StackExchange&lt;/a&gt; post but I&amp;#x27;ve replicated it here for my own easy access.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Remove all node_modules directories recursively]]></title><description><![CDATA[Here's a simple command to recursively delete all  node_modules  directories below the current directory within the tree. Add this as an…]]></description><link>https://www.39digits.com/recursively-remove-node_modules-directories</link><guid isPermaLink="false">https://www.39digits.com/recursively-remove-node_modules-directories</guid><pubDate>Sat, 06 May 2017 12:40:19 GMT</pubDate><content:encoded>&lt;p&gt;Here&amp;#x27;s a simple command to recursively delete all &lt;code class=&quot;language-text&quot;&gt;node_modules&lt;/code&gt; directories below the current directory within the tree.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;find&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt; -name &lt;span class=&quot;token string&quot;&gt;&amp;quot;node_modules&amp;quot;&lt;/span&gt; -type d -prune -exec &lt;span class=&quot;token function&quot;&gt;rm&lt;/span&gt; -rf &lt;span class=&quot;token string&quot;&gt;&amp;quot;{}&amp;quot;&lt;/span&gt; +&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Add this as an alias to bash &lt;code class=&quot;language-text&quot;&gt;~/.bashrc&lt;/code&gt; or zsh &lt;code class=&quot;language-text&quot;&gt;~/.zshrc&lt;/code&gt; to save having to type it all out each time.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;alias&lt;/span&gt; node-delete&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;find . -name &amp;quot;node_modules&amp;quot; -type d -prune -exec rm -rf &amp;quot;{}&amp;quot; +&amp;#x27;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Create environment specific configuration for a Node app]]></title><description><![CDATA[What we will be doing in this post. We will be solving two main requirements. Manage sensitive configuration values safely within our app…]]></description><link>https://www.39digits.com/create-environment-specific-config-for-a-node-app</link><guid isPermaLink="false">https://www.39digits.com/create-environment-specific-config-for-a-node-app</guid><pubDate>Wed, 03 May 2017 23:08:39 GMT</pubDate><content:encoded>&lt;h2 id=&quot;what-we-will-be-doing-in-this-post&quot;&gt;What we will be doing in this post.&lt;/h2&gt;&lt;p&gt;We will be solving two main requirements.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Manage sensitive configuration values safely within our app&lt;/strong&gt;. Additionally these settings will also need to be environment-specific. To accomplish this we use the &lt;code class=&quot;language-text&quot;&gt;dotenv&lt;/code&gt; module to load data into environment variables not stored in the code.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Ensure application configuration is managed in a single location&lt;/strong&gt;. We meet this criteria by creating a config module that handles all application configuration. The config module will also include environment-specific overrides.&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;overview-of-files-created&quot;&gt;Overview of files created&lt;/h2&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;.
├── config
│   ├── env
│   │   ├── defaults.js
│   │   └── development.js
│   └── index.js
... rest of your Node project files ...
├── .env
└── .env.sample&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&quot;install-dependencies&quot;&gt;Install dependencies&lt;/h2&gt;&lt;p&gt;We keep the dependencies low and require only two packages to be installed - &lt;strong&gt;dotenv&lt;/strong&gt; and &lt;strong&gt;lodash&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;Start by installing the &lt;a href=&quot;https://www.npmjs.com/package/dotenv&quot;&gt;dotenv&lt;/a&gt; module using your package manager of choice, be it NPM &lt;code class=&quot;language-text&quot;&gt;npm i dotenv --save&lt;/code&gt; or Yarn &lt;code class=&quot;language-text&quot;&gt;yarn add dotenv&lt;/code&gt;. The usage of this module is explained in the next section.&lt;/p&gt;&lt;p&gt;Next we require the &lt;code class=&quot;language-text&quot;&gt;merge&lt;/code&gt; function from the excellent &lt;a href=&quot;https://lodash.com/&quot;&gt;lodash&lt;/a&gt; library.&lt;/p&gt;&lt;p&gt;It seems to be so ubiquitous that you possibly already have it installed as a dependency for your project. But in case you don&amp;#x27;t, install it using either NPM &lt;code class=&quot;language-text&quot;&gt;npm i lodash --save&lt;/code&gt; or Yarn &lt;code class=&quot;language-text&quot;&gt;yarn add lodash&lt;/code&gt;&lt;/p&gt;&lt;p&gt;We use lodash instead of the native &lt;code class=&quot;language-text&quot;&gt;Object.assign&lt;/code&gt; to ensure a safe, deep merge can take place when combining configurations later on.&lt;/p&gt;&lt;p&gt;If you wish to read more on &lt;code class=&quot;language-text&quot;&gt;Object.assign&lt;/code&gt; and the way it handles deep merges then be sure to check out the &lt;a href=&quot;https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/assign&quot;&gt;Mozilla Developer Network documentation&lt;/a&gt; on the subject.&lt;/p&gt;&lt;h2 id=&quot;manage-sensitive-configuration-values&quot;&gt;Manage sensitive configuration values&lt;/h2&gt;&lt;p&gt;The dotenv NPM module loads credentials stored in a &lt;code class=&quot;language-text&quot;&gt;.env&lt;/code&gt; file and saves it in environment variables inside &lt;a href=&quot;https://nodejs.org/docs/latest/api/process.html#process_process_env&quot;&gt;process.env&lt;/a&gt; for use within our code.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Dotenv is a zero-dependency module that loads environment variables from a &lt;code class=&quot;language-text&quot;&gt;.env&lt;/code&gt; file into &lt;code class=&quot;language-text&quot;&gt;process.env&lt;/code&gt;. Storing configuration in the environment separate from code is based on &lt;a href=&quot;https://12factor.net/config&quot;&gt;The Twelve-Factor App&lt;/a&gt; methodology.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;It is important for &lt;strong&gt;security&lt;/strong&gt; that you do not commit any of your sensitive configuration to version control as these often include items such as database username and password.&lt;/p&gt;&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;.env&lt;/code&gt; file used by this module is stored in the root of the project and should not be added to version control. So be sure to add it to your &lt;code class=&quot;language-text&quot;&gt;.gitignore&lt;/code&gt; file or the equivalent ignore list in your version control of choice.&lt;/p&gt;&lt;p&gt;Another goal is to ensure our configuration is &lt;strong&gt;environment-specific&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;Your development environment will likely have different database access credentials compared to another developer&amp;#x27;s local machine setup also working on the project. In the same way your credentials for development will differ to staging. Storing this in the code would soon become difficult to maintain without collisions.&lt;/p&gt;&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;dotenv&lt;/code&gt; module allows us to easily accomplish both goals.&lt;/p&gt;&lt;p&gt;It is often convention to create a &lt;code class=&quot;language-text&quot;&gt;.env.sample&lt;/code&gt; file with helpful defaults set to safe values. This file is committed to version control. It will be used by anybody working on the project as a template to create the &lt;code class=&quot;language-text&quot;&gt;.env&lt;/code&gt; file storing the actual credentials and configuration settings.&lt;/p&gt;&lt;p&gt;Create this file now in the root of your project.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;touch .env.sample&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here is an example of a &lt;code class=&quot;language-text&quot;&gt;.env.sample&lt;/code&gt; file with common settings.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;DB_HOST=localhost
DB_NAME=database_name_here
DB_USER=database_user_here
DB_PASS=database_password_here

PORT=3000&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now to get started on the project you need merely copy &lt;code class=&quot;language-text&quot;&gt;.env.sample&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;.env&lt;/code&gt; and replace the defaults with actual values.&lt;/p&gt;&lt;p&gt;To reiterate, it&amp;#x27;s important to note that the only thing this module does is to take settings from a &lt;code class=&quot;language-text&quot;&gt;.env&lt;/code&gt; file and populate variables in &lt;code class=&quot;language-text&quot;&gt;process.env&lt;/code&gt; with the same values.&lt;/p&gt;&lt;p&gt;We will cover how these settings are used further down in the &lt;strong&gt;Load the Configuration&lt;/strong&gt; section.&lt;/p&gt;&lt;h2 id=&quot;create-the-config-module-scaffolding&quot;&gt;Create the config module scaffolding&lt;/h2&gt;&lt;p&gt;The previous section loaded configuration into &lt;code class=&quot;language-text&quot;&gt;process.env&lt;/code&gt; but we definitely don&amp;#x27;t want to sprinkle calls to that throughout our code. So instead we&amp;#x27;ll create a single config module that will handle all of this in one maintainable place.&lt;/p&gt;&lt;p&gt;We start by creating a &lt;code class=&quot;language-text&quot;&gt;config&lt;/code&gt; directory off of the project root. Within this we create a &lt;code class=&quot;language-text&quot;&gt;env&lt;/code&gt; folder which will contain some default and environment-specific overrides - including making use of the data loaded by &lt;code class=&quot;language-text&quot;&gt;dotenv&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Run the commands from your project root.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;mkdir&lt;/span&gt; -p config/env
&lt;span class=&quot;token function&quot;&gt;touch&lt;/span&gt; config/index.js
&lt;span class=&quot;token function&quot;&gt;touch&lt;/span&gt; config/env/&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;defaults,development&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;.js&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&quot;create-default-configuration&quot;&gt;Create default configuration&lt;/h2&gt;&lt;p&gt;Now that the general scaffolding has been created we will populate the default application configuration.&lt;/p&gt;&lt;p&gt;Add this code to the &lt;code class=&quot;language-text&quot;&gt;config/env/defaults.js&lt;/code&gt; file.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; config &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  port&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;PORT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

  database&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    client&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;postgresql&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    connection&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      host&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;DB_HOST&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      user&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;DB_USER&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      password&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;DB_PASS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      database&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;DB_NAME&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      charset&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;utf8&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    debug&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  logger&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    level&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;info&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    format&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;tiny&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Set the current environment or default to &amp;#x27;development&amp;#x27;&lt;/span&gt;
process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;NODE_ENV&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;NODE_ENV&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;development&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;NODE_ENV&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; config&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Define &lt;em&gt;all&lt;/em&gt; of the configuration expected by your app inside &lt;code class=&quot;language-text&quot;&gt;defaults.js&lt;/code&gt;. This is an important point to stress as our environment-specific settings should only ever override values already defined in &lt;code class=&quot;language-text&quot;&gt;defaults.js&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Be sure to set your defaults to sensible values. I favour setting defaults that I would want for production such as turning off any debug output. We&amp;#x27;ll learn how to override these settings in different environments (such as development) in the next section.&lt;/p&gt;&lt;p&gt;This module is the only location in our app that will make direct use of environment variables (such as &lt;code class=&quot;language-text&quot;&gt;process.env.DB_HOST&lt;/code&gt;). The rest of our app will reference the keys we define in the JSON exported by the config module.&lt;/p&gt;&lt;h2 id=&quot;set-environment-overrides&quot;&gt;Set environment overrides&lt;/h2&gt;&lt;p&gt;Next we create environment-specific configuration overrides by creating files inside &lt;code class=&quot;language-text&quot;&gt;config/env&lt;/code&gt; named after our environments. It is important that these are named the same as the value we use in &lt;code class=&quot;language-text&quot;&gt;process.env.NODE_ENV&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;The files inside &lt;code class=&quot;language-text&quot;&gt;config/env&lt;/code&gt; named after environments will contain only the settings you wish to override for that environment.&lt;/p&gt;&lt;p&gt;Example of a &lt;code class=&quot;language-text&quot;&gt;config/env/development.js&lt;/code&gt; file.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; config &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  database&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    debug&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  logger&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    level&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; debug&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    format&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;combined&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; config&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A good use for the development override is to set more verbose debug output.&lt;/p&gt;&lt;p&gt;In this example we create &lt;code class=&quot;language-text&quot;&gt;config/env/development.js&lt;/code&gt; but you would do the same for any other environments across your stack requiring overrides to the default settings.&lt;/p&gt;&lt;h2 id=&quot;load-the-configurations&quot;&gt;Load the configurations&lt;/h2&gt;&lt;p&gt;Excellent - we&amp;#x27;ve now created the application&amp;#x27;s default and environment-specific configurations but to use it we need to code our config loader.&lt;/p&gt;&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;config/index.js&lt;/code&gt; file has the responsibility of loading our default configuration and then any environment-specific overrides.&lt;/p&gt;&lt;p&gt;To accomplish that add this code to &lt;code class=&quot;language-text&quot;&gt;config/index.js&lt;/code&gt;.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; merge &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;lodash/merge&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Load .env settings into process.env&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// Will fail silently if no .env file present.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;NODE_ENV&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;production&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;dotenv&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// Load our own defaults which will grab from process.env&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; config &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;./env/defaults&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Only try this if we&amp;#x27;re not on Production&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;NODE_ENV&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;production&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Load environment-specific settings&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; localConfig &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// The environment file might not exist&lt;/span&gt;
    localConfig &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;./env/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    localConfig &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; localConfig &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    localConfig &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// merge the config files&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// localConfig will override defaults&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;merge&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; config&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; localConfig&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; config&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We start by loading only the merge functionality from lodash.&lt;/p&gt;&lt;p&gt;Next, the line &lt;code class=&quot;language-text&quot;&gt;require(&amp;#x27;dotenv&amp;#x27;).config();&lt;/code&gt; is what checks for the existence of a &lt;code class=&quot;language-text&quot;&gt;.env&lt;/code&gt; file and loads the contents into environment variables.&lt;/p&gt;&lt;p&gt;The default configuration is loaded next and stored in a &lt;code class=&quot;language-text&quot;&gt;config&lt;/code&gt; variable.&lt;/p&gt;&lt;p&gt;The block of code related to &lt;code class=&quot;language-text&quot;&gt;localConfig&lt;/code&gt; first attempts to include the overrides for the environment defined in &lt;code class=&quot;language-text&quot;&gt;config.env&lt;/code&gt;. If it doesn&amp;#x27;t exist then &lt;code class=&quot;language-text&quot;&gt;localConfig&lt;/code&gt; is safely set to an empty object.&lt;/p&gt;&lt;p&gt;We are now ready to merge the default and environment overrides into a single config object which is exported.&lt;/p&gt;&lt;h2 id=&quot;using-the-configuration-within-the-app&quot;&gt;Using the configuration within the app&lt;/h2&gt;&lt;p&gt;One benefit to following this config module approach is that it eliminates making &lt;code class=&quot;language-text&quot;&gt;process.env&lt;/code&gt; calls throughout our app. Instead we simply require our config wherever it is needed.&lt;/p&gt;&lt;p&gt;Example of using this new configuration in an Express app.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; express &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;express&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; app &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;express&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; config &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;./config&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; logger &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;morgan&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;port&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;format&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;listen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;port&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;App running on port &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;port&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; app&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Thunderstorm - a Sublime Text theme for Web Developers]]></title><description><![CDATA[Introducing  Thunderstorm  - the Sublime Text 3 color scheme for Web Developers. Supported Languages The theme is designed by ensuring the…]]></description><link>https://www.39digits.com/thunderstorm-a-sublime-text-theme-for-web-developers</link><guid isPermaLink="false">https://www.39digits.com/thunderstorm-a-sublime-text-theme-for-web-developers</guid><pubDate>Wed, 16 Sep 2015 10:04:10 GMT</pubDate><content:encoded>&lt;p&gt;Introducing &lt;a href=&quot;https://github.com/39digits/thunderstorm-sublime-theme&quot;&gt;Thunderstorm&lt;/a&gt; - the Sublime Text 3 color scheme for Web Developers.&lt;/p&gt;&lt;h2 id=&quot;supported-languages&quot;&gt;Supported Languages&lt;/h2&gt;&lt;p&gt;The theme is designed by ensuring the colours are set for base defaults which are inherited by languages not explicitly defined.&lt;/p&gt;&lt;p&gt;Thunderstorm has been specifically created for and tested against PHP, HTML, CSS and JavaScript with added support for YAML and XML.&lt;/p&gt;&lt;h3 id=&quot;php&quot;&gt;PHP&lt;/h3&gt;&lt;p&gt;&lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1024px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:66.015625%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAABYlAAAWJQFJUiTwAAABWklEQVQ4y5VTQXKDMBAjGIIhpH1BA9hgG0KANLn0/w9TZxeSobTQ9qBZG8ZCQrIX5DXisgPNOUTm4J8sz+V6Cx4djlTLpInpIcsL75cE/yI8mCuO9Z0RVz32qsVenZmE8SD9E2HmkFYXvOQGx9xCTmqlJqVnSN1CMLGBmGG5f8ALzRWRuyF2N0h743Vo3iGqgRFUA/yy/wYxm3N4dEDoDkF1hShHgqDsnxCz9fOZ7uDrjucSbJlA1qQ6I8odQvofFMIqNixTKGHRINItkqpDQoS5w+40hrCGtZC8R+co1Vi3nDAp3meW58/J2u3aCLZ8QUwdJOtUpcxM/TPw38yzQmKq0VovmZBeksKDGZDaAVHRIM0tDoVDOAUmVQOp6lHxaVNhMxIWDV7rO6Tu+Ka8mAFHMyChSrUfXKlw+vgvN2UkjOjq2QGxcohVjbRssC+IwGGXkbL6i9U1y5/zWVN+xGCZtQAAAABJRU5ErkJggg==&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;PHP&quot; title=&quot;PHP&quot; src=&quot;/static/60640d4116f10ac6499c8cb1dc0ee47f/2bef9/thunderstorm_php_example.png&quot; srcSet=&quot;/static/60640d4116f10ac6499c8cb1dc0ee47f/6f3f2/thunderstorm_php_example.png 256w,/static/60640d4116f10ac6499c8cb1dc0ee47f/01e7c/thunderstorm_php_example.png 512w,/static/60640d4116f10ac6499c8cb1dc0ee47f/2bef9/thunderstorm_php_example.png 1024w,/static/60640d4116f10ac6499c8cb1dc0ee47f/71c1d/thunderstorm_php_example.png 1536w,/static/60640d4116f10ac6499c8cb1dc0ee47f/a878e/thunderstorm_php_example.png 2048w,/static/60640d4116f10ac6499c8cb1dc0ee47f/821da/thunderstorm_php_example.png 2200w&quot; sizes=&quot;(max-width: 1024px) 100vw, 1024px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
    &lt;/span&gt;&lt;/p&gt;&lt;h3 id=&quot;html&quot;&gt;HTML&lt;/h3&gt;&lt;p&gt;&lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1024px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:66.015625%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAIAAAAmMtkJAAAACXBIWXMAAAsTAAALEwEAmpwYAAABPElEQVQoz42Ri2rDIBSGA/Ea07XsAWK8JOox1aRpB3v/Fxu2lI022wofouDv+Tynoioe4Pwez42JSHqiY0GFWvpaenQH9+GZCvehsUnYJIYk7NQa4Bq4TY0BoUOjg9ChNUD6gH68daNC0nMz7WHlQxb2yHUkfSCq3Ca9vxNQ7zfCRUB6bI7UrUxD3RXbunMP2puUykzHg1+YCo3yjfJMBa4j6ty/VGQ87dNnGz+oOzG3cDeXjV+Rzcjm+roWhhk/URGbG7+2cNnDugsnNubWZTrMdJzZOL/FC3cLGxdiMzbpgQrL0g9h081W2EkYYHqiCriJVEHpyO2Tz9q4D1RP3KbNef7dsxJG0lMVmY7MRLw1z1/DREHdOSRDO+adm6mO1+NrYdwDVSCGXMvweuxbm5Y5zwcDe7+0Y3rd/At04VKdM+eoMgAAAABJRU5ErkJggg==&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;HTML&quot; title=&quot;HTML&quot; src=&quot;/static/47a7920bbadcbf1beb6ea12cd84f0c17/2bef9/thunderstorm_html_example.png&quot; srcSet=&quot;/static/47a7920bbadcbf1beb6ea12cd84f0c17/6f3f2/thunderstorm_html_example.png 256w,/static/47a7920bbadcbf1beb6ea12cd84f0c17/01e7c/thunderstorm_html_example.png 512w,/static/47a7920bbadcbf1beb6ea12cd84f0c17/2bef9/thunderstorm_html_example.png 1024w,/static/47a7920bbadcbf1beb6ea12cd84f0c17/71c1d/thunderstorm_html_example.png 1536w,/static/47a7920bbadcbf1beb6ea12cd84f0c17/a878e/thunderstorm_html_example.png 2048w,/static/47a7920bbadcbf1beb6ea12cd84f0c17/821da/thunderstorm_html_example.png 2200w&quot; sizes=&quot;(max-width: 1024px) 100vw, 1024px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
    &lt;/span&gt;&lt;/p&gt;&lt;h3 id=&quot;css&quot;&gt;CSS&lt;/h3&gt;&lt;p&gt;&lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1024px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:66.015625%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAIAAAAmMtkJAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA8ElEQVQoz62RwW6DMAyGMyAJKSndtNsOJEBI4hAaSrWtu/S6t6j6KnvzCTr1WiJN+g6Wf9u/LaO88a/D+86EEiZuRm7GQoeiC1zf+MvwJTlLOjA1YAlYAiK1YyoQ6dLKJAvpIzJhbyAsges97+ZhRFoq7V17CMK1oyowfaDtftP2heozCStBxByZ/yrGM/cn7j9z95HpCa8DkcZvx3PpT1vV502E7eLcjS+Xn/L7SiuFBWARczOtXWmnTeNI7VBlk6hmLIHpA7PHsht82781LhF2pf/8ZywhE5YK+yyhEJCud75HqbBPInLtLKb6P5t/Aa5BUrcCdlKcAAAAAElFTkSuQmCC&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;CSS&quot; title=&quot;CSS&quot; src=&quot;/static/31f5a252c295025a2380f5bc503c1616/2bef9/thunderstorm_css_example.png&quot; srcSet=&quot;/static/31f5a252c295025a2380f5bc503c1616/6f3f2/thunderstorm_css_example.png 256w,/static/31f5a252c295025a2380f5bc503c1616/01e7c/thunderstorm_css_example.png 512w,/static/31f5a252c295025a2380f5bc503c1616/2bef9/thunderstorm_css_example.png 1024w,/static/31f5a252c295025a2380f5bc503c1616/71c1d/thunderstorm_css_example.png 1536w,/static/31f5a252c295025a2380f5bc503c1616/a878e/thunderstorm_css_example.png 2048w,/static/31f5a252c295025a2380f5bc503c1616/821da/thunderstorm_css_example.png 2200w&quot; sizes=&quot;(max-width: 1024px) 100vw, 1024px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
    &lt;/span&gt;&lt;/p&gt;&lt;h3 id=&quot;javascript&quot;&gt;JavaScript&lt;/h3&gt;&lt;p&gt;&lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1024px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:66.40625%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAIAAAAmMtkJAAAACXBIWXMAAAsTAAALEwEAmpwYAAABJUlEQVQoz42SW2+DMAyFq0EuNLRUu7ySOIFiOwG0rf//t00JfZpQhWTlKcfn87FP0rGEqH1sPOkw65CExfpYnYQj5aMZ13Zc2iFe7qsZFwXxoJglcEff52GWYVFhro87S2DhqC6vciQdilyUx3FYu+e/at8ZWDk2+NvS4xPnbuAusBmX67S+ezIhSuCzJ+NZbzb/nCVwE1LJjGuLz8D6qbaTsCQsSocFinawc4VZ+tgAacjwKjeKjUNh6RX2Jt6wPzDdBr6GHP51TB1gm7GjCXwZOHPtYmtg6UgBVRarHqv+/tZP1ZG0c7B+lj7lOwHKvUIysDPk3p4dGfy58OOL5i5Qxr6v3cAtkPExn9BrbOWTKmkV7Kk6wFycLWkfhSuyw7e1if8AOQRRRjV6ZjAAAAAASUVORK5CYII=&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;JavaScript&quot; title=&quot;JavaScript&quot; src=&quot;/static/3eeeb156fbfc3dd227fc278baabf2086/2bef9/thunderstorm_javascript_example.png&quot; srcSet=&quot;/static/3eeeb156fbfc3dd227fc278baabf2086/6f3f2/thunderstorm_javascript_example.png 256w,/static/3eeeb156fbfc3dd227fc278baabf2086/01e7c/thunderstorm_javascript_example.png 512w,/static/3eeeb156fbfc3dd227fc278baabf2086/2bef9/thunderstorm_javascript_example.png 1024w,/static/3eeeb156fbfc3dd227fc278baabf2086/71c1d/thunderstorm_javascript_example.png 1536w,/static/3eeeb156fbfc3dd227fc278baabf2086/a878e/thunderstorm_javascript_example.png 2048w,/static/3eeeb156fbfc3dd227fc278baabf2086/821da/thunderstorm_javascript_example.png 2200w&quot; sizes=&quot;(max-width: 1024px) 100vw, 1024px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
    &lt;/span&gt;
 &lt;/p&gt;&lt;h2 id=&quot;color-decisions&quot;&gt;Color Decisions&lt;/h2&gt;&lt;p&gt;Thunderstorm was designed to immediately highlight the important code while pushing the less important to the background giving a truly useful At-a-Glance overview of your code.&lt;/p&gt;&lt;p&gt;Class and function definitions are in a red to allow you to very quickly spot where one function ends and the next begins when scrolling.&lt;/p&gt;&lt;p&gt;All function calls are of the same light blue allowing them to pop out when viewing your code.&lt;/p&gt;&lt;p&gt;Variables, on the other hand, are pushed slightly to the background in a dark grey but the use of highlighted variable punctuation (the \$ sign in PHP) ensures variables are still quickly identifiable.&lt;/p&gt;&lt;p&gt;The decision was also made to emphasise control keywords (if, else, return) but ensuring the colour used was not too strong or contrasting against both the background and surrounding code. White was chosen given its neutrality ensuring it still caught your eye but didn’t vie too much for your attention.&lt;/p&gt;&lt;h2 id=&quot;installation&quot;&gt;Installation&lt;/h2&gt;&lt;h3 id=&quot;using-package-control&quot;&gt;Using Package Control&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;If you haven&amp;#x27;t already, install &lt;a href=&quot;https://packagecontrol.io/installation&quot;&gt;Sublime Package Control&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Bring up the Command Palette (Ctrl+Shift+P on Windows/Linux, ⌘+⇧+P on OS X) and type &lt;code class=&quot;language-text&quot;&gt;Install Package&lt;/code&gt;&lt;/li&gt;&lt;li&gt;Type &lt;code class=&quot;language-text&quot;&gt;Thunderstorm&lt;/code&gt; to bring the Color Scheme up in the packages list.&lt;/li&gt;&lt;li&gt;On installed, select &lt;code class=&quot;language-text&quot;&gt;Thunderstorm&lt;/code&gt; from Preferences -&amp;gt; Color Scheme -&amp;gt; Color Scheme - Thunderstorm to activate it.&lt;/li&gt;&lt;/ul&gt;&lt;h3 id=&quot;using-git&quot;&gt;Using Git&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;Clone the &lt;a href=&quot;https://github.com/39digits/thunderstorm-sublime-theme&quot;&gt;Thunderstorm GitHub repository&lt;/a&gt; to your local machine.&lt;/li&gt;&lt;li&gt;Bring up the Command Palette (Ctrl+Shift+P on Windows/Linux, ⌘+⇧+P on OS X) and type &lt;code class=&quot;language-text&quot;&gt;Browse Packages&lt;/code&gt; to open your Packages directory.&lt;/li&gt;&lt;li&gt;Copy the .tmTheme file to the &lt;code class=&quot;language-text&quot;&gt;Packages/User&lt;/code&gt; directory&lt;/li&gt;&lt;li&gt;Once installed, select &lt;code class=&quot;language-text&quot;&gt;Thunderstorm&lt;/code&gt; from Preferences -&amp;gt; Color Scheme -&amp;gt; User to activate it.&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;recommended-settings&quot;&gt;Recommended Settings&lt;/h2&gt;&lt;h3 id=&quot;monospace-font&quot;&gt;Monospace Font&lt;/h3&gt;&lt;p&gt;Browse the excellent &lt;a href=&quot;https://github.com/chrissimpkins/codeface&quot;&gt;codeface&lt;/a&gt; for fonts that make your source code look beautiful.&lt;/p&gt;&lt;p&gt;I recommend using one of the following fonts:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/adobe-fonts/source-code-pro&quot;&gt;Source Code Pro&lt;/a&gt; (my personal favourite)&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://www.marksimonson.com/fonts/view/anonymous-pro&quot;&gt;Anonymous Pro&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/tonsky/FiraCode&quot;&gt;Fira Code&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/chrissimpkins/codeface/tree/master/fonts/ubuntu-mono&quot;&gt;Ubuntu Mono&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h3 id=&quot;user-settings&quot;&gt;User Settings&lt;/h3&gt;&lt;p&gt;To get the absolute best visuals (as seen in the screenshots above), I recommend the following Sublime Text 3 User Settings:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;	&lt;span class=&quot;token property&quot;&gt;&amp;quot;caret_style&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;phase&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;&amp;quot;font_face&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;Source Code Pro&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;&amp;quot;font_options&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
		&lt;span class=&quot;token string&quot;&gt;&amp;quot;gray_antialias&amp;quot;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// For Retina screens&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;&amp;quot;font_size&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;&amp;quot;highlight_line&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;&amp;quot;line_padding_bottom&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;&amp;quot;line_padding_top&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&quot;ui-themes&quot;&gt;UI Themes&lt;/h3&gt;&lt;p&gt;Thunderstorm works well with the phenomenally awesome &lt;a href=&quot;http://equinusocio.github.io/material-theme/&quot;&gt;Material Design UI Theme&lt;/a&gt; for Sublime Text 3.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Build a Nintendo emulator using a Raspberry Pi 2]]></title><description><![CDATA[Today we'll build an extremely portable retro gaming console that emulates some classic consoles including Nintendo Entertainment System…]]></description><link>https://www.39digits.com/build-a-nintendo-emulator-using-a-raspberry-pi-2</link><guid isPermaLink="false">https://www.39digits.com/build-a-nintendo-emulator-using-a-raspberry-pi-2</guid><pubDate>Sun, 31 May 2015 18:58:34 GMT</pubDate><content:encoded>&lt;p&gt;Today we&amp;#x27;ll build an extremely portable retro gaming console that emulates some classic consoles including Nintendo Entertainment System (NES), SNES, Sega MegaDrive and others.&lt;/p&gt;&lt;p&gt;We will be using a Raspberry Pi 2 and the RetroPie software.&lt;/p&gt;&lt;h2 id=&quot;requirements&quot;&gt;Requirements&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;http://www.raspberrypi.org/products/raspberry-pi-2-model-b/&quot;&gt;Raspberry Pi 2&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Raspberry Pi 2 case&lt;/li&gt;&lt;li&gt;Micro SD Card (8Gb+ recommended)&lt;/li&gt;&lt;li&gt;USB controller (we’re using the excellent &lt;a href=&quot;http://www.nes30.com/&quot;&gt;NES30&lt;/a&gt; controller)&lt;/li&gt;&lt;li&gt;USB keyboard&lt;/li&gt;&lt;li&gt;FTP client&lt;/li&gt;&lt;li&gt;Ethernet cable (local network access only)&lt;/li&gt;&lt;li&gt;HDMI cable and TV/Monitor&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;step-by-step-guide-os-x&quot;&gt;Step-by-Step Guide (OS X)&lt;/h2&gt;&lt;h3 id=&quot;getting-the-software&quot;&gt;Getting the software&lt;/h3&gt;&lt;p&gt;Start by downloading the latest build of RetroPie from &lt;a href=&quot;http://blog.petrockblock.com/retropie/retropie-downloads/&quot;&gt;PetRockBlock.com&lt;/a&gt;. We&amp;#x27;ll save it to a &lt;code class=&quot;language-text&quot;&gt;~/Downloads/RetroPie&lt;/code&gt; directory on our MacBook Pro.&lt;/p&gt;&lt;p&gt;The latest stable release is v2.6 but we’ll be installing &lt;strong&gt;RetroPie 3.0 Beta 2&lt;/strong&gt;. Make sure that you download the correct build depending on whether you&amp;#x27;re using a Raspberry Pi 1 or 2.&lt;/p&gt;&lt;p&gt;The Raspberry Pi 2 is highly recommended for the extra CPU power which comes in handy to get emulation without lag and is a requirement for some of the more demanding consoles such as N64.&lt;/p&gt;&lt;p&gt;The RetroPie distributable is usually compressed so unpack the archive to a .img file after the download is complete.&lt;/p&gt;&lt;h3 id=&quot;installing-retropie&quot;&gt;Installing RetroPie&lt;/h3&gt;&lt;blockquote&gt;&lt;p&gt;If you aren&amp;#x27;t keen to use the command-line on OS X or perhaps use Windows instead there are some programs that can help with the progress.&lt;/p&gt;&lt;p&gt;For Windows use &lt;a href=&quot;http://sourceforge.net/projects/win32diskimager/&quot;&gt;Win32 Disk Imager&lt;/a&gt;&lt;/p&gt;&lt;p&gt;For OS X use &lt;a href=&quot;http://sourceforge.net/projects/piwriter/&quot;&gt;PiWriter&lt;/a&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Insert the Micro SD Card into your Mac and open up your Terminal program (I use iTerm2 but OS X comes with a built-in Terminal).&lt;/p&gt;&lt;p&gt;We need to start off by finding the device node of the Micro SD Card. Use a built-in command-line utility called &lt;code class=&quot;language-text&quot;&gt;diskutil&lt;/code&gt; to get a list of all current disks attached to your machine.&lt;/p&gt;&lt;p&gt;From the open Terminal window run:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;diskutil list&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1024px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:62.890625%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAIAAAAmMtkJAAAACXBIWXMAAAsTAAALEwEAmpwYAAABv0lEQVQoz5VQyW7bMBAd7ZZseUuvSXqyRGoXSUnU5vTjeuitsBOgKNDGho32N4uRnBXIocADMXicN2/mwddf3/eHPw/H8/3hvD/ge3/EYvd4+vbwc/d4Gvn9EflLfTjvfp9/nP6Cm+VWxA0/h88h3AZwQ+CGwi3FGhmKzPheP39RuPZnUQmrQtpJpQeFRoVGhZ3UTlprVKiUq4QPjFSpsGI5zRozKlXKNcqBsKtiCypBzTRr7EROYrkWWyetwcvBZ+AzlfBP1ZdZ1ixFf1XeKQRJxWfg5WvegxmW4DMzKp20XvJ+JbZ2Is2w1IMCvFwhfMG6OevcvEWrp6EXsZs1GhU4j3AzKmdZ67LWCEuVcNigyXS4wk7knHU48bUzNo3DfGYn0knrdXG34L2bNUZYOGm94J0RFnYi7UQqQ/MbMV7ijchhk405YWaE64FAcuAR79aeDQ6YLeUK4XYsJ3GljILBZBJL8JkVVdO0Md6trfhMD8Qklk4qV6JfF1s3b624ek57KXorruasXYle8d+mveCdk1aTuNKo0AOhDDurQ4QjFLwL8Zq8iOe8G2/DMLzLx0vTRxjFsMnh6cL/wCD+ByjIgvRKvyjdAAAAAElFTkSuQmCC&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;diskutil list output&quot; title=&quot;diskutil list output&quot; src=&quot;/static/eec681e284850ac071ff3b01f4cc36c1/2bef9/retropie_diskutil_list-2.png&quot; srcSet=&quot;/static/eec681e284850ac071ff3b01f4cc36c1/6f3f2/retropie_diskutil_list-2.png 256w,/static/eec681e284850ac071ff3b01f4cc36c1/01e7c/retropie_diskutil_list-2.png 512w,/static/eec681e284850ac071ff3b01f4cc36c1/2bef9/retropie_diskutil_list-2.png 1024w,/static/eec681e284850ac071ff3b01f4cc36c1/71c1d/retropie_diskutil_list-2.png 1536w,/static/eec681e284850ac071ff3b01f4cc36c1/ce0a7/retropie_diskutil_list-2.png 1590w&quot; sizes=&quot;(max-width: 1024px) 100vw, 1024px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
    &lt;/span&gt;&lt;/p&gt;&lt;p&gt;You’re looking for the entry that matches your Micro SD Card. In the screenshot we can see our SD Card is at &lt;code class=&quot;language-text&quot;&gt;/dev/disk2&lt;/code&gt;. Make note of the device node and be very sure you have the correct one.&lt;/p&gt;&lt;p&gt;Now that we have the device node, we need to unmount the SD Card by running:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;diskutil unmountdisk disk2&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Replace disk2 above with your own device node leaving off the &lt;code class=&quot;language-text&quot;&gt;/dev/&lt;/code&gt; prefix.&lt;/p&gt;&lt;p&gt;Still in the terminal, navigate to the directory to which you previously saved the RetroPie img file.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; ~/Downloads/RetroPie&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Next we’ll use a command to load the RetroPie image onto the Micro SD Card.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;dd&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;retropie-v3.0beta2-rpi2.img &lt;span class=&quot;token assign-left variable&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;/dev/rdisk2 &lt;span class=&quot;token assign-left variable&quot;&gt;bs&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;1m&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;if&lt;/strong&gt; = Input File&lt;/li&gt;&lt;li&gt;&lt;strong&gt;of&lt;/strong&gt; = Output File&lt;/li&gt;&lt;li&gt;&lt;strong&gt;bs&lt;/strong&gt; = Block Size&lt;/li&gt;&lt;li&gt;Make sure to replace &lt;strong&gt;retropie-v3.0beta-rpi2.img&lt;/strong&gt; with the actual name of the img file you previously uncompressed.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Note that we use &lt;code class=&quot;language-text&quot;&gt;/dev/rdisk2&lt;/code&gt; instead of &lt;code class=&quot;language-text&quot;&gt;/dev/disk2&lt;/code&gt;. Specifying rdisk writes directly to the disk and skips the write to buffer which speeds up the entire copying process.&lt;/p&gt;&lt;p&gt;It’s important you specify the correct output location or you may overwrite data on your primary hard drive and you really don&amp;#x27;t want to make that mistake!&lt;/p&gt;&lt;p&gt;When you hit Enter the copy process will start running but it won’t show any output to the terminal window. It can take anywhere between 5 and 20 minutes to complete so be patient.&lt;/p&gt;&lt;p&gt;If you’re concerned the process is taking too long, you can hit &lt;code class=&quot;language-text&quot;&gt;Ctrl-T&lt;/code&gt; to get the current latest transfer data output to screen. You can do this as often as you want until the image copy is complete.&lt;/p&gt;&lt;p&gt;&lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1024px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:63.28125%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAIAAAAmMtkJAAAACXBIWXMAAAsTAAALEwEAmpwYAAABuElEQVQoz3WS2Y7aMBSGTVYTYCiLettdBCdxFq9J2NTXq9SbSgNtxVQFRJgyfcfK9mjQqIP0y/JyPh+f3wd82X273Tebg9L3w+nu/uHX/cPm0Kz3x68/tre7491JLben8+/z35/Nn/XhuNk3612zbc4AfMjB2xi8U7LCwkasm1V+LGxEwcesFRYQS/Apc0PiIAreYxMJ3kS9tAQWotaUtrQsREFI3Jj7ifQTaSYWYuo0JCAkrZCYSBCSEV+Cbl67EX/a8hPpJQJMCi0CJoUXC32jaOPSibi5AkyKIVsACzEn4hCX7bTs08WQLXvFzEsk0KksxAZs6SXyhsyHfGESXOBOJiAWEEsn4l4ibMSMDAxCYiNmTamNqBNdNh/hseQDVo/F6obMTWYfq2rV459C/5eBe0XdzatOVkFcerFwIu5G3I35szzX4D6dvS4/j7guNdZW6bNnugaPpXjF6j6dw0T28rqb10FaQiy9WHiJ0H8mrsIjUUEs26lyO0jV+4O0Ch7H0o2FPWVX4QFbjcSqk1emWu25cl4tVZ9dOuQFGGLZzWftVBumuyrIStNhQVbBtOxkqotehP8BG+yGEv8IF/MAAAAASUVORK5CYII=&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;output of dd command&quot; title=&quot;output of dd command&quot; src=&quot;/static/18c7f895f804010544cbfc469cc3f1bb/2bef9/retropie_dd_command-1.png&quot; srcSet=&quot;/static/18c7f895f804010544cbfc469cc3f1bb/6f3f2/retropie_dd_command-1.png 256w,/static/18c7f895f804010544cbfc469cc3f1bb/01e7c/retropie_dd_command-1.png 512w,/static/18c7f895f804010544cbfc469cc3f1bb/2bef9/retropie_dd_command-1.png 1024w,/static/18c7f895f804010544cbfc469cc3f1bb/71c1d/retropie_dd_command-1.png 1536w,/static/18c7f895f804010544cbfc469cc3f1bb/ce0a7/retropie_dd_command-1.png 1590w&quot; sizes=&quot;(max-width: 1024px) 100vw, 1024px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
    &lt;/span&gt;&lt;/p&gt;&lt;p&gt;Once it’s complete verify the install by running diskutil list again. Under &lt;code class=&quot;language-text&quot;&gt;/dev/disk2&lt;/code&gt; you should now see the installation of a boot partition as well as a Linux partition sized at approximately 3.5Gb. Don&amp;#x27;t be too alarmed at the 3.5Gb size as by default it won’t have used the entirety of the SD Card’s available space - this is something we’ll correct once we’ve booted up the Raspberry Pi.&lt;/p&gt;&lt;h3 id=&quot;setup-the-raspberry-pi-hardware&quot;&gt;Setup the Raspberry Pi Hardware&lt;/h3&gt;&lt;p&gt;&lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1000px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:66.796875%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAECA//EABYBAQEBAAAAAAAAAAAAAAAAAAIDBP/aAAwDAQACEAMQAAAB22l5KWIC/8QAGhAAAgMBAQAAAAAAAAAAAAAAAAECAxESIv/aAAgBAQABBQK2zgh7rJLRLIH/xAAWEQADAAAAAAAAAAAAAAAAAAABEBH/2gAIAQMBAT8BhX//xAAXEQEAAwAAAAAAAAAAAAAAAAABAhBB/9oACAECAQE/ARjtf//EABoQAAICAwAAAAAAAAAAAAAAAAAQAUEREiH/2gAIAQEABj8Cgza6a0v/xAAZEAACAwEAAAAAAAAAAAAAAAABEQAQIZH/2gAIAQEAAT8hNkAXHRwuUGWBp//aAAwDAQACAAMAAAAQbw//xAAXEQEAAwAAAAAAAAAAAAAAAAABEBEh/9oACAEDAQE/EG1RyP/EABYRAQEBAAAAAAAAAAAAAAAAAAEQIf/aAAgBAgEBPxA4A2f/xAAaEAADAQEBAQAAAAAAAAAAAAAAAREhQTGx/9oACAEBAAE/EO8haR3JJxPr6UnxYKZVOmpd33RH/9k=&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;Raspberry Pi 2 in a NES case&quot; title=&quot;Raspberry Pi 2 in a NES case&quot; src=&quot;/static/2fdfdea6f0ff4b4c8a6c6debedc0ab80/a2510/retropie-nes-how-to-5.jpg&quot; srcSet=&quot;/static/2fdfdea6f0ff4b4c8a6c6debedc0ab80/e4a55/retropie-nes-how-to-5.jpg 256w,/static/2fdfdea6f0ff4b4c8a6c6debedc0ab80/36dd4/retropie-nes-how-to-5.jpg 512w,/static/2fdfdea6f0ff4b4c8a6c6debedc0ab80/a2510/retropie-nes-how-to-5.jpg 1000w&quot; sizes=&quot;(max-width: 1000px) 100vw, 1000px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
    &lt;/span&gt;
We are now ready to setup the Raspberry Pi itself.&lt;/p&gt;&lt;p&gt;If you haven&amp;#x27;t already, install the Raspberry Pi 2 into a case and plug in the keyboard, USB Controller, network cable and Micro SD Card. Plug it into your TV/monitor via HDMI and turn it on.&lt;/p&gt;&lt;p&gt;You will be greeted with a Welcome screen and it should show ==1 Gamepad Detected==. At this point, it will ask you to “Hold a button on your device to configure it”. This process sets up the controller to allow you to navigate the menus of the RetroPie UI. We&amp;#x27;ll still need to setup the controller to be recognised within the emulators themselves (but more on that later).&lt;/p&gt;&lt;h3 id=&quot;first-time-configuration&quot;&gt;First-time configuration&lt;/h3&gt;&lt;p&gt;Once the system has booted and you&amp;#x27;re inside the GUI, select ==RetroPie== from the main menu (you may need to hit right on the controller a few times).&lt;/p&gt;&lt;p&gt;Select ==Raspberry Pi Configuration Tool Raspi-Config== from the list of options. At this point it will boot the Raspberry Pi Config utility.&lt;/p&gt;&lt;p&gt;&lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:710px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:60.15625%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAIAAADtbgqsAAAACXBIWXMAAAsTAAALEwEAmpwYAAACVUlEQVQoz42LW0/TABiGe2mGt5rd+nf0VuEHaIKTkKBABg5jDCqOsTYchOA4bqVr19YKo90G3QbtDj1s7Q5sbXUr69icEH+Al6aA6KXJky/f+3zfC/i2yGmSh8jCHCn5SfmG90jm9WbyLczNhLPgHzn3dymCGAc4Hsduu0THIO94mrlm0J59rmzfs1yfK+dwZa/M9cmGvzUk3nlCAJ5PET+V8hGsl2DnwnEwRINYAoQZPxLz79AQwkBoHArHQSQGIrFZnJ0lWC9+CFFplzcAFFPrF1vT7Xl3Z8VzNu/ufRj54XvRm33e8450ofGzxYnOwkR33t1dmuwsuDtLk53Fie76m19nDBz0AWk2qPIoH9+SUjCf2BbYkHKEGBJhKpTChbOHQT6xLaXhfDIkpmAhGcqzoQKHfG8kVjdA4DgZrBZIMYNKWUwv7+qVXaOya2q0ZTDlAinnMFXEFRGXc5icjwgZVMygRSFy3owHrsolIZI72inmMV2lrPr+aS1qnkSt+n5ZwDNpuJBFheMdgUMkPlwScUOlDJW6MBN2mUsGdZUqi3hVJk4KpHkSbdWjpzV76iqlCnhZwisSXiuSFYnQlM+6Sn0tf7ks+wE+DfeaB5bGtHUbs0Yblb2WRlsaY12athFr1elGNWrpjC015rRG/2yxa5sQwB2statEQ0FNFTNLWEPBNAlpqhGzFDFVrHnJtyKqy0jTfrB9Q8HOdSoQmAEejC8OTK088ixf0e9Z7v8nXsspm5v48OXHgVer94ffAc6hA+eY4hyVnKPyfyPdHVPvDe/9BrzQ1RJxoGLsAAAAAElFTkSuQmCC&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;raspi-config tool&quot; title=&quot;raspi-config tool&quot; src=&quot;/static/5f0b7afe16f4b849ddfc2b1b100e6d69/7131f/retropie_raspi_config.png&quot; srcSet=&quot;/static/5f0b7afe16f4b849ddfc2b1b100e6d69/6f3f2/retropie_raspi_config.png 256w,/static/5f0b7afe16f4b849ddfc2b1b100e6d69/01e7c/retropie_raspi_config.png 512w,/static/5f0b7afe16f4b849ddfc2b1b100e6d69/7131f/retropie_raspi_config.png 710w&quot; sizes=&quot;(max-width: 710px) 100vw, 710px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
    &lt;/span&gt;&lt;/p&gt;&lt;p&gt;First, select ==Overclock== - you will need to use your keyboard at this config menu. You will see a message that overclocking may reduce the life of your Raspberry Pi. You may ignore this as we&amp;#x27;re just ensuring we&amp;#x27;ve selected the correct high clock speed to take advantage of all the new and more powerful Raspberry Pi 2 offers.&lt;/p&gt;&lt;p&gt;On the &amp;#x27;Choose overclock preset&amp;#x27; screen select ==Pi2== and press Enter. You will now be back at the main Raspberry Pi Config menu&lt;/p&gt;&lt;p&gt;Second, select ==Expand Filesystem== to kick off the process to use all of the space on your Micro SD Card. By default it will only use the first 4Gb of space. We&amp;#x27;re using a 32Gb card so we&amp;#x27;ll definitely want to expand the filesystem to take full advantage of all of that space.&lt;/p&gt;&lt;p&gt;Once the expansion has compelted, select ==Finish== from the bottom of the Raspberry Pi Config screen. You will be asked if you wish to Reboot - select Yes.&lt;/p&gt;&lt;h3 id=&quot;setup-the-usb-controllers&quot;&gt;Setup the USB Controllers&lt;/h3&gt;&lt;p&gt;&lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1000px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:66.796875%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAQCA//EABYBAQEBAAAAAAAAAAAAAAAAAAIBA//aAAwDAQACEAMQAAABscc4K5GN/8QAGhABAQACAwAAAAAAAAAAAAAAAgEAAxEhIv/aAAgBAQABBQJ7SM1urOcvdRlp8n//xAAVEQEBAAAAAAAAAAAAAAAAAAAAEf/aAAgBAwEBPwGI/8QAFxEAAwEAAAAAAAAAAAAAAAAAAAECEv/aAAgBAgEBPwFWjR//xAAZEAADAAMAAAAAAAAAAAAAAAAAAREQIUH/2gAIAQEABj8CnTaixWish//EABkQAQEAAwEAAAAAAAAAAAAAAAEAETFBUf/aAAgBAQABPyFcWVaCXXwLD0gABTV1RDAeX//aAAwDAQACAAMAAAAQhO//xAAVEQEBAAAAAAAAAAAAAAAAAAABEP/aAAgBAwEBPxBc/wD/xAAYEQEBAAMAAAAAAAAAAAAAAAABABEhMf/aAAgBAgEBPxDgSXnV/8QAGxABAAMBAQEBAAAAAAAAAAAAAQARMSFhcYH/2gAIAQEAAT8QcsOwb+zl7espHPfst6MeM10CS6gOxBR3JeRQ0LP/2Q==&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;NES30 USB Controller&quot; title=&quot;NES30 USB Controller&quot; src=&quot;/static/269a63ab65f29c6e00a163ca4a96b8a4/a2510/retropie-nes-how-to-8.jpg&quot; srcSet=&quot;/static/269a63ab65f29c6e00a163ca4a96b8a4/e4a55/retropie-nes-how-to-8.jpg 256w,/static/269a63ab65f29c6e00a163ca4a96b8a4/36dd4/retropie-nes-how-to-8.jpg 512w,/static/269a63ab65f29c6e00a163ca4a96b8a4/a2510/retropie-nes-how-to-8.jpg 1000w&quot; sizes=&quot;(max-width: 1000px) 100vw, 1000px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
    &lt;/span&gt;
Earlier when you booted up the RetroPie for the first time it detected a controller and asked you to setup the inputs.&lt;/p&gt;&lt;p&gt;This setup was to enable the controller for navigating the RetroPie&amp;#x27;s UI. We will now go through the process of ensuring our controllers are properly recognised for use within the Emulators themselves.&lt;/p&gt;&lt;p&gt;We have a NES30 Controller which is styled like the old NES controller but has the same number of buttons as the SNES controller (including two shoulder buttons).&lt;/p&gt;&lt;p&gt;As before, select ==RetroPie== from the UI after booting up but this time select ==Configure RetroArch Keyboard/Joystick==. You will need the keyboard again at this point so use to to select ==Configure controller for use with RetroArch==.&lt;/p&gt;&lt;p&gt;This will boot a setup script asking you to press the corresponding button shown on the screen. You have to be quite quick as the delay to press a button isn&amp;#x27;t very long. If you make a mistake you can just re-run the setup script.&lt;/p&gt;&lt;p&gt;Note: Your controller might not have all of the inputs - for example our NES30 does not have any analog sticks. You can safely ignore and wait until the script reaches completion.&lt;/p&gt;&lt;p&gt;Once you have completed the setup for the connected controller you will find yourelf back at the RetroPie UI.&lt;/p&gt;&lt;p&gt;Repeat the above process for any new controller you may want to use on the RetroPie in the future. We used a NES30 controller since our interest was in NES and SNES games but you can plug a PS4 controller into the console and use that too if you plan to play games requiring analog sticks.&lt;/p&gt;&lt;h3 id=&quot;transfer-some-roms&quot;&gt;Transfer some ROMs&lt;/h3&gt;&lt;p&gt;Time to transfer some games across!&lt;/p&gt;&lt;p&gt;First off get the IP address of your RetroPie by loading up Terminal and pinging it with the following command:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;ping&lt;/span&gt; retropie&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Load your favourite FTP client (I use &lt;a href=&quot;https://panic.com/transmit/&quot;&gt;Transmit&lt;/a&gt; but feel free to use any others such as &lt;a href=&quot;https://cyberduck.io/&quot;&gt;CyberDuck&lt;/a&gt;).&lt;/p&gt;&lt;p&gt;Connect via SFTP to the Raspberry Pi’s IP address using the username &lt;code class=&quot;language-text&quot;&gt;pi&lt;/code&gt; and password &lt;code class=&quot;language-text&quot;&gt;raspberry&lt;/code&gt;. This will connect you to the &lt;code class=&quot;language-text&quot;&gt;/home/RetroPie&lt;/code&gt; directory.&lt;/p&gt;&lt;p&gt;All ROMs go into the &lt;code class=&quot;language-text&quot;&gt;/home/RetroPie/roms&lt;/code&gt; directory under the relevant emulator’s directory.&lt;/p&gt;&lt;p&gt;In earlier versions, RetroPie sometimes has multiple emulators for the same device but the ROMs directory has been tidied up in 3.x&lt;/p&gt;&lt;p&gt;We&amp;#x27;ll be uploading the excellent Super Mario Bros to the &lt;code class=&quot;language-text&quot;&gt;/home/RetroPie/roms/nes&lt;/code&gt; directory to get ready to stomp some Goombas.&lt;/p&gt;&lt;p&gt;Once you&amp;#x27;ve completed and uploaded all of your games onto the Raspberry Pi, you will need to reboot.&lt;/p&gt;&lt;p&gt;Reboot the RetroPie by pressing Start on your controller and selecting Quit -&amp;gt; Restart System. When the RetroPie starts back up you should find an entry in the menu for all of the consoles for which you have uploaded ROMs.&lt;/p&gt;&lt;h3 id=&quot;best-nes-settings&quot;&gt;Best NES Settings&lt;/h3&gt;&lt;p&gt;We have found the following to be the best settings when running NES games to reduce input lag as much as possible.&lt;/p&gt;&lt;p&gt;Now open a Terminal window and ssh into your RetroPie by running:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;ssh&lt;/span&gt; pi@retropie&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once connected you will need to edit the NES retroarch.cfg file located at &lt;code class=&quot;language-text&quot;&gt;/opt/retropie/configs/nes/retroarch.cfg&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Replace the contents of that file with the following configuration:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;vim&quot;&gt;&lt;pre class=&quot;language-vim&quot;&gt;&lt;code class=&quot;language-vim&quot;&gt;#&lt;span class=&quot;token builtin&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;/opt/retropie/configs/all/retroarch.cfg&amp;quot;&lt;/span&gt;
# All settings made here will override the global settings &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; the current emulator core
input_remapping_directory &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;opt&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;retropie&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;configs&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;nes&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;
video_shader &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;opt&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;retropie&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;emulators&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;retroarch&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;shader&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;phosphor&lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt;glslp
video_shader_enable &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; false
video_smooth &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; false

video_vsync &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; true
video_hard_sync &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; false
video_hard_sync_frames &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
# video_frame_delay &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
# video_black_frame_insertion &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; false
video_threaded &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; false
video_scale_integer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; false
video_crop_overscan &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; false&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you&amp;#x27;re keen to experiment you can find the full list of configration options in &lt;code class=&quot;language-text&quot;&gt;/opt/retropie/configs/all/retroarch.cfg&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Lastly (and you&amp;#x27;ll need the USB keyboard plugged in for this bit), switch back to your RetroPie itself and start up your favourite NES title (Super Mario, right?). Press &lt;code class=&quot;language-text&quot;&gt;x&lt;/code&gt; before the game boots to get to the emulator config menu. Inside here we found the best settings for NES emulation was to use ==lr-fceumm== as the emulator using a video mode of ==CEA-1== (640x480 without any enhancements).&lt;/p&gt;&lt;p&gt;The good news is that all of the above will only need to be done once and will be active permanently.&lt;/p&gt;&lt;p&gt;Do you have better configuration options to get the emulation as close to the real NES as possible? Please leave recommendations in the comments below.&lt;/p&gt;&lt;h3 id=&quot;some-handy-tips&quot;&gt;Some handy tips&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;IMPORTANT TIP&lt;/strong&gt;: Make sure you are using the correct power output on the Micro USB (5V/2.1A) otherwise the Raspberry Pi 2 won&amp;#x27;t get enough to take advantage of all that extra power. When I accidentally used a 5V/1A Star Fox on SNES ran at horrendous speeds but using the correct power was 100% speed emulation.&lt;/li&gt;&lt;li&gt;As already stated, press &lt;code class=&quot;language-text&quot;&gt;x&lt;/code&gt; on a plugged in keyboard when starting a game to get to a special menu. You can use this to tweak the resolution and visual effects to optimise the emulator&amp;#x27;s settings to get the best performance.&lt;/li&gt;&lt;li&gt;When inside a game, press &lt;code class=&quot;language-text&quot;&gt;Start + Select&lt;/code&gt; to quit back to the main RetroPie menu.&lt;/li&gt;&lt;li&gt;To safely turn off your RetroPie, press &lt;code class=&quot;language-text&quot;&gt;start&lt;/code&gt; on the main RetroPie menu to Quit -&amp;gt; Shutdown System when you&amp;#x27;re done playing.&lt;/li&gt;&lt;/ul&gt;&lt;h3 id=&quot;the-final-nes-emulator&quot;&gt;The final NES emulator&lt;/h3&gt;&lt;p&gt;&lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position:relative;display:block;margin-left:auto;margin-right:auto;max-width:1000px&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom:66.796875%;position:relative;bottom:0;left:0;background-image:url(&amp;#x27;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAQBAgP/xAAWAQEBAQAAAAAAAAAAAAAAAAACAAP/2gAMAwEAAhADEAAAAWq5zg3RcN//xAAZEAACAwEAAAAAAAAAAAAAAAABAgADERD/2gAIAQEAAQUCezILWmxqwxKDn//EABYRAQEBAAAAAAAAAAAAAAAAAAAREv/aAAgBAwEBPwHKP//EABcRAQADAAAAAAAAAAAAAAAAAAABEhP/2gAIAQIBAT8B0hZ//8QAGRAAAgMBAAAAAAAAAAAAAAAAECEAARJR/9oACAEBAAY/AlKWmeD/xAAbEAACAgMBAAAAAAAAAAAAAAABEQAhMUFRYf/aAAgBAQABPyHe4zcXIKKFWonYyFvyFCmXYzsuf//aAAwDAQACAAMAAAAQqN//xAAXEQADAQAAAAAAAAAAAAAAAAAAARFB/9oACAEDAQE/EG2En//EABcRAQEBAQAAAAAAAAAAAAAAAAEAEUH/2gAIAQIBAT8QMcSXy//EABsQAQACAwEBAAAAAAAAAAAAAAEAESExQVFx/9oACAEBAAE/EGbU54kr7hiENntBreCXwWrDaquALg4d577DoJ2s/9k=&amp;#x27;);background-size:cover;display:block&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;A NES RetroPie emulator&quot; title=&quot;A NES RetroPie emulator&quot; src=&quot;/static/03b20c90c1a22be9047b5b2700a7289a/a2510/retropie-nes-how-to-7.jpg&quot; srcSet=&quot;/static/03b20c90c1a22be9047b5b2700a7289a/e4a55/retropie-nes-how-to-7.jpg 256w,/static/03b20c90c1a22be9047b5b2700a7289a/36dd4/retropie-nes-how-to-7.jpg 512w,/static/03b20c90c1a22be9047b5b2700a7289a/a2510/retropie-nes-how-to-7.jpg 1000w&quot; sizes=&quot;(max-width: 1000px) 100vw, 1000px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
    &lt;/span&gt;&lt;/p&gt;&lt;p&gt;You should now have a fully working mini console to play all the games you loved as a kid. We&amp;#x27;ve only loaded up games we already own the cartridges to with Super Mario Bros and Zelda taking the top Must Play Again spot.&lt;/p&gt;&lt;p&gt;Unplug that clunky USB keyboard as only the controller is required from this point onwards.&lt;/p&gt;&lt;p&gt;Happy Gaming!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Third-party USB LAN Adapter for Wii U]]></title><description><![CDATA[Splatoon is out and with it came my first requirement for some stable Internet connectivity from the Wii U. I was experiencing too many…]]></description><link>https://www.39digits.com/third-party-usb-lan-adapter-for-wii-u</link><guid isPermaLink="false">https://www.39digits.com/third-party-usb-lan-adapter-for-wii-u</guid><pubDate>Sun, 31 May 2015 18:58:34 GMT</pubDate><content:encoded>&lt;p&gt;Splatoon is out and with it came my first requirement for some stable Internet connectivity from the Wii U.&lt;/p&gt;&lt;p&gt;I was experiencing too many network connection errors while running off of WiFi that I decided to wire it up using trusty Ethernet cables. The only snag in that grand plan is that the Wii U doesn&amp;#x27;t have an Ethernet Port built-in - you require a USB LAN Adapter.&lt;/p&gt;&lt;p&gt;The only problem is that Nintendo have stopped producing the official LAN Adapters back in March 2015 and they&amp;#x27;re pretty tricky to find. Thankfully you can use a third-party USB LAN Adapter if it shares the same chipset (AX88772A).&lt;/p&gt;&lt;p&gt;This &lt;a href=&quot;http://www.maplin.co.uk/p/usb-20-to-ethernet-adapter-a31gy&quot;&gt;USB 2.0 to Ethernet Adapter&lt;/a&gt; at Maplin is the one I bought and can verify it works.&lt;/p&gt;&lt;p&gt;Avoid the temptation to get their USB 3.0 Gigabit adapter. I bought that one first and it wasn&amp;#x27;t recognised by the Wii U.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Create a Minecraft Server on DigitalOcean]]></title><description><![CDATA[This post was edited on 31 May 2015 to switch to using screen for spawning an instance of the Minecraft Server. Previously it created a…]]></description><link>https://www.39digits.com/create-a-minecraft-server-on-digitalocean</link><guid isPermaLink="false">https://www.39digits.com/create-a-minecraft-server-on-digitalocean</guid><pubDate>Fri, 12 Sep 2014 19:53:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;&lt;p&gt;This post was edited on 31 May 2015 to switch to using screen for spawning an instance of the Minecraft Server. Previously it created a startup script but that meant it wasn&amp;#x27;t possible to easily switch back into the running console.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Today we&amp;#x27;ll go through some steps to create your own MineCraft Server. We&amp;#x27;ll install &lt;a href=&quot;http://minecraft.net&quot;&gt;Minecraft&lt;/a&gt; Server 1.8 onto a very basic 512Mb &lt;a href=&quot;http://digitalocean.com&quot;&gt;DigitalOcean&lt;/a&gt; Droplet.&lt;/p&gt;&lt;h2 id=&quot;setup-pre-requisites&quot;&gt;Setup Pre-requisites&lt;/h2&gt;&lt;p&gt;Start by creating a new &lt;strong&gt;512Mb Droplet with Ubuntu 14.04&lt;/strong&gt;. We won&amp;#x27;t need to use any of the Application droplets so make sure you use the Linux-only image.&lt;/p&gt;&lt;p&gt;Wait a shockingly fast 55 seconds and then SSH into your brand new Ubuntu Droplet.&lt;/p&gt;&lt;p&gt;First lets update the package list on our fresh Ubuntu installation:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;apt-get&lt;/span&gt; update&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And then upgrade to the latest version of all installed packages.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;apt-get&lt;/span&gt; upgrade&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Our next step on the box is to &lt;strong&gt;install Java&lt;/strong&gt;. We won&amp;#x27;t be running a Java application with a full GUI so we can get away with installing the &lt;code class=&quot;language-text&quot;&gt;default-jre-headless&lt;/code&gt; package. JRE stands for Java Runtime Environment used to run the Java application, while headless refers to the lack of any GUI libraries.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;apt-get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; default-jre-headless&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Our last pre-requisite is to install &lt;code class=&quot;language-text&quot;&gt;screen&lt;/code&gt; which will be used when starting the Minecraft server.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;apt-get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;screen&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note: You might need to run each command with &lt;em&gt;sudo&lt;/em&gt; at the start of the command due to permissions, but on a new droplet you should have the right privileges.&lt;/p&gt;&lt;h2 id=&quot;create-a-minecraft-system-user&quot;&gt;Create a minecraft system user&lt;/h2&gt;&lt;p&gt;Next up we create a Linux &lt;em&gt;user&lt;/em&gt; and &lt;em&gt;group&lt;/em&gt; to own the Java process when running the Minecraft Server.&lt;/p&gt;&lt;p&gt;Start off by creating a directory for Minecraft itself.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;mkdir&lt;/span&gt; /srv/minecraft&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then create the Linux group.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;addgroup --system minecraft&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now to actually create the user run the following on the command line (note that it&amp;#x27;s all one line):&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;adduser --system --no-create-home --home /srv/minecraft --ingroup minecraft minecraft&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The user&amp;#x27;s home is set to &lt;code class=&quot;language-text&quot;&gt;/srv/minecraft&lt;/code&gt; as srv is the prescribed location for site-specific data which are served by the system. Another popular location may be &lt;code class=&quot;language-text&quot;&gt;/opt/minecraft&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;--ingroup minecraft&lt;/code&gt; flag will automatically add the new user to the minecraft group created the line above.&lt;/p&gt;&lt;h2 id=&quot;install-minecraft-server&quot;&gt;Install Minecraft Server&lt;/h2&gt;&lt;p&gt;Change to the minecraft directory.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; /srv/minecraft&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And download the MineCraft Server package (note that the command is all one line).&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;wget&lt;/span&gt; https://s3.amazonaws.com/Minecraft.Download/versions/1.8/minecraft_server.1.8.jar -O minecraft_server.jar&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&quot;configure-the-minecraft-server&quot;&gt;Configure the Minecraft Server&lt;/h2&gt;&lt;p&gt;Start our MineCraft server.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;java -Xms256M -Xmx450M -jar minecraft_server.jar nogui&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We only have 512Mb RAM so we have to be a bit conservative. The first parameter starts the process with 256Mb of RAM and sets an upper limit of 450Mb through the second parameter.&lt;/p&gt;&lt;p&gt;MineCraft will try to start and since it&amp;#x27;s the first launch it will generate a &lt;code class=&quot;language-text&quot;&gt;server.properties&lt;/code&gt; file with some sane defaults. It will, however, fail as we haven&amp;#x27;t yet agreed to the EULA (End-User License Agreement). We need to edit the newly generated &lt;code class=&quot;language-text&quot;&gt;eula.txt&lt;/code&gt; file and set the value to &lt;code class=&quot;language-text&quot;&gt;TRUE&lt;/code&gt; (You may need to type &lt;em&gt;/stop&lt;/em&gt; to shutdown the Minecraft Server console).&lt;/p&gt;&lt;p&gt;So let us edit that file now:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;vi&lt;/span&gt; eula.txt&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once you&amp;#x27;ve changed &lt;em&gt;false&lt;/em&gt; to &lt;em&gt;true&lt;/em&gt;, save the file and exit the editor.&lt;/p&gt;&lt;p&gt;Now that you&amp;#x27;ve accepted the EULA, start MineCraft Server up again:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;java -Xms256M -Xmx450M -jar minecraft_server.jar nogui&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This time it will create the &lt;code class=&quot;language-text&quot;&gt;banned-players.json&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;banned-ips.json&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;ops.json&lt;/code&gt;, and &lt;code class=&quot;language-text&quot;&gt;whitelist.json&lt;/code&gt; files.&lt;/p&gt;&lt;p&gt;These files allow you to add users to the different roles. The banned files allow you to ban users or IPs from accessing your instance. The ops file allows you to set &amp;quot;administrators&amp;quot; while whitelist is used if you only want to allow a specified list of users to access the instance at all.&lt;/p&gt;&lt;p&gt;We&amp;#x27;ll add our own Minecraft user to the ops and whitelists within Minecraft Server. I particularly like to keep a strict whitelist to ensure random users don&amp;#x27;t discover and use the Minecraft Server (especially because it&amp;#x27;s on such a modest setup).&lt;/p&gt;&lt;p&gt;To add yourself to the ops and whitelist files run the following while Minecraft is running (but obviously replacing &lt;em&gt;YourMojangUsernameHere&lt;/em&gt; with your own username):&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;/op YourMojangUsernameHere
/whitelist &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt; YourUserNameHere
/whitelist on&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In future, you may add friends to the whitelist by using the &lt;code class=&quot;language-text&quot;&gt;/whitelist add YourFriendsUserName&lt;/code&gt; command from within Minecraft client itself so you won&amp;#x27;t always need server SSH access. Ahh, the power of being an Admin!&lt;/p&gt;&lt;p&gt;Quit from the Minecraft console by typing &lt;code class=&quot;language-text&quot;&gt;stop&lt;/code&gt; to return to the command prompt.&lt;/p&gt;&lt;p&gt;Next up we edit &lt;code class=&quot;language-text&quot;&gt;server.properties&lt;/code&gt; that was generated and change the value for &lt;code class=&quot;language-text&quot;&gt;max-players&lt;/code&gt; from 20 to a more reasonable 5 for our modest server specs.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;vi&lt;/span&gt; server.properties&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Feel free to explore the properties set in this file to get an understanding for what all can be tweaked. I like to change the &lt;code class=&quot;language-text&quot;&gt;motd&lt;/code&gt; (Message of the Day) of the server from the default &amp;quot;A Minecraft Server&amp;quot; to something more personal but that&amp;#x27;s purely optional.&lt;/p&gt;&lt;p&gt;Once you&amp;#x27;ve changed at least the maximum number of players to a sensible 5, save the file and exit the editor.&lt;/p&gt;&lt;p&gt;At this point a lot of files have been created in &lt;code class=&quot;language-text&quot;&gt;/srv/minecraft&lt;/code&gt; under your logged in user, but we&amp;#x27;ll want them to be owned by the minecraft user and group.&lt;/p&gt;&lt;p&gt;To do this we have to run:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;chown&lt;/span&gt; -R minecraft:minecraft /srv/minecraft&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&quot;some-theory-on-the-screen-utility&quot;&gt;Some theory on the screen utility&lt;/h2&gt;&lt;p&gt;Screen is a handy command-line utility that allows you to create detachable instances of your shell called screens.&lt;/p&gt;&lt;p&gt;Think of a screen on the command-line in a similar fashion to tabs in your browser. You can load a website and then switch to another tab to continue doing something else. Screens in Linux allow you to do the same thing.&lt;/p&gt;&lt;p&gt;To start a new screen you would type:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;screen&lt;/span&gt; -S screen_name &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;command&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Replace &lt;code class=&quot;language-text&quot;&gt;screen_name&lt;/code&gt; with a friendly name to identify it if you run multiple screen instances (not something we will do though).&lt;/p&gt;&lt;p&gt;Likewise, replace &lt;code class=&quot;language-text&quot;&gt;[command]&lt;/code&gt; with any command you wish to run when creating a new screen. The command is optional but it allows us to immediately spawn a Minecraft Server instance without having to manually type the java command.&lt;/p&gt;&lt;p&gt;To see a list of all running screen instances, run:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;screen&lt;/span&gt; -list&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To switch back to a running screen (called reattaching), run:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;screen&lt;/span&gt; -r screen_name&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will &amp;quot;tab&amp;quot; you back into the relevant shell instance.&lt;/p&gt;&lt;p&gt;If you&amp;#x27;re inside a screen instance and want to return to the main commandline, press &lt;code class=&quot;language-text&quot;&gt;Ctrl-A&lt;/code&gt; followed by &lt;code class=&quot;language-text&quot;&gt;d&lt;/code&gt;. You won&amp;#x27;t see anything on screen when you first press &lt;code class=&quot;language-text&quot;&gt;Ctrl-A&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;If you want to kill the screen instance, press &lt;code class=&quot;language-text&quot;&gt;Ctrl-d&lt;/code&gt; from within it. But since we&amp;#x27;ll be running a Minecraft instance, we&amp;#x27;ll need merely run &lt;code class=&quot;language-text&quot;&gt;stop&lt;/code&gt; in the Minecraft console instead.&lt;/p&gt;&lt;h2 id=&quot;create-a-minecraft-startup-script&quot;&gt;Create a Minecraft startup script&lt;/h2&gt;&lt;p&gt;Now that the initial setup is complete, we won&amp;#x27;t be starting Minecraft Server using the manual java line anymore. Instead we&amp;#x27;ll create a Linux shell script to start our Minecraft Server using &lt;code class=&quot;language-text&quot;&gt;screen&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;This way you can always SSH into your Minecraft Server and switch to the Minecraft screen to run some handy Admin commands within the console.&lt;/p&gt;&lt;p&gt;Start off by ensuring we&amp;#x27;re in the right directory:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; /srv/minecraft&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Create the shell script by running:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;vi&lt;/span&gt; start.sh&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And save the contents as:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;vim&quot;&gt;&lt;pre class=&quot;language-vim&quot;&gt;&lt;code class=&quot;language-vim&quot;&gt;#&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;bin&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;bash

# Some reminder text &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; the user&lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt;
echo &lt;span class=&quot;token string&quot;&gt;&amp;quot;Welcome to the simple Minecraft screen script.&amp;quot;&lt;/span&gt;
echo &lt;span class=&quot;token string&quot;&gt;&amp;quot;----------------------------------------------&amp;quot;&lt;/span&gt;
echo &lt;span class=&quot;token string&quot;&gt;&amp;quot; If you run this and no screen instance exists,&amp;quot;&lt;/span&gt;
echo &lt;span class=&quot;token string&quot;&gt;&amp;quot; one will be created and you will be transferred&amp;quot;&lt;/span&gt;
echo &lt;span class=&quot;token string&quot;&gt;&amp;quot; into that screen instance.&amp;quot;&lt;/span&gt;
echo &lt;span class=&quot;token string&quot;&gt;&amp;quot; &amp;quot;&lt;/span&gt;
echo &lt;span class=&quot;token string&quot;&gt;&amp;quot; To leave and return to the terminal prompt (i.e. detach)&amp;quot;&lt;/span&gt;
echo &lt;span class=&quot;token string&quot;&gt;&amp;quot; Then press:  Ctrl-A, followed by D&amp;quot;&lt;/span&gt;
echo &lt;span class=&quot;token string&quot;&gt;&amp;quot;----------------------------------------------&amp;quot;&lt;/span&gt;
echo &lt;span class=&quot;token string&quot;&gt;&amp;quot; To shutdown your instance of Minecraft Server,&amp;quot;&lt;/span&gt;
echo &lt;span class=&quot;token string&quot;&gt;&amp;quot; switch to the Minecraft screen instance&amp;quot;&lt;/span&gt;
echo &lt;span class=&quot;token string&quot;&gt;&amp;quot; by typing: screen -r mcscreen&amp;quot;&lt;/span&gt;
echo &lt;span class=&quot;token string&quot;&gt;&amp;quot; from anywhere and the type stop&amp;quot;&lt;/span&gt;
echo &lt;span class=&quot;token string&quot;&gt;&amp;quot;----------------------------------------------&amp;quot;&lt;/span&gt;

# Run a check &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; an already running minecraft screen session
RUNNING&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;pgrep &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;f&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;mcscreen&amp;quot;&lt;/span&gt; | &lt;span class=&quot;token builtin&quot;&gt;wc&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

# DEBUG&lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt; UNCOMMENT TO SEE VALUE
# echo $RUNNING

# If there &lt;span class=&quot;token operator&quot;&gt;is&lt;/span&gt; no detached screen running&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; create one and switch
# Else default &lt;span class=&quot;token keyword&quot;&gt;to&lt;/span&gt; switching &lt;span class=&quot;token keyword&quot;&gt;to&lt;/span&gt; the Minecraft screen instance&lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; $RUNNING &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;eq &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
then
	echo &lt;span class=&quot;token string&quot;&gt;&amp;quot;Minecraft screen not yet running - starting one now...&amp;quot;&lt;/span&gt;
	echo &lt;span class=&quot;token string&quot;&gt;&amp;quot; &amp;quot;&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;read&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;Press any key to initiate...&amp;quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;

	screen &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;S mcscreen java &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Xms256M &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Xmx450M &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;jar minecraft_server&lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt;jar nogui
&lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;
	echo &lt;span class=&quot;token string&quot;&gt;&amp;quot;An instance of Minecraft screen is already running.&amp;quot;&lt;/span&gt;
	echo &lt;span class=&quot;token string&quot;&gt;&amp;quot; &amp;quot;&lt;/span&gt;
	echo &lt;span class=&quot;token string&quot;&gt;&amp;quot;To detach from inside screen:  Press Ctrl-A followed by D&amp;quot;&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;read&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;Press any key to switch now. &amp;quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;

	screen &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;r&lt;/span&gt; mcscreen
fi&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;(Warning: The line starting with &lt;code class=&quot;language-text&quot;&gt;screen -S...&lt;/code&gt; and ending with &lt;code class=&quot;language-text&quot;&gt;nogui&lt;/code&gt; should all be one line)&lt;/p&gt;&lt;p&gt;Save the file and return to the command prompt. If you installed the server to a different location other than &lt;code class=&quot;language-text&quot;&gt;/srv/minecraft&lt;/code&gt; then be sure to change the above file to match your install location.&lt;/p&gt;&lt;p&gt;Set the newly created script to be executable by running:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;chmod&lt;/span&gt; a+x start.sh&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To start the Minecraft instance for the first time, run:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;./start.sh&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In future when you SSH into your server, you can either:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;cd into &lt;code class=&quot;language-text&quot;&gt;/srv/minecraft/&lt;/code&gt; and re-run this script using &lt;code class=&quot;language-text&quot;&gt;./start.sh&lt;/code&gt; command (it will intelligently check for a running screen instance and switch to it instead of creating a new one)&lt;/li&gt;&lt;li&gt;run &lt;code class=&quot;language-text&quot;&gt;screen -r mcscreen&lt;/code&gt; to reattach to the already running Minecraft screen&lt;/li&gt;&lt;/ol&gt;&lt;h2 id=&quot;connecting-to-our-new-minecraft-server&quot;&gt;Connecting to our new Minecraft Server&lt;/h2&gt;&lt;p&gt;If you ran our &lt;code class=&quot;language-text&quot;&gt;start.sh&lt;/code&gt; script already then you should have a Minecraft Server up and ready to connect to. If not, now is the time to run that script.&lt;/p&gt;&lt;p&gt;At this point logout of your SSH session as we are done with the server setup phase (remember to hit &lt;code class=&quot;language-text&quot;&gt;Ctrl-A&lt;/code&gt; followed by &lt;code class=&quot;language-text&quot;&gt;d&lt;/code&gt; to disconnect from the Minecraft screen to get back to the terminal to &lt;code class=&quot;language-text&quot;&gt;logout&lt;/code&gt;).&lt;/p&gt;&lt;p&gt;You should now be able to load up Minecraft Client and attempt to connect to a Multiplayer game. Add your newly created server to the list by using &lt;code class=&quot;language-text&quot;&gt;1.2.3.4:25565&lt;/code&gt; where 1.2.3.4 is your DigitalOcean droplet IP.&lt;/p&gt;&lt;p&gt;Now go build that house before nightfall and escape those Creepers!&lt;/p&gt;&lt;h2 id=&quot;bonus-paragraph-performance&quot;&gt;Bonus Paragraph: Performance&lt;/h2&gt;&lt;p&gt;If you find the performance of your Minecraft Server is a bit poor you might want to consider upgrading your Droplet to a 1Gb instance. To do this you don&amp;#x27;t have to start from scratch, instead power down your droplet and increase the resources accordingly.&lt;/p&gt;&lt;p&gt;Don&amp;#x27;t forget, once you&amp;#x27;ve booted up the new powerhouse to edit the &lt;code class=&quot;language-text&quot;&gt;/srv/minecraft/start.sh&lt;/code&gt; script. If you upgrade to 1Gb total RAM you should change the 450M to roughly 920Mb. End this off by re-running the &lt;code class=&quot;language-text&quot;&gt;start.sh&lt;/code&gt; script to initiate a new screen instance with the new upper limit in effect. Jeepers Creepers!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Create a Chrome Extension to hide YouTube Comments]]></title><description><![CDATA[Project Definition Today we will go through how we can create a very basic Chrome Extension called NoComment that hides the comments section…]]></description><link>https://www.39digits.com/create-a-chrome-extension-to-hide-youtube-comments</link><guid isPermaLink="false">https://www.39digits.com/create-a-chrome-extension-to-hide-youtube-comments</guid><pubDate>Thu, 11 Sep 2014 18:07:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;project-definition&quot;&gt;Project Definition&lt;/h2&gt;&lt;p&gt;Today we will go through how we can create a very basic Chrome Extension called NoComment that hides the comments section on YouTube.&lt;/p&gt;&lt;p&gt;The Chrome Extension will hide the comments on YouTube.com on page load. It then displays an icon in the addressbar allowing you to click on it to toggle the visibility of the comment section should you wish to see the comments for a particular video.&lt;/p&gt;&lt;p&gt;Once completed you can browse YouTube without being bombarded with the very worst comments to be found on the Internet. Bliss.&lt;/p&gt;&lt;h2 id=&quot;basic-project-setup&quot;&gt;Basic Project Setup&lt;/h2&gt;&lt;p&gt;To create our basic Chrome Extension we need the following files:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;manifest.json&lt;/li&gt;&lt;li&gt;images/icon-19.png&lt;/li&gt;&lt;li&gt;images/icon-48.png&lt;/li&gt;&lt;li&gt;images/icon-128.png&lt;/li&gt;&lt;li&gt;background.js&lt;/li&gt;&lt;li&gt;content.js&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;You can find the source code for the &lt;a href=&quot;https://github.com/39digits/nocomment-chrome-extension&quot;&gt;NoComment Chrome Extension&lt;/a&gt; on GitHub.&lt;/p&gt;&lt;h2 id=&quot;manifestjson&quot;&gt;Manifest.json&lt;/h2&gt;&lt;p&gt;Each Chrome Extension requires, as a minimum, a &lt;code class=&quot;language-text&quot;&gt;manifest.json&lt;/code&gt; file. Note that this is the only file in the extension with a strict filename requirement.&lt;/p&gt;&lt;p&gt;The configuration of the extension itself is defined within the manifest file using JSON format. The &lt;code class=&quot;language-text&quot;&gt;name&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;version&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;manifest_version&lt;/code&gt; are required fields, but we&amp;#x27;ll be adding in a few others to get our extension to actually do something.&lt;/p&gt;&lt;p&gt;Name is the name of your extension - NoComment in our example. Version is your own defined version number. Manifest_Version is a Google-defined configuration that should be set to 2 as manifest version 1 has been deprecated.&lt;/p&gt;&lt;p&gt;Here is the &lt;code class=&quot;language-text&quot;&gt;manifest.json&lt;/code&gt; file we&amp;#x27;ll be using in your extension:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;NoComment&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&amp;quot;version&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;1.0.0&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&amp;quot;manifest_version&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&amp;quot;description&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;Toggle comment visibility on YouTube.com&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&amp;quot;background&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&amp;quot;scripts&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;quot;background.js&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&amp;quot;persistent&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&amp;quot;page_action&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&amp;quot;default_icon&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;icon-19.png&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&amp;quot;default_title&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;Toggle YouTube comments&amp;quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&amp;quot;permissions&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;quot;declarativeContent&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;activeTab&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&amp;quot;content_scripts&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&amp;quot;matches&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;quot;*://*.youtube.com/*&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&amp;quot;js&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;quot;content.js&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&amp;quot;run_at&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;document_end&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&amp;quot;icons&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&amp;quot;48&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;icon-48.png&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&amp;quot;128&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;icon-128.png&amp;quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&quot;types-of-chrome-extensions&quot;&gt;Types of Chrome Extensions&lt;/h2&gt;&lt;p&gt;Extensions impact the browser UI in two distinct ways:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Browser Actions&lt;/li&gt;&lt;li&gt;Page Actions&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The &lt;strong&gt;Browser Action&lt;/strong&gt; extensions are the types you may already be familiar with that provide an icon that is always visible in the browser&amp;#x27;s primary toolbar. You are advised to use a Browser Action when the functionality is relevant to most pages you visit (e.g. Save To Pocket). More info can be found at the &lt;a href=&quot;https://developer.chrome.com/extensions/browserAction&quot;&gt;browserAction&lt;/a&gt; documentation on the Chrome site.&lt;/p&gt;&lt;p&gt;The &lt;strong&gt;Page Action&lt;/strong&gt; extensions, however, display their icon in the address bar itself. It is advised that you use the Page Action style extension on functionality that is only relevant to some pages (e.g. to hide comments on youtube.com only). More info can be found at the &lt;a href=&quot;https://developer.chrome.com/extensions/pageAction&quot;&gt;pageAction&lt;/a&gt; documentation on the Chrome site.&lt;/p&gt;&lt;p&gt;The NoComment extension will be a PageAction type. As such we add the following configuration to our manifest file to define the icon displayed in the addressbar.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;&amp;quot;page_action&amp;quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&amp;quot;default_icon&amp;quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;images/icon-19.png&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&amp;quot;default_title&amp;quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;Toggle YouTube comments&amp;quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&quot;content-script&quot;&gt;Content Script&lt;/h2&gt;&lt;p&gt;Next up we&amp;#x27;ll create the Content Script by creating a file named &lt;code class=&quot;language-text&quot;&gt;content.js&lt;/code&gt; within our extension folder. The name of this file doesn&amp;#x27;t really matter so you can call it anything you want as it will be referenced within our &lt;code class=&quot;language-text&quot;&gt;manifest.json&lt;/code&gt; file.&lt;/p&gt;&lt;p&gt;The content script is run on each page load where relevant and is used to manipulate the DOM within the current browser tab&amp;#x27;s content.&lt;/p&gt;&lt;p&gt;Our &lt;code class=&quot;language-text&quot;&gt;content.js&lt;/code&gt; file contains a simple function to toggle the styling to display none on the content div. We also run the function so as to trigger the function to hide the contents on page load.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token function&quot;&gt;showHide&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;showHide&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; el &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;watch-discussion&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;el &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; el&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;style&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;display &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;none&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; el&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;style&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;display &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;block&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;el&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; el&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;style&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;display &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;none&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We add the following to our manifest configuration to ensure our extension knows which script to run.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;&amp;quot;content_scripts&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token property&quot;&gt;&amp;quot;matches&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;quot;*://*.youtube.com/*&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token property&quot;&gt;&amp;quot;js&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;quot;content.js&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token property&quot;&gt;&amp;quot;run_at&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;document_end&amp;quot;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;matches&lt;/code&gt; field is used to restrict the execution of the content script to only pages that match the regex used. We could use &amp;quot;*://*/*&amp;quot; if we wanted it to match on all pages, or (as we have used) &amp;quot;*://*.youtube.com/*&amp;quot; to target only YouTube pages.&lt;/p&gt;&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;js&lt;/code&gt; field defines the script file that will be run on matched pages. We set that to our &lt;code class=&quot;language-text&quot;&gt;content.js&lt;/code&gt; file but this would be the same as the name you chose earlier.&lt;/p&gt;&lt;p&gt;Lastly, the &lt;code class=&quot;language-text&quot;&gt;run_at&lt;/code&gt; field allows us to ensure the execution of the content script is deferred to the end of the page load.&lt;/p&gt;&lt;p&gt;More info can be found at the &lt;a href=&quot;https://developer.chrome.com/extensions/content_scripts&quot;&gt;Content Scripts&lt;/a&gt; documentation on the Chrome site.&lt;/p&gt;&lt;h2 id=&quot;background-script&quot;&gt;Background Script&lt;/h2&gt;&lt;p&gt;The background script can be thought of as our extension&amp;#x27;s initiliaser or constructor. We have named our background script &lt;code class=&quot;language-text&quot;&gt;background.js&lt;/code&gt; but you may name it anything you wish.&lt;/p&gt;&lt;p&gt;Our background script contains some simple initialisation code and a Listener for the action of clicking on the addressbar icon.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// When the extension is installed or upgraded ...&lt;/span&gt;
chrome&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;runtime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;onInstalled&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Replace all rules ...&lt;/span&gt;
  chrome&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;declarativeContent&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;onPageChanged&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;removeRules&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;undefined&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// With a new rule ...&lt;/span&gt;
    chrome&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;declarativeContent&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;onPageChanged&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addRules&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// That fires when a page&amp;#x27;s URL contains &amp;#x27;youtube.com&amp;#x27;...&lt;/span&gt;
        conditions&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;chrome&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;declarativeContent&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;PageStateMatcher&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            pageUrl&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; urlContains&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;youtube.com&amp;#x27;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// And shows the extension&amp;#x27;s page action.&lt;/span&gt;
        actions&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;chrome&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;declarativeContent&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ShowPageAction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

chrome&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pageAction&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;onClicked&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;tab&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  chrome&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tabs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;executeScript&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; file&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;content.js&amp;#x27;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Make note of the line where we define the URL to which this page action is valid (namely youtube.com). This means that the icon will only be visible in the addressbar when we are browsing pages within that defined URL.&lt;/p&gt;&lt;p&gt;Also note the Listener that executes the &lt;code class=&quot;language-text&quot;&gt;content.js&lt;/code&gt; script when the page action icon is clicked. Recall that this script contains the function to toggle the visibility of the YouTube comments which is executed when the content script is run.&lt;/p&gt;&lt;p&gt;We add a few lines to our manifest file to ensure our extension knows which script to run for background tasks.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;&amp;quot;background&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&amp;quot;scripts&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;quot;background.js&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&amp;quot;persistent&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We set &lt;code class=&quot;language-text&quot;&gt;persistent&lt;/code&gt; to &lt;strong&gt;false&lt;/strong&gt; to ensure the background script is active only when required. This saves on resources and is the method prescribed by Google unless persistence is indeed a neccessity. In fact, setting the persistence to false means we have created a very specific type called &lt;strong&gt;Event Pages&lt;/strong&gt;. To quote the Chrome documentation &lt;em&gt;&amp;quot;Persistence is what differentiates an event page from a background page.&amp;quot;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;As always, more info can be found at the &lt;a href=&quot;https://developer.chrome.com/extensions/event_pages&quot;&gt;Event Pages&lt;/a&gt; and &lt;a href=&quot;https://developer.chrome.com/extensions/background_pages&quot;&gt;Background Pages&lt;/a&gt; documentation on the Chrome site.&lt;/p&gt;&lt;h2 id=&quot;install-your-extension&quot;&gt;Install your extension&lt;/h2&gt;&lt;p&gt;To install the extension you will first need to navigate to &lt;code class=&quot;language-text&quot;&gt;chrome://extensions&lt;/code&gt; in Chrome or alternatively click on the hamburger menu, select Tools and then Extensions.&lt;/p&gt;&lt;p&gt;Now click on the button reading &lt;strong&gt;Load unpacked extension...&lt;/strong&gt; and browse to your extension&amp;#x27;s folder before clicking on &lt;strong&gt;Select&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;If there are any errors then Chrome will highlight those but if you followed the instructions above you should now have a Chrome Extension that hides those pesky YouTube comments by default.&lt;/p&gt;&lt;p&gt;Now go enjoy your new, clean, distraction-free YouTube!&lt;/p&gt;</content:encoded></item></channel></rss>