<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Nate Watson</title>
    <description>The latest articles on Forem by Nate Watson (@nw).</description>
    <link>https://forem.com/nw</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%2F9488%2F093a6baa-014f-4672-b699-6e351c0d990f.jpg</url>
      <title>Forem: Nate Watson</title>
      <link>https://forem.com/nw</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/nw"/>
    <language>en</language>
    <item>
      <title>CSS tweaks for more accessible text</title>
      <dc:creator>Nate Watson</dc:creator>
      <pubDate>Sat, 08 May 2021 13:04:58 +0000</pubDate>
      <link>https://forem.com/nw/css-tweaks-for-more-accessible-text-ipl</link>
      <guid>https://forem.com/nw/css-tweaks-for-more-accessible-text-ipl</guid>
      <description>&lt;p&gt;One of the magical things about the web is that there are numerous ways people will access the sites we build. Whether it's on a phone, in a "reader mode", with images disabled, using a screen-reader or without a mouse, we can (and &lt;em&gt;should&lt;/em&gt;) write code that ensures our sites work beautifully for all these different configurations.&lt;/p&gt;

&lt;p&gt;With text particularly, there are a number of browser options that people might use to make content more accessible to them. This ranges from increasing font sizes for more legible text, swapping to a different font completely, or even swapping the text content itself via a translation tool.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc2cxt7ikwayv6zv3jkzz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc2cxt7ikwayv6zv3jkzz.png" alt="Increased font size, overridden font families and automatically translated content on a webpage. In all cases the text has enough room, remains readable and maintains bold and italics" width="800" height="264"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Code that worsens support for these features is unfortunately common across the web, but goes unnoticed when testing against common accessibility tools such as screen-readers or automated auditors. Fortunately some small changes to how we write CSS can easily bring support for various text-related settings, and once you set things up, it'll quickly become second-nature on all your projects. So let's dive in and have a look at some guidelines for writing accessible CSS!&lt;/p&gt;

&lt;h2&gt;
  
  
  Tip 1: Use relative font size for nice font scaling
&lt;/h2&gt;

&lt;p&gt;Font size is one of the most prominent accessibility settings in most browsers, and a good number of users set it to something other than the default. Stats gathered by the Internet Archive in 2018 showed that &lt;a href="https://medium.com/@vamptvo/pixels-vs-ems-users-do-change-font-size-5cfb20831773" rel="noopener noreferrer"&gt;just over 3% of their website visitors were using a non-default font size setting&lt;/a&gt;, which is a pretty decent number of people!&lt;/p&gt;

&lt;p&gt;What might be more surprising is that setting your CSS font sizes using pixels (i.e. with the &lt;code&gt;px&lt;/code&gt; unit) causes that setting to do absolutely nothing. The problem with pixel units is that they tell the browser exactly how big something should be without allowing for any other factors (like a user having "very large" fonts selected in their browser settings) to come into play.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo588b7cr9qlu1w9lf5p3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo588b7cr9qlu1w9lf5p3.png" alt="Setting the browser font size to " width="800" height="285"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is where relative font size units really shine. Rather than telling the browser "make my text exactly this big", they instead describe how big the text should be relative to some other element on the page. In the case of the &lt;code&gt;em&lt;/code&gt; unit, it's relative to the parent element's font size, and for the &lt;code&gt;rem&lt;/code&gt; unit it's relative to the font size of page's top-most, or &lt;em&gt;root&lt;/em&gt;, element (almost always the &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt; element).&lt;/p&gt;

&lt;p&gt;By default, that root element's font size comes directly from the browser's font size setting. A browser with its fonts set to "very large" might give pages a root font-size of 24px by default, while one with "very small" might result in a default of 10px.&lt;/p&gt;

&lt;p&gt;A lot of pages will override that default by setting a pixel-based font size on the HTML element, for example:&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="c"&gt;/** Don't do this **/&lt;/span&gt;
&lt;span class="nt"&gt;html&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;18px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is often framed as providing a "sensible default", but it prevents the rest of the page from knowing the user's preferred font size. If you're approaching your sites from an accessible perspective, the user preference is almost always the most sensible default.&lt;/p&gt;

&lt;p&gt;In short, avoid setting a pixel-based font-size on the HTML (or &lt;code&gt;:root&lt;/code&gt;) element.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting font sizes
&lt;/h3&gt;

&lt;p&gt;Now that we've got our root element respecting the browser font size, we need to extend that to all the other other elements on our site. Fortunately with a little bit of math (or an SCSS mixin) we can do this fairly effortlessly in a way that will be invisible to most users.&lt;/p&gt;

