<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: mattstobbs</title>
    <description>The latest articles on DEV Community by mattstobbs (@mattstobbs).</description>
    <link>https://dev.to/mattstobbs</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F712082%2Ff170cbd7-756c-4097-a805-dd1c9e79e10e.jpeg</url>
      <title>DEV Community: mattstobbs</title>
      <link>https://dev.to/mattstobbs</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mattstobbs"/>
    <language>en</language>
    <item>
      <title>5 Reasons To Use TypeScript</title>
      <dc:creator>mattstobbs</dc:creator>
      <pubDate>Thu, 28 Oct 2021 12:51:54 +0000</pubDate>
      <link>https://dev.to/mattstobbs/5-reasons-to-use-typescript-1il2</link>
      <guid>https://dev.to/mattstobbs/5-reasons-to-use-typescript-1il2</guid>
      <description>&lt;p&gt;&lt;em&gt;This post was originally posted on my blog, &lt;a href="https://www.mattstobbs.com/reasons-to-use-typescript/" rel="noopener noreferrer"&gt;5 Reasons To Use TypeScript&lt;/a&gt;. Some changes have been made from the original post to fit the styling of dev.to. I recommend reading the post on the original site to see it styled as intended.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The rise of TypeScript seems to have happened slowly, then all at once (like &lt;a href="https://twitter.com/hankgreen/status/1183235225528061952?lang=en" rel="noopener noreferrer"&gt;ketchup&lt;/a&gt; coming out of the bottle).&lt;/p&gt;

&lt;p&gt;Over the past five years, it has continued to grow in popularity until it is now by far the most liked "&lt;a href="https://2020.stateofjs.com/en-US/technologies/javascript-flavors/" rel="noopener noreferrer"&gt;JavaScript flavour&lt;/a&gt;". According to the &lt;a href="https://2020.stateofjs.com/en-US/technologies/" rel="noopener noreferrer"&gt;State of JS 2020 survey&lt;/a&gt;, TypeScript is now one of the most used frontend technologies and one with the highest positive opinions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.mattstobbs.com%2Fimages%2Freasons-to-use-typescript%2FRise_Of_TypeScript.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.mattstobbs.com%2Fimages%2Freasons-to-use-typescript%2FRise_Of_TypeScript.webp" alt="A chart showing the increase in TypeScript's popularity over the past 5 years."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As I mentioned in my previous post, I was &lt;a href="https://www.mattstobbs.com/my-typescript-conversion/" rel="noopener noreferrer"&gt;sceptical&lt;/a&gt;, but have grown to love TypeScript so that I would recommend it as the default flavour for any new JavaScript project. TypeScript had so many benefits that I hadn't appreciated before I tried it.&lt;/p&gt;

&lt;p&gt;In this post, we'll take a look at five of those benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Compile-Time Errors&lt;/li&gt;
&lt;li&gt;Clearer Code&lt;/li&gt;
&lt;li&gt;Tooling Over Documentation&lt;/li&gt;
&lt;li&gt;Safe Refactoring&lt;/li&gt;
&lt;li&gt;Incredible Autocomplete&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  1. Compile-Time Errors
&lt;/h2&gt;

&lt;p&gt;Let's get this out of the way first - the obvious benefit to using TypeScript is compile-time errors for type safety.&lt;/p&gt;

&lt;p&gt;Did you forget to check if a value is null? Typescript won't.&lt;/p&gt;

&lt;p&gt;Missed a case in a switch statement? Typescript won't.&lt;/p&gt;

&lt;p&gt;Added an argument to a function but overlooked the existing uses of the function? Typescript won't.&lt;/p&gt;

&lt;p&gt;This is generally how people describe TypeScript. It means that, from my experience, when people are hesitant about whether TypeScript is worth the extra effort, this is all they're thinking about.&lt;/p&gt;

&lt;p&gt;Compile-time errors are useful, but it's the secondary benefits that you get &lt;em&gt;because of&lt;/em&gt; the type checking that makes TypeScript really exciting.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Clearer Code
&lt;/h2&gt;

&lt;p&gt;TypeScript requires you to be more explicit with your code and with your mental model of how the code works.&lt;/p&gt;

&lt;p&gt;It is said that writing forces you to clarify your thinking. It's so hard to write what's in your head - attempting to write it down forces you to organise your thoughts, challenge your assumptions, question whether there is a better way. TypeScript is the equivalent of frontend development.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.scottlogic.com/2020/12/08/finite-state-machines.html#we-are-not-smart-enough-for-complexity" rel="noopener noreferrer"&gt;Programming is hard&lt;/a&gt;. There are huge amounts of complexity. TypeScript restricts the freedom of JavaScript, but by doing so, it &lt;em&gt;reduces&lt;/em&gt; the complexity of the code. This makes it a lot easier to catch bugs and move forward with confidence with what we're writing.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;TypeScript reduces the complexity of the code.&lt;/em&gt; That may surprise you. One of the common complaints against TypeScript is how complicated it is. Sure, the basic use cases may be easy, but pretty soon you're down a rabbit hole with type-generics and conditional types and you're spending more time reading the TypeScript documentation than actually coding.&lt;/p&gt;

&lt;p&gt;When this happens to me, 9 times out of 10 that's a flag that my code is too complicated. When I think about what I'm trying to do, I can usually simplify my data structures and function signatures. The advanced parts of TypeScript are &lt;a href="https://econsultancy.com/prudent-ux-for-banking-monzo-designs-positive-friction/" rel="noopener noreferrer"&gt;positive friction&lt;/a&gt;, slowing me down enough to question whether there are better ways of designing my app.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"If you find yourself struggling with writing types or you're basically spending time developing your types instead of developing your app, maybe the code you're writing is too complicated." - &lt;a href="https://www.youtube.com/watch?v=4PduzmHf1YQ&amp;amp;t=2980s" rel="noopener noreferrer"&gt;Ben Ilegbodu&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  3. Tooling Over Documentation
&lt;/h2&gt;

&lt;p&gt;Documentation is essential for the long-term health of a project. However, it's also easy to neglect, hard to enforce, and can't report if it is no longer up-to-date. So if it's possible, &lt;a href="https://www.youtube.com/watch?v=4PduzmHf1YQ&amp;amp;t=304s" rel="noopener noreferrer"&gt;tooling should be prioritised&lt;/a&gt; over documentation.&lt;/p&gt;

&lt;p&gt;When I started working at a new company, part of my onboarding involved reading the company's coding style guide. I skimmed through it, but very little stayed in my head. I told myself once I was coding, I would refer back to the style guide to make sure I was following the standards. Unsurprisingly, I never did.&lt;/p&gt;

&lt;p&gt;Tools like &lt;a href="https://eslint.org/" rel="noopener noreferrer"&gt;ESLint&lt;/a&gt; and &lt;a href="https://prettier.io/" rel="noopener noreferrer"&gt;Prettier&lt;/a&gt; document your code styles. But they go a step further and enforce those styles while you're coding. You no longer need to worry about stray &lt;code&gt;console.log&lt;/code&gt;s or inconsistent semi-colons. Instead of the style guide being one extra thing you have to hold in your head, it becomes something you don't even have to think about. You just focus on what really matters.&lt;/p&gt;

&lt;p&gt;TypeScript is a tool that forces you to extract knowledge out of the developer's head and into the code. It documents what arguments a function is expecting, what shape objects are, and which variables may be undefined. And it will remind you when it is no longer up to date and where exactly you need to update.&lt;/p&gt;

&lt;p&gt;Without TypeScript, so much redundant time is spent by each developer having to track down the shapes of objects. It requires searching through documentation and praying they are up-to-date. Or it requires debugging the code and praying your guesses of which fields are required/optional are correct.&lt;/p&gt;

&lt;p&gt;TypeScript is an up-front investment that saves you and your team much more time in the future.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"TypeScript shines on a team. Even if the team is you and you-from-6-months-ago" - &lt;a href="https://swizec.com/blog/learn-typescript-in-5-minutes/#why-even-bother" rel="noopener noreferrer"&gt;@swizec&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  4. Safe Refactoring
&lt;/h2&gt;

&lt;p&gt;I recently had two refactoring experiences that were a world apart.&lt;/p&gt;

&lt;p&gt;In the first case, I was updating all our buttons to use our common &lt;code&gt;Button&lt;/code&gt; component. It was a straightforward change, and completely terrifying. The codebase was JavaScript, there were too many buttons to manually check each one and our test coverage was spotty at best. It felt like I was walking on the edge of a cliff, knowing that if I missed or misspelt a prop, that could potentially stop a button from working - a critical bug.&lt;/p&gt;

&lt;p&gt;In another refactor, I was changing the shape of the state. Originally we just needed a list of titles but now we needed a title and a description for each item. Fortunately, this time I was working on a TypeScript codebase so I updated the type from &lt;code&gt;string[]&lt;/code&gt; to &lt;code&gt;{ description: string; title: string; }[]&lt;/code&gt; and then just let TypeScript tell me exactly what would need updating. It was only halfway through that I suddenly realised how hard this could potentially be in JavaScript. Instead, I hardly needed to think at all.&lt;/p&gt;