&lt;p&gt;The key thing to know is that the default root font size is 16px in most browsers. That means text with its font size set to &lt;code&gt;1rem&lt;/code&gt; will display as 16px (1 times the root font size), &lt;code&gt;2rem&lt;/code&gt; will be 32px, &lt;code&gt;0.5rem&lt;/code&gt; will be 8px, and so on, for users who haven't changed the font size setting. With a little bit of maths, you can convert all your existing pixel sizes to an rem-equivalent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sizeInRems = sizeInPixels / 16
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which should let you update any existing font size declarations to be root-relative:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- font-size: 16px;
&lt;/span&gt;&lt;span class="gi"&gt;+ font-size: 1rem;
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gd"&gt;- font-size: 24px;
&lt;/span&gt;&lt;span class="gi"&gt;+ font-size: 1.5rem;
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;// and so on
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(If you're using SCSS, read on for a mixin that will do some of this for you).&lt;/p&gt;

&lt;p&gt;By not overriding the root font size and switching to relative sizes your text will now scale to match each user's preference - if they've left the setting untouched, everything will look exactly as it did before. But if they've chosen to change the setting - to something larger or something smaller - your website's text will now scale to reflect that preference:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6e6zd79luij83tudxhxo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6e6zd79luij83tudxhxo.png" width="800" height="405"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  A note on line-height
&lt;/h3&gt;

&lt;p&gt;It's important to also switch your line-height values to proportional units, otherwise the values won't scale to match any changes to the calculated font size. For people who increase the root font size, this can cause overlapping lines of text that are pretty difficult to decipher:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2w7j68nzokusd53ea5w9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2w7j68nzokusd53ea5w9.png" alt="With default browser settings a pixel-based line height looks perfectly fine. Increasing the browser font size setting causes the size of the letters to increase, but the vertical space between the lines remains unchanged, causing the lines to overlap each other." width="789" height="308"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In fact, this is something you should do even if your font-size values are still using pixel units. In addition to settings that change the root font size, most browsers have an option to force a &lt;em&gt;minimum&lt;/em&gt; font size (usually found under an "advanced font options" menu or similar). This will override any font-size values that are too small, but won't affect any fixed-unit line-heights, resulting in the same overlapping shown above.&lt;/p&gt;

&lt;p&gt;The best way to fix this is to switch to unitless line-height values. Line-heights are one of the few CSS properties where it's valid to leave off the units completely (so no &lt;code&gt;px&lt;/code&gt;, no &lt;code&gt;em&lt;/code&gt;, no &lt;code&gt;%&lt;/code&gt;), with unitless values being interpreted as "the unitless number multiplied by the element's own font size".&lt;/p&gt;

&lt;h3&gt;
  
  
  A mixin to help
&lt;/h3&gt;

&lt;p&gt;If you're like me, pixel values are how you and the people you work with communicate - design tools usually give values in pixels, the browser inspector shows computed sizes in pixels and they're a unit anyone who stares at a screen for eight hours a day probably has baked into their mind. Thinking in terms of &lt;code&gt;rems&lt;/code&gt; can feel less instinctive, and having to pull out a calculator to convert units is no fun.&lt;/p&gt;

&lt;p&gt;Fortunately, you can write a nice little SCSS mixin (or equivalent in your favourite CSS pre-processor) to do all the hard work for you. The one I use looks something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="k"&gt;@mixin&lt;/span&gt; &lt;span class="nf"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$font-size&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$line-height&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nv"&gt;$font-size&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="m"&gt;16px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Assuming 16px as the "standard" root font size&lt;/span&gt;

    &lt;span class="k"&gt;@if&lt;/span&gt; &lt;span class="nv"&gt;$line-height&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;line-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$line-height&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nv"&gt;$font-size&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Gives a unitless value&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;...and then you can write font sizes using pixels (specifically the pixel size you want to be used when the browser is set to its default font sizes), and the outputted CSS will use the appropriate relative units:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Input:&lt;/span&gt;
&lt;span class="nc"&gt;.paragraph&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;@include&lt;/span&gt; &lt;span class="nd"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;24px&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="m"&gt;30px&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Output:&lt;/span&gt;
&lt;span class="nc"&gt;.paragraph&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="mi"&gt;.5rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;line-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="mi"&gt;.25&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;
  
  
  Tip 2: Avoid hardcoded element dimensions - give text space to grow
&lt;/h2&gt;

&lt;p&gt;If you browse the web with increased font sizes, it's common to encounter situations where text is too big for its container:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feu61kiuxotzf2hjvkibc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feu61kiuxotzf2hjvkibc.png" alt="At default browser font sizes all text on the page fits inside its visual containers - a card sit nicely around the main text and a button has text sitting inside the button's outline. Increasing the font size results in text spilling out the bottom of the card and the button text overlapping the button's borders." width="789" height="526"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In fact this isn't just an issue when increasing the font size. Anything that changes the amount of text or dimensions of the letters can make this happen, which includes things like translating a page, using a different typeface (whether intentionally or due to a webfont not loading) or changing the letter spacing.&lt;/p&gt;

&lt;p&gt;The source of issues like this is hardcoding the dimensions of elements that contain text:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="nc"&gt;.Button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;50px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;250px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// html: &amp;lt;button class="Button"&amp;gt;Learn more about the rubber plant&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case our button is going to look bad if the text ever becomes taller than 50px or wider than 250.&lt;/p&gt;

&lt;p&gt;We're essentially telling the element to not shrink or grow, so anything that doesn't fit will simply overflow out of the element - as shown in the earlier screenshot.&lt;/p&gt;

&lt;p&gt;Often hardcoded dimensions like this can be removed completely or, if you need to prevent the element becoming too small, replaced with min-width/min-height alternatives. If the dimensions play a part in vertically centering the text content, padding is often a suitable alternative:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="nc"&gt;.Button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;min-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;250px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After applying this approach to the button and card in the earlier screenshot, we end up with something much nicer:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4n9l7j439edhj7o8w80j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4n9l7j439edhj7o8w80j.png" alt="At both default and increased font sizes all text on the page now fits inside its visual containers. At the larger font size the button text wraps over three lines and the container/outline has grown to accommodate this" width="789" height="560"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Tip 3: Specify web font weights and styles
&lt;/h2&gt;

&lt;p&gt;Just as the font sizes you specify might be overridden, the font-families named in your CSS may not end up being displayed on the page. There are a number of situations where this will happen: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A webfont fails to load, causing a fallback to be used,&lt;/li&gt;
&lt;li&gt;Webfonts get blocked completely (for speed, security, to save data, etc.),&lt;/li&gt;
&lt;li&gt;A user finds a particular typeface easier to read, so uses an extension to force it to be used everywhere,&lt;/li&gt;
&lt;li&gt;The page is automatically translated into a language that uses characters not available in the webfont.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The first step to handling these situations gracefully is something most people already do: specifying fallback fonts, or a "font stack":&lt;/p&gt;

&lt;p&gt;&lt;code&gt;font-family: Fira, Arial, sans-serif;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This tells the browser to use Fira if it's available, otherwise use Arial, and if that's not available, use the browser's default sans-serif font family. The fallback fonts should be similar to the preferred font, so the page will still look reasonable regardless of which one ends up being used.&lt;/p&gt;

&lt;p&gt;If you're using webfonts there's one more thing you should do to really make your text great, regardless of the font family that ends up being used. Attributes such as font weight and italics often carry additionally (and intentional) meaning, or help differentiate between pieces of text on the page. That makes it important to preserve them no matter which font family gets used. To do this, we need to look at our &lt;code&gt;@font-face&lt;/code&gt; declarations. These might look something like:&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="k"&gt;@font-face&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;'Fira'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sx"&gt;url('fira.woff')&lt;/span&gt; &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;'woff'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
        &lt;span class="sx"&gt;url('fira.ttf')&lt;/span&gt; &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;'truetype'&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;Different font weights (bold, light, etc.) and styles (e.g. italics) have to be loaded from separate font files, and therefore require separate @font-face declarations. It's common to see these each given unique names, like:&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="k"&gt;@font-face&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;'Fira'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sx"&gt;url('fira.woff')&lt;/span&gt; &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;'woff'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
        &lt;span class="sx"&gt;url('fira.ttf')&lt;/span&gt; &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;'truetype'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; 

&lt;span class="k"&gt;@font-face&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;'FiraBold'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sx"&gt;url('fira-bold.woff')&lt;/span&gt; &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;'woff'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
        &lt;span class="sx"&gt;url('fira-bold.ttf')&lt;/span&gt; &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;'truetype'&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;Which might be used something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.bold&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;FiraBold&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Arial&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;sans-serif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The issue with this approach is that the browser doesn't really know that our 'FiraBold' font is bold - if it fails to load (or is swapped out for a different font), the fallback font will display in a regular weight, no different to the text around it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fef16i8ee5viph7znw743.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fef16i8ee5viph7znw743.png" alt="Webpage shown with bold text adding emphasis to a warning and italics being used on a Latin word. With fonts overridden the bold and italics text look no different to the text around them." width="789" height="325"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To fix this, we need to be explicit about the weights and styles when declaring and when using our fonts. Rather than giving each variation a unique &lt;code&gt;font-family&lt;/code&gt; value, we should use the &lt;em&gt;same&lt;/em&gt; font-family value across all the variations, and explicitly specify the weight and style of each. For the above example this would look like:&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="k"&gt;@font-face&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;'Fira'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;400&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;font-style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;normal&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sx"&gt;url('fira.woff')&lt;/span&gt; &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;'woff'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
        &lt;span class="sx"&gt;url('fira.ttf')&lt;/span&gt; &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;'truetype'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; 

&lt;span class="k"&gt;@font-face&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;'Fira'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;700&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;font-style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;normal&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sx"&gt;url('fira-bold.woff')&lt;/span&gt; &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;'woff'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
        &lt;span class="sx"&gt;url('fira-bold.ttf')&lt;/span&gt; &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;'truetype'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The final step is to update any CSS where you're using the font variations, so that &lt;code&gt;font-weight&lt;/code&gt; and &lt;code&gt;font-style&lt;/code&gt; CSS properties are used to select variations of the same font-family:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;.bold {
&lt;span class="gd"&gt;-   font-family: FiraBold, Arial, sans-serif;
&lt;/span&gt;&lt;span class="gi"&gt;+   font-family: Fira, Arial, sans-serif;
+   font-weight: 700;
&lt;/span&gt;}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now if the fonts on your page are overridden, fail to load, or are unable to display some text, the weight and style of the text will be preserved:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flqlxfawke65cfid95gs1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flqlxfawke65cfid95gs1.png" width="789" height="354"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus tip: Don't use icon fonts
&lt;/h2&gt;