&lt;p&gt;TypeScript not only gives you confidence that you haven't missed anything when you refactor but also shows you where you need to update. You no longer need to manually track your variables all over the code - just follow the red squiggles.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Incredible Autocomplete
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;This section assumes you're using VSCode as your IDE. Both TypeScript and VSCode are developed and maintained by Microsoft so are designed to integrate well with each other.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Other IDEs have TypeScript plugins which also give you Autocomplete. However, I have no experience with using them, so can't comment on how good they are.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The TypeScript autocomplete unexpectedly has become one of the biggest reasons why I love TypeScript. I imagine it's one of the reasons why it's so popular among developers.&lt;/p&gt;

&lt;p&gt;Autocomplete means I don't have to worry about typos (did we name this prop &lt;code&gt;color&lt;/code&gt; or &lt;code&gt;colour&lt;/code&gt;?). I don't have to keep jumping between files to see which component props I need. I don't need to keep googling the names of string and array functions.&lt;/p&gt;

&lt;p&gt;Let's say I have a string variable that could be undefined - &lt;code&gt;string | undefined&lt;/code&gt;. I want to see if the string, contains a &lt;code&gt;'#'&lt;/code&gt; character but I can't remember if I should be using &lt;code&gt;.contains&lt;/code&gt; or &lt;code&gt;.includes&lt;/code&gt; (happens every time!). I enter the variable name, press &lt;code&gt;.&lt;/code&gt; and all the possible string functions are shown to me:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.mattstobbs.com%2Fimages%2Freasons-to-use-typescript%2FTypeScript_Autocomplete.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.mattstobbs.com%2Fimages%2Freasons-to-use-typescript%2FTypeScript_Autocomplete.webp" alt="TypeScript shows autocomplete options for all the functions that can be used on strings."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I find the function I want and press tab to select it:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.mattstobbs.com%2Fimages%2Freasons-to-use-typescript%2Ftypescript-autocomplete-after.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.mattstobbs.com%2Fimages%2Freasons-to-use-typescript%2Ftypescript-autocomplete-after.webp" alt="TypeScript autocompletes function and uses optional chaining."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Did you see that? Not only did it help us look up which function we wanted, but it also filled it in for us &lt;em&gt;and&lt;/em&gt; used optional chaining to make sure we handle the cases where it's &lt;code&gt;undefined&lt;/code&gt;. &lt;a href="https://emojipedia.org/exploding-head/" rel="noopener noreferrer"&gt;🤯&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All these mean you don't have to keep interrupting your flow. You can just tab and move on to the next thing. It's like having a co-pilot as you code.&lt;/p&gt;

&lt;h2&gt;
  
  
  TypeScript As A Default For New Projects
&lt;/h2&gt;

&lt;p&gt;TypeScript isn't perfect. There are plenty of arguments against it (some better than others). But for me, TypeScript should be the default for any new project. Instead of asking if there is a good reason to include it, you should be asking if there is a good reason not to.&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1453703985089630216-881" src="https://platform.twitter.com/embed/Tweet.html?id=1453703985089630216"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1453703985089630216-881');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1453703985089630216&amp;amp;theme=dark"
  }



&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>javascript</category>
      <category>programming</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Adding Dark Mode to an ElderJS Site</title>
      <dc:creator>mattstobbs</dc:creator>
      <pubDate>Fri, 24 Sep 2021 07:50:36 +0000</pubDate>
      <link>https://dev.to/mattstobbs/adding-dark-mode-to-an-elderjs-site-3bp9</link>
      <guid>https://dev.to/mattstobbs/adding-dark-mode-to-an-elderjs-site-3bp9</guid>
      <description>&lt;p&gt;&lt;em&gt;This post was originally posted on my blog, &lt;a href="https://www.mattstobbs.com/elderjs-dark-mode/"&gt;Adding Dark Mode to an ElderJS Site&lt;/a&gt;. Some changes have been made from the original post to fit the styling of dev.to. I recommend reading the post on the original site to see it styled as intended.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;One of the trickiest parts of creating this site was implementing the dark mode. I thought it would be simple:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use CSS variables for all the colours. CSS variables are &lt;em&gt;reactive&lt;/em&gt;, so they'll automatically update the colours on the page if their values change.&lt;/li&gt;
&lt;li&gt;Define two sets of CSS variables, a default value for light mode and a dark mode value for when the body node has a class of &lt;code&gt;dark&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Toggle the body node's &lt;code&gt;dark&lt;/code&gt; class to switch between light and dark mode.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--colour-background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#ffffff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c"&gt;/* Define the other light-mode colours here */&lt;/span&gt;

  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--colour-background&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="nc"&gt;.dark&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--colour-background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#111827&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c"&gt;/* Define the other dark-mode colours here */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, with this approach, we don't remember what the user's preference is. ElderJS doesn't use &lt;a href="https://elderguide.com/tech/elderjs/#why-we-built-elderjs"&gt;client-side routing&lt;/a&gt;, so when you navigate the site, each page will fall back to the default light mode. Refreshing the page or returning to the site later gives us the same problem.&lt;/p&gt;

&lt;p&gt;It turns out solving this problem is more complicated than it seems. In this post, we'll look at how I implemented dark-mode for this blog so that the user's choice of theme is always the one they see.&lt;/p&gt;

&lt;p&gt;A huge inspiration for this post is taken from Josh W. Comeau's excellent blog post &lt;a href="https://www.joshwcomeau.com/react/dark-mode"&gt;The Quest for the Perfect Dark Mode&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;While that post was written for sites built with Gatsby.js, the main strategy behind it was used for this post. It's worth reading if you're interested in learning more about why this approach was chosen.&lt;/p&gt;

&lt;p&gt;If you just want to jump to where we start coding our final solution, you can find that here.&lt;/p&gt;

&lt;h2&gt;
  
  
  Proposed Initial Solution
&lt;/h2&gt;

&lt;p&gt;When the user toggles between light and dark mode, we'll store their choice in localStorage.&lt;/p&gt;

&lt;p&gt;When a user navigates to our page, we'll see if they have a previous value saved and use it as the initial value.&lt;/p&gt;

&lt;p&gt;If localStorage doesn't define a value, we'll use their operating system preferences as our default. If no theme preferences are available, we'll use light mode as our default.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OmUZ_I2Q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.mattstobbs.com/images/elderjs-dark-mode/flow-chart.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OmUZ_I2Q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.mattstobbs.com/images/elderjs-dark-mode/flow-chart.webp" alt="A flow chart showing the above requirements."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Our code will look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getInitialColourMode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;persistedColourPreference&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;colour-mode&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hasPersistedPreference&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;persistedColourPreference&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hasPersistedPreference&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;persistedColourPreference&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mql&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;matchMedia&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;(prefers-color-scheme: dark)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hasMediaQueryPreference&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;mql&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;matches&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;boolean&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hasMediaQueryPreference&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;mql&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;matches&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;light&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;light&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  One Last Hurdle
&lt;/h2&gt;

&lt;p&gt;Running this code in one of our components, such as the &lt;code&gt;onMount&lt;/code&gt; of our layout component, exposes the last hurdle we need to overcome - the dreaded light-flash 😱&lt;/p&gt;

&lt;p&gt;The problem is that we only have access to the &lt;code&gt;window&lt;/code&gt; after the components have mounted. Therefore, the page renders using the default value of "light-mode" before our code runs and switches the page to dark mode.&lt;/p&gt;

&lt;p&gt;We need a way of running our JavaScript before the page renders, which means we need to run it outside the Svelte components. We can do this by inserting a script tag before the &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; element of our HTML. Script tags are blocking, so placing it before the &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; element will mean the JavaScript inside will run &lt;em&gt;before&lt;/em&gt; the page renders.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementing Dark-Mode
&lt;/h2&gt;

&lt;p&gt;Ok, we're finally ready to start coding!&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting the correct initial theme
&lt;/h3&gt;

&lt;p&gt;Let's start with inserting the script tag before the &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; element to get our initial dark-mode value. One of the most powerful features of ElderJS is &lt;a href="https://elderguide.com/tech/elderjs/#hooks-how-to-customize-elderjs"&gt;hooks&lt;/a&gt;, which allow us to plug into and customise any part of the page generation process.&lt;/p&gt;