&lt;p&gt;Icons fonts are easy to set-up and use, but they have a number of issues, including breaking completely for users who override fonts (or any other situation where webfonts aren't loaded). That's not to say you shouldn't use icons though (far from it - well used icons are great for accessibility and usability), but approaches that don't involve fonts are the best way to go. &lt;/p&gt;

&lt;p&gt;People much smarter than me have written various articles on the topic, so I'm going to delegate to them for this one:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://cloudfour.com/thinks/seriously-dont-use-icon-fonts/" rel="noopener noreferrer"&gt;Seriously, Don’t Use Icon Fonts by Tyler Sticka&lt;/a&gt; - an older article, but still entirely relevant.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.24a11y.com/2018/accessible-svg-icons-with-inline-sprites/" rel="noopener noreferrer"&gt;Accessible SVG Icons with Inline Sprites by Marco Hengstenberg&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://css-tricks.com/accessible-svg-icons/" rel="noopener noreferrer"&gt; Accessible SVG Icons by Chris Coyier&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  And we're done!
&lt;/h2&gt;

&lt;p&gt;Supporting numerous different browser configurations can seem like a daunting task, but hopefully these tips have shown that some small changes to how you write CSS can have a big impact. If you're starting a fresh project, try to make these patterns the default! You'll quickly see that they're no extra effort, and visitors to your site will love you for it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;p&gt;These are some pages I bookmarked while writing this post - you might find the useful too:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://css-tricks.com/accessible-font-sizing-explained/" rel="noopener noreferrer"&gt;Accessible Font Sizing, Explained - Andrés Galante&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nicolas-hoizey.com/articles/2016/03/02/people-don-t-change-the-default-16px-font-size-in-their-browser" rel="noopener noreferrer"&gt;People don't change the default 16px font size in their browser (You wish!) - Nicolas Hoizey&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://adrianroselli.com/2019/12/responsive-type-and-zoom.html" rel="noopener noreferrer"&gt;Responsive Type and Zoom - Adrian Roselli&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/@matuzo/writing-css-with-accessibility-in-mind-8514a0007939" rel="noopener noreferrer"&gt;Writing CSS with Accessibility in Mind - Manuel Matuzovic&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.smashingmagazine.com/2013/02/setting-weights-and-styles-at-font-face-declaration/" rel="noopener noreferrer"&gt;How To Set Weights And Styles With The @font-face Declaration - Laura Franz&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=9xXBYcWgCHA" rel="noopener noreferrer"&gt;Death to icon fonts - talk by Seren Davies at EpicFEL 2015&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://eyeondesign.aiga.org/can-fonts-really-help-those-with-dyslexia/" rel="noopener noreferrer"&gt;Can Fonts Really Help Those With Dyslexia? - Madeleine Morley&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>a11y</category>
      <category>webdev</category>
      <category>css</category>
    </item>
    <item>
      <title>Adding dark mode to your React app with hooks, media queries and CSS variables</title>
      <dc:creator>Nate Watson</dc:creator>
      <pubDate>Thu, 28 May 2020 12:18:39 +0000</pubDate>
      <link>https://forem.com/nw/adding-dark-mode-to-your-react-app-with-hooks-media-queries-and-css-variables-50h0</link>
      <guid>https://forem.com/nw/adding-dark-mode-to-your-react-app-with-hooks-media-queries-and-css-variables-50h0</guid>
      <description>&lt;p&gt;Dark mode is quickly becoming an essential feature on the web — Twitter’s recent redesign has the much-requested feature baked in, as does Facebook’s (beta) redesign, not to mention numerous smaller sites adding support.&lt;/p&gt;

&lt;p&gt;Wanting to keep up with the cool kids, I decided to have a go at adding dark mode to &lt;a href="https://nw.nz" rel="noopener noreferrer"&gt;my own personal website&lt;/a&gt;. After a night of picking out some colours and procrastinating over the technical approach, piecing together a working implementation turned out to be much quicker and easier than I had expected. I’ve detailed the approach I took here, in the hope that someone else might find it useful!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: this approach is great for smaller sites, but for more complex cases you might need to combine it with other techniques — there are some resources that might be handy linked at the end.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  So what are we actually trying to build here?
&lt;/h3&gt;

&lt;p&gt;Great question. The key features I’ll outline are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Detecting if a device is set to dark mode at the system level&lt;/li&gt;
&lt;li&gt;Switching the theme whenever the system-level setting changes&lt;/li&gt;
&lt;li&gt;A simple system (using CSS variables) to swap colours throughout the site&lt;/li&gt;
&lt;li&gt;A toggle to let people manually switch between dark and light themes&lt;/li&gt;
&lt;li&gt;A SCSS mixin to support more complex theming, for when you need to do more than just swap out a colour.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s a quick example of what this can look like — if you’re in a hurry feel free to jump into the code to see how it all fits together:&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/reactscss-dark-mode-v6415"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Detecting dark mode with Media Queries
&lt;/h2&gt;

&lt;p&gt;First up, let’s use some CSS to detect when someone has their device set to dark mode. To do this we’ll use a &lt;strong&gt;media query&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;CSS media queries are most commonly used to alter styles based on the size of the browser. But recently they’ve been becoming much more powerful, with &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries/Using_media_queries#Media_features" rel="noopener noreferrer"&gt;an impressive list of detectable features&lt;/a&gt; making it into recent specifications.&lt;/p&gt;

&lt;p&gt;The media query feature we care about is &lt;code&gt;prefers-color-scheme&lt;/code&gt;. As the name suggests, it lets us detect what kind of colour scheme the user prefers — one of &lt;code&gt;dark&lt;/code&gt;, &lt;code&gt;light&lt;/code&gt; or &lt;code&gt;no-preference&lt;/code&gt;. Using it looks something like this:&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="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefers-color-scheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c"&gt;/* dark theme styles go here */&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Any styles within that media query will only be applied if the user’s system is set to dark mode. That alone is enough to start adding a dark theme to your site! Here’s a quick example of what that might look like (in plain CSS) for a simple component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;    &lt;span class="nc"&gt;.TextCard&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;black&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefers-color-scheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nc"&gt;.TextCard&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;black&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&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;&lt;em&gt;For simplicity I’m using named colours here like “black” and “white”. In the actual implementation I extract my common colours to SCSS variables to keep them consistent.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Awesome! This is good progress. But after doing this for a few components you might notice a lot of repetition: you’ll likely be swapping the same colours in and out over and over. For example, if most of your text is a particular dark grey, you’ll likely be adding an identical media query everywhere you use that colour, to swap it for a (different) particular shade in dark mode.&lt;/p&gt;

&lt;p&gt;This is where the next bit of the puzzle comes in: &lt;strong&gt;CSS variables&lt;/strong&gt; ✨&lt;/p&gt;

&lt;h2&gt;
  
  
  Swapping colours with CSS variables
&lt;/h2&gt;

&lt;p&gt;With CSS variables we can define our default (light mode) colours in single place, then set them up to swap to different colours when dark mode is active. If you’re familiar with SCSS variables then these are similar, except that we can dynamically change their values at runtime — this is key for using them as part of our theming system.&lt;/p&gt;

&lt;p&gt;As a simple example, we might define &lt;code&gt;primaryTextColor&lt;/code&gt; and &lt;code&gt;primaryBackgroundColor&lt;/code&gt; as variables. For our default light theme we would set them like this:&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;html&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="py"&gt;--primaryBackgroundColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="py"&gt;--primaryTextColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;black&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;&lt;em&gt;Setting the variables on the html element means they will be accessible to everything else on our page, since everything will be a descendant of the html element.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;To use these variables we would then swap out the relevant hardcoded colours throughout our styles with a &lt;code&gt;var()&lt;/code&gt; value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;    .TextCard {
&lt;span class="gd"&gt;-      background: white;
&lt;/span&gt;&lt;span class="gi"&gt;+      background: var(--primaryBackgroundColor);
&lt;/span&gt;&lt;span class="gd"&gt;-      color: black;
&lt;/span&gt;&lt;span class="gi"&gt;+      color: var(--primaryTextColor);
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;      margin: 0;
      padding: 10px 20px;
      border-radius: 20px;
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we need to make the value of the variables change when dark mode is active. To do that we can use our query selector from earlier, but instead of applying it to each individual component, we’ll just use it once, targetting the html element:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;    html {
      --primaryBackgroundColor: white;
      --primaryTextColor: black;
    }
&lt;span class="gi"&gt;+
+    @media (prefers-color-scheme: dark) {
+      html {
+        --primaryBackgroundColor: black;
+        --primaryTextColor: white;
+      }
+    }
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that within the query selector the values of the two variables have swapped. When dark mode is active this change will propagate to everywhere we use the variables, instantly switching the colours of those elements.&lt;/p&gt;

&lt;p&gt;Extending this to other areas of your site is as easy as defining new variables, setting them to a different value within the dark mode media query, then swapping hardcoded colour values throughout your code for variables.&lt;/p&gt;

&lt;p&gt;Here’s a quick demo of this approach:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/nw/embed/bGdxbRj?height=600&amp;amp;default-tab=css,result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding an override button to toggle the theme
&lt;/h2&gt;

&lt;p&gt;At this point we’ve built out a pretty manageable, extremely lightweight way to respect a user’s system colour preferences. But what if want to give users more control and let them manually select the theme? Maybe they’re on a device that doesn’t support system-level dark mode, or maybe they want everything dark &lt;em&gt;except&lt;/em&gt; our website.&lt;/p&gt;

&lt;p&gt;To do that we’ll add a toggle button that not only allows the theme to be switched manually, but also automatically reflects the system-level preference.&lt;/p&gt;

&lt;p&gt;I chose to use the &lt;a href="https://github.com/aaronshaf/react-toggle" rel="noopener noreferrer"&gt;react-toggle&lt;/a&gt; library for the actual toggle button, but this should work with any toggle component — whether it’s from a library, a custom component you’ve built, or even a trusty &lt;code&gt;&amp;lt;checkbox&amp;gt;&lt;/code&gt; element.&lt;/p&gt;

&lt;p&gt;Here’s the code I started with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Toggle&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-toggle&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;DarkToggle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Toggle&lt;/span&gt;
          &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"DarkToggle"&lt;/span&gt;
          &lt;span class="na"&gt;icons&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;checked&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;🌙&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;unchecked&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;🔆&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;aria-label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Dark mode"&lt;/span&gt;
        &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’ll start by adding some state that controls whether the toggle is set to dark mode and connecting it up to the toggle:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gi"&gt;+   import React, { useState } from "react";
+   import Toggle from "react-toggle";
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;    export const DarkToggle = () =&amp;gt; {
&lt;span class="gi"&gt;+     const [isDark, setIsDark] = useState(true);
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;      return (
        &amp;lt;Toggle
          className="DarkToggle"
&lt;span class="gi"&gt;+         checked={isDark}
+         onChange={event =&amp;gt; setIsDark(event.target.checked)}
&lt;/span&gt;          icons={{ checked: "🌙", unchecked: "🔆" }}
          aria-label="Dark mode"
        /&amp;gt;
      );
    };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;If you’re not familiar with React’s &lt;code&gt;useState&lt;/code&gt; hook, it’s definitely worth having a look at the &lt;a href="https://reactjs.org/docs/hooks-intro.html" rel="noopener noreferrer"&gt;official hooks documentation&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you jump into the React dev tools you should be able to see the &lt;code&gt;isDark&lt;/code&gt; state updating when you click the toggle:&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%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fugxk0fds3h0okf9h50l5.gif" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fugxk0fds3h0okf9h50l5.gif" alt="Clicking the toggle causes the state shown in the React dev tools to switch between “true” and “false”."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now let’s add in some standards-based magic so that the toggle automatically matches the user’s &lt;em&gt;system&lt;/em&gt; dark mode setting. To do that we’ll use a great little React library called &lt;a href="https://github.com/contra/react-responsive" rel="noopener noreferrer"&gt;react-responsive&lt;/a&gt;. This lets you get the result of a CSS media query and have the value automatically update whenever the query result changes. It’s super useful stuff, all built around the standard JavaScript &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia" rel="noopener noreferrer"&gt;matchMedia function&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As you might’ve guessed the media query we’ll use is &lt;code&gt;prefers-color-scheme: dark&lt;/code&gt;. The code for that looks 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="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Toggle&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-toggle&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useMediaQuery&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-responsive&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;DarkToggle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;systemPrefersDark&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useMediaQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&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="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;prefersDark&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nf"&gt;setIsDark&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prefersDark&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;isDark&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setIsDark&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;systemPrefersDark&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;useMediaQuery&lt;/code&gt; hook takes two important arguments: the media query (the first argument), and a function (the third argument) to call whenever the result of the media query changes. We want to update our &lt;code&gt;isDark&lt;/code&gt; state whenever the media query changes, so that’s exactly what the code does.&lt;/p&gt;

&lt;p&gt;Now if you switch your system dark mode on and off, the toggle should automatically switch at the same time. Sweet!&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%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F0xbiw7e1l44hlj6vz7br.gif" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F0xbiw7e1l44hlj6vz7br.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;…But it’s not connected up to our CSS yet, so the toggle is pretty useless. To remedy that we’ll need to run some code whenever our &lt;code&gt;isDark&lt;/code&gt; state changes. React’s &lt;a href="https://reactjs.org/docs/hooks-effect.html" rel="noopener noreferrer"&gt;useEffect hook&lt;/a&gt; is perfect for this — we give it an function, tell it what properties it depends on (&lt;code&gt;isDark&lt;/code&gt; in this case), and then React handles calling the function whenever the property changes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="p"&gt;[...]&lt;/span&gt;

      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;isDark&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setIsDark&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;systemPrefersDark&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// whatever we put here will run whenever `isDark` changes&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;isDark&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="p"&gt;[...]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The other half of the puzzle requires a slight tweak to our CSS. There's no way our code can change the value of &lt;code&gt;prefers-color-scheme&lt;/code&gt;, which makes it hard to force dark mode with our current set-up. Instead, we're going to make our colour variables change whenever the HTML element has &lt;code&gt;dark&lt;/code&gt; class (which we'll dynamically add to the element in just a second):&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;html&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="py"&gt;--primaryBackgroundColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="py"&gt;--primaryTextColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;black&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="p"&gt;{&lt;/span&gt;
      &lt;span class="py"&gt;--primaryBackgroundColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;black&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="py"&gt;--primaryTextColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&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;Finally, let’s update the body of the &lt;code&gt;useEffect&lt;/code&gt; function to add (and remove) the &lt;code&gt;dark&lt;/code&gt; class based on whether &lt;code&gt;isDark&lt;/code&gt; is true:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;    import React, { useEffect, useState } from "react";
    import { useMediaQuery } from "react-responsive";
    import Toggle from "react-toggle";
&lt;span class="gi"&gt;+
+   const DARK_CLASS = "dark";
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;    export const DarkToggle = () =&amp;gt; {
      const systemPrefersDark = useMediaQuery(
        {
          query: "(prefers-color-scheme: dark)"
        },
        undefined,
        prefersDark =&amp;gt; {
          setIsDark(prefersDark);
        }
      );
&lt;span class="err"&gt;
&lt;/span&gt;      const [isDark, setIsDark] = useState(systemPrefersDark);
&lt;span class="gi"&gt;+
+     useEffect(() =&amp;gt; {
+       if (isDark) {
+         document.documentElement.classList.add(DARK_CLASS)
+       } else {
+         document.documentElement.classList.remove(DARK_CLASS)
+       }
+     }, [isDark]);
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;      return (
        &amp;lt;Toggle
          className="DarkToggle"
          checked={isDark}
          onChange={event =&amp;gt; setIsDark(event.target.checked)}
          icons={{ checked: "🌙", unchecked: "🔆" }}
          aria-label="Dark mode"
        /&amp;gt;
      );
    };
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🎉 And we’re done! The page’s theme should now automatically change whenever the toggle’s value is changed, either by clicking the toggle directly or by changing the system’s dark mode setting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Optional finishing touches
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Handling more complex styles
&lt;/h3&gt;

&lt;p&gt;I found that CSS variables were powerful enough to handle &lt;em&gt;nearly&lt;/em&gt; all the tweaks I needed to make on my website. However, there were still a few edge cases they couldn’t handle (or would be inconvenient for) — things like adding a subtle border or ever-so-slightly changing a shadow opacity so it would show up better in dark mode.&lt;/p&gt;

&lt;p&gt;For these cases I created a SCSS mixin that only applies styles when dark mode is active (similar to what we were doing before we introduced variables, where we had a media query directly in the CSS for each component). Using it looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;    &lt;span class="nc"&gt;.Card&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;backgroundPrimary&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nl"&gt;box-shadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;4px&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt; &lt;span class="nf"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;darken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$mint&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="m"&gt;15%&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="mi"&gt;.22&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="k"&gt;@include&lt;/span&gt; &lt;span class="nd"&gt;whenDark&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="c1"&gt;// styles to apply to the element when dark mode is active&lt;/span&gt;
         &lt;span class="nl"&gt;box-shadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;4px&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt; &lt;span class="nf"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;#000&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="mi"&gt;.5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code for the mixin itself uses the &lt;code&gt;&amp;amp;&lt;/code&gt; SCSS feature to reference the selector the mixin is called from, plus &lt;code&gt;@content&lt;/code&gt; to allow content to be passed into it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;    &lt;span class="k"&gt;@mixin&lt;/span&gt; &lt;span class="nf"&gt;whenDark&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="k"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;@content&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;&lt;em&gt;(If you dive into &lt;a href="https://codesandbox.io/s/reactscss-dark-mode-v6415" rel="noopener noreferrer"&gt;the sandbox code&lt;/a&gt; you’ll see I’m also using the mixin to set the colour variables, so all the CSS is using the same code to determine whether dark mode is active).&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Supporting users without JS
&lt;/h3&gt;

&lt;p&gt;By switching our CSS away from using the &lt;code&gt;prefers-color-scheme&lt;/code&gt; media query to instead relying on a class set from our JavaScript code, we inadvertently broke dark mode support for any users with JavaScript disabled. (if you’re not pre-rendering your site this won’t be an issue, since the site the site probably won't display for non-JS users anyway).&lt;/p&gt;

&lt;p&gt;Fortunately bringing back support is fairly straight forward if you’re using the mixin above — simply update it to also apply any styles when the media query is active:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;    &lt;span class="k"&gt;@mixin&lt;/span&gt; &lt;span class="nf"&gt;whenDark&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="k"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;@content&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefers-color-scheme&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;@content&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;h2&gt;
  
  
  Additional tips + resources
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Emulating dark mode
&lt;/h3&gt;

&lt;p&gt;Chrome’s dev tools let you emulate prefers-color-scheme values &lt;a href="https://developers.google.com/web/updates/2019/10/devtools#userpreferences" rel="noopener noreferrer"&gt;via the &lt;em&gt;Rendering&lt;/em&gt; tab&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you’re on a Mac, Safari’s dev tools also lets you flip to dark mode with the click of a button:&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%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fqw6wrkhp8a7xryx5qwyu.gif" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fqw6wrkhp8a7xryx5qwyu.gif" alt="screen recording showing the “dark mode” toggle being clicked, within the Elements panel of Safari’s dev tools"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Remembering the user’s preference
&lt;/h3&gt;

&lt;p&gt;This isn’t something I’ve done, but it’s certainly something worth exploring. If your site doesn’t already have a data-persistence solution, the &lt;a href="https://github.com/donavon/use-persisted-state" rel="noopener noreferrer"&gt;use-persisted-state&lt;/a&gt; hook would be a great fit for the dark mode toggle.&lt;/p&gt;

&lt;h3&gt;
  
  
  Querying dark-mode from outside the toggle
&lt;/h3&gt;

&lt;p&gt;In the set-up I’ve described the only thing that really knows if dark mode is active is the toggle component. For simple cases that’s all good and fine, but what if other pieces of your JavaScript need to behave differently based on the theme? Again, this isn’t something that I’ve needed, but any common option for sharing state should do the trick — whether it’s the &lt;a href="https://reactjs.org/docs/context.html" rel="noopener noreferrer"&gt;Context API&lt;/a&gt;, Redux or whatever your site is already using.&lt;/p&gt;

&lt;h3&gt;
  
  
  Browser support
&lt;/h3&gt;

&lt;p&gt;Bad news: our old friend Internet Explorer doesn’t support CSS variables. That means this approach isn’t going to look great there — all your variable-ized properties will fall back to their default/inherited values (e.g. probably black for text colour). If you do need to support IE, there are a few options — the main ones being &lt;a href="https://github.com/jhildenbiddle/css-vars-ponyfill" rel="noopener noreferrer"&gt;css-vars-ponyfill&lt;/a&gt; and &lt;a href="https://codepen.io/jakealbaugh/post/css4-variables-with-fallbacks-using-sass" rel="noopener noreferrer"&gt;SASS-based fallbacks&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;p&gt;Here are some other resources that you might find useful (I certainly did):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://css-tricks.com/lets-say-you-were-going-to-write-a-blog-post-about-dark-mode/" rel="noopener noreferrer"&gt;Let’s Say You Were Going to Write a Blog Post About Dark Mode &lt;/a&gt;— even if you’re not writing a blog post about dark mode, this is an excellent starting point for diving into the edge cases, accessibility concerns and other points I haven’t really covered (sorry Chris!)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://color.review/" rel="noopener noreferrer"&gt;Color.review&lt;/a&gt; —my absolute favourite site for choosing accessible colour combos.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.smashingmagazine.com/2018/05/css-custom-properties-strategy-guide/" rel="noopener noreferrer"&gt;A Strategy Guide To CSS Custom Properties&lt;/a&gt; — A great article on how to strategically use and think about CSS variables.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://blog.superhuman.com/how-to-design-delightful-dark-themes-7b3da644ff1f" rel="noopener noreferrer"&gt;How to design delightful dark themes&lt;/a&gt; — handy things to keep in mind when designing a dark theme.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://nandovieira.com/supporting-dark-mode-in-web-content" rel="noopener noreferrer"&gt;Supporting dark mode in web content&lt;/a&gt; —some useful tips in here for dealing with images in dark mode.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Wowee, what a journey! Thanks for making it this far, and if you found any of this useful I’d love to see what you’ve built!&lt;/p&gt;

</description>
      <category>darkmode</category>
      <category>react</category>
      <category>hooks</category>
      <category>sass</category>
    </item>
  </channel>
</rss>