&lt;p&gt;We want to add the script to the head, so we'll use the &lt;code&gt;stacks&lt;/code&gt; hook. It exposes a prop called &lt;code&gt;headStack&lt;/code&gt; which we can mutate to add elements to the head:&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hooks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;hook&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;stacks&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;addDarkModeScript&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Adds script to check for existing dark mode preferences&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;headStack&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;codeToRunOnClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`
      &amp;lt;script&amp;gt;
        (function() {
          function getInitialColourMode() {
            // same as above - removed for brevity
          }

          const colourMode = getInitialColourMode();
          if (colourMode === 'dark') {
            document.documentElement.classList.add('dark');
          }
        })()
      &amp;lt;/script&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="nx"&gt;headStack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;addDarkModeScript&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;string&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;codeToRunOnClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We use &lt;code&gt;getInitialColourMode&lt;/code&gt; to find our initial colour mode from the user's pre-defined preferences. If it's &lt;code&gt;'light'&lt;/code&gt;, we don't need to do anything - that's our default. If it's &lt;code&gt;'dark'&lt;/code&gt;, we'll add a &lt;code&gt;'dark'&lt;/code&gt; class to our HTML root element (this is running before the &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; element, so, for our purposes, the root element will be the only defined element).&lt;/p&gt;

&lt;p&gt;Why do we define a new function and immediately call it?&lt;/p&gt;

&lt;p&gt;This is called an &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/IIFE"&gt;IIFE (Immediately Invoked Function Expression)&lt;/a&gt;. The main idea is that we won't be polluting the global namespace because everything is scoped within a function.&lt;/p&gt;

&lt;h3&gt;
  
  
  Showing the correct colours
&lt;/h3&gt;

&lt;p&gt;Now that the root element has the right class, we can use CSS variables to show the correct colours. This is the same as the code in the introduction, but now our &lt;code&gt;.dark&lt;/code&gt; class is on the HTML element.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--colour-background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#ffffff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c"&gt;/* Define the other light-mode colours here */&lt;/span&gt;

  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--colour-background&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;html&lt;/span&gt;&lt;span class="nc"&gt;.dark&lt;/span&gt; &lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--colour-background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#111827&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c"&gt;/* Define the other dark-mode colours here */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we're showing the correct initial value without any incorrect flashes after the page loads 🎉&lt;/p&gt;

&lt;h3&gt;
  
  
  Toggling the Theme
&lt;/h3&gt;

&lt;p&gt;The last step is to allow the user to toggle the theme. We need a button/toggle which toggles the root element's class when clicked, and stores that new value to localStorage.&lt;/p&gt;

&lt;p&gt;The only complication is that we won't know what the initial value should be when the component mounts. To solve this, we'll use &lt;a href="https://www.joshwcomeau.com/react/dark-mode/#adding-a-toggle"&gt;Josh W. Comeau's&lt;/a&gt; solution: defer rendering the toggle until after we can read the initial value.&lt;/p&gt;

&lt;p&gt;There are lots of ways to display a toggle. If you use a switch component, I recommend basing it off a library like &lt;a href="https://headlessui.dev/react/switch"&gt;Headless UI&lt;/a&gt; to ensure the component is fully accessible. For my blog, I use &lt;code&gt;&amp;lt;Moon /&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;Sun /&amp;gt;&lt;/code&gt; components, which are just SVGs from &lt;a href="https://feathericons.com/"&gt;Feather Icons&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;script&amp;gt;
  import { onMount } from 'svelte';
  import Moon from './icons/Moon.svelte';
  import Sun from './icons/Sun.svelte';

  const darkModeClass = 'dark';

  let isDarkMode;
  onMount(() =&amp;gt; {
    isDarkMode = window.document.documentElement.classList.contains(darkModeClass);
  });

  const toggle = () =&amp;gt; {
    window.document.documentElement.classList.toggle(darkModeClass);
    isDarkMode = window.document.documentElement.classList.contains(darkModeClass);
    window.localStorage.setItem('colour-mode', isDarkMode ? 'dark' : 'light');
  };
&amp;lt;/script&amp;gt;

{#if typeof isDarkMode === 'boolean'}
  &amp;lt;button aria-label="Activate dark mode" title="Activate dark mode" on:click={toggle}&amp;gt;
    {#if isDarkMode}
      &amp;lt;Moon /&amp;gt;
    {:else}
      &amp;lt;Sun /&amp;gt;
    {/if}
  &amp;lt;/button&amp;gt;
{/if}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Success 🎉
&lt;/h2&gt;

&lt;p&gt;We've successfully created a dark-mode toggle for our ElderJS site, which shows the user's preferred theme when they first see the page in all its glory. First impressions matter, so it's vital to get the details right in the first few seconds of a user's experience.&lt;/p&gt;

&lt;p&gt;If there is enough interest, this would be an excellent candidate for an &lt;a href="https://elderguide.com/tech/elderjs/#plugins"&gt;ElderJS plugin&lt;/a&gt;. In the meantime, if you have any questions, feel free to reach out to me on &lt;a href="https://twitter.com/matt_stobbs"&gt;Twitter&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>svelte</category>
      <category>elderjs</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
