<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
  <channel>
    <title>All posts</title>
    <description>Posts from sashaqred.com</description>
    <language>en</language>
    <link >https://sashaqred.com/en/</link>

    <atom:link href="https://sashaqred.com/en/feed.xml" rel="self" type="application/rss+xml"/>
      <item>
        <title>How to add a dark theme to a web app without begging a designer for help</title>
        <link>https://sashaqred.com/en/blog/add-a-dark-theme-without-a-designer/</link>
        <description>
          <![CDATA[
            <p>Have you ever been in a situation where you needed to make a dark theme for your new app, but there was no designer to help? You are on a deadline, and, seemingly, there is no hope to finish the project in time. If you find yourself in this situation now, you are in the right place to find the way out of it.<p>From this article, you will know how to create a dark theme for your app without a designer’s help. Without further ado, let’s dive into the process.</p></p>
            <h2 id="add-a-dark-theme" tabindex="-1">Add a Dark Theme</h2>
<p>Adding the themes is a quite easy process that is done in 3 steps:</p>
<p>Step 1 — choose the colors:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">:root</span> <span class="token punctuation">{</span>
  <span class="token property">--primary-color</span><span class="token punctuation">:</span> #2e3a59<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Step 2 — add a use case:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">html</span> <span class="token punctuation">{</span>
  <span class="token property">color</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--primary-color<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Step 3 — add a new theme:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">:root.other-theme</span> <span class="token punctuation">{</span>
  <span class="token property">--primary-color</span><span class="token punctuation">:</span> #8f9bb3<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>If you cannot use <a href="https://caniuse.com/css-variables">CSS Custom Properties</a>, you will have to write a few cycles on SASS. You can’t call this task a piece of cake, but we’ll manage to complete it.</p>
<p>But there’s a catch — you can have a few themes only if a designer made it for you first. Although you don’t always have one by hand, you want to add a dark theme to a default one. Accessibility matters.</p>
<p>Once, I participated in a project where a designer worked only at an early stage. All the designers were busy, so we couldn’t count on them after they finished their part. But at the end of the project came an idea to make it better by adding a dark theme.</p>
<p>Creating the first version, we didn’t want to add complex logic and additional controls. Therefore, we decided not to allow a user to change the theme. We had to act in line with system settings because it was the only way to keep the first option simple.</p>
<h2 id="define-the-rules-for-the-dark-theme" tabindex="-1">Define the rules for the Dark Theme</h2>
<p>Now we need to know when to display the dark theme. There is a special CSS media inquiry for that purpose: <code class="language-">prefers-color-scheme</code>.
Support is practically the same as in CSS Custom Properties (<a href="https://caniuse.com/prefers-color-scheme">https://caniuse.com/prefers-color-scheme</a>).</p>
<pre class="language-css"><code class="language-css"><span class="token atrule"><span class="token rule">@media</span> <span class="token punctuation">(</span><span class="token property">prefers-color-scheme</span><span class="token punctuation">:</span> dark<span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
<span class="token punctuation">}</span></code></pre>
<h2 id="what-about-the-colors" tabindex="-1">What about the colors?</h2>
<p>Alright, we have established how to define what theme to display. But what to do if there are no colors for the dark theme? And there is the answer to that question too. We can apply color inverse filters to the whole page. Again, support will be similar to CSS Custom Properties (<a href="https://caniuse.com/css-filters">https://caniuse.com/css-filters</a>).</p>
<pre class="language-css"><code class="language-css"><span class="token atrule"><span class="token rule">@media</span> <span class="token punctuation">(</span><span class="token property">prefers-color-scheme</span><span class="token punctuation">:</span> dark<span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
  <span class="token selector">:root</span> <span class="token punctuation">{</span>
    <span class="token property">filter</span><span class="token punctuation">:</span> <span class="token function">invert</span><span class="token punctuation">(</span>100%<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>Now let’s add a text on the page. Looks sharp, huh? Then we add a list, a citation, and a random text item — they look just as good as the previous one. Even so, the moment we add a picture, it turns negative.</p>
<p><picture><source type="image/webp" srcset="/generated_images/wUCEoT7GdB-600.webp 600w"><source type="image/avif" srcset="/generated_images/wUCEoT7GdB-600.avif 600w"><img src="/generated_images/wUCEoT7GdB-600.jpeg" alt="The UI before &amp; after we've made an inversion" loading="lazy" decoding="async" width="600" height="500"></picture></p>
<p>At this stage, we invert the whole page. It means that <strong>all</strong> the elements will change their color to the opposite. The inversion works well with the text, but I don’t want my photos to turn negative. To avoid that, you need to add an exception.</p>
<pre class="language-css"><code class="language-css"><span class="token atrule"><span class="token rule">@media</span> <span class="token punctuation">(</span><span class="token property">prefers-color-scheme</span><span class="token punctuation">:</span> dark<span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
  <span class="token selector">:root,
  :root img</span> <span class="token punctuation">{</span>
    <span class="token property">filter</span><span class="token punctuation">:</span> <span class="token function">invert</span><span class="token punctuation">(</span>100%<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>But in this case, it would rather be a reinversion than an exception. Now, we’ve applied “reinversion” for images only. If you have a video or iFrame, or other content that is not to be reversed, you should apply the same rule to them too.</p>
<p><picture><source type="image/webp" srcset="/generated_images/B8YBXJCA22-600.webp 600w"><source type="image/avif" srcset="/generated_images/B8YBXJCA22-600.avif 600w"><img src="/generated_images/B8YBXJCA22-600.jpeg" alt="The UI after the reinversion" loading="lazy" decoding="async" width="600" height="1000"></picture></p>
<p>Excellent!</p>
<p>But you will ask me: “why do we need to apply a filter to <code class="language-">:root</code> and reverse visual elements to their initial state?”. Why can’t we just write the following CSS code:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">*:not(img)</span> <span class="token punctuation">{</span>
  <span class="token property">filter</span><span class="token punctuation">:</span> <span class="token function">invert</span><span class="token punctuation">(</span>100%<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Selector will allow us to apply the filter to all the elements except images. Although, you should consider that the filter has some peculiarities. Let’s look at them in the case of the page layout below:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span><span class="token punctuation">></span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>
    Text in p
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span><span class="token punctuation">></span></span>and in span.<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>span</span><span class="token punctuation">></span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span>
  With picture of me:
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>img</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>./pic.jpg<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span></code></pre>
<ol>
<li>So as <code class="language-">div</code> matches our selector, the filter will apply to it. As a result, everything will be negative.</li>
<li>Since <code class="language-">p</code> is inside the <code class="language-">div</code>, it matches the selector and will change too. Thus, we will have it reinverted so that the colors get back to normal.</li>
<li>The <code class="language-">span</code> also matches the selector. Whereas the <code class="language-">p</code> has become normal, the contents of the <code class="language-">span</code> went negative.</li>
<li>Finally, the <code class="language-">img</code> selector is not influenced by the filter, so no inversion happens. However, as the <code class="language-">img</code> is a part of the element that we have inverted, the image will be negative.</li>
</ol>
<p>That is how it will look like on your screen:</p>
<p><picture><source type="image/webp" srcset="/generated_images/ma0FPUS--m-600.webp 600w"><source type="image/avif" srcset="/generated_images/ma0FPUS--m-600.avif 600w"><img src="/generated_images/ma0FPUS--m-600.jpeg" alt="Broken invert" loading="lazy" decoding="async" width="600" height="448"></picture></p>
<p>That’s why we apply the filter to the <code class="language-">root</code> element once. Then, we invert the elements that we want to stay the same, again to bring them to the initial state.</p>
<p>Done! The first version works just fine, and the client is happy.</p>
<p>In the second stage of the process, we add a switch for our themes.</p>
<p>To do this, we’ll add <code class="language-">select</code> to the header of our app, allowing users to choose the theme they prefer. The select will include 3 choices: <code class="language-">light</code>, <code class="language-">dark</code>, and <code class="language-">auto</code>.</p>
<p>Therefore, the <code class="language-">auto</code> will be a default value that will allow the app to inherit system settings. At the same time, when choosing <code class="language-">light</code> or <code class="language-">dark</code>, we are adding a <code class="language-">theme-light</code> or <code class="language-">theme-dark</code> class correspondingly to the <code class="language-">root</code> element of a document. Then, we delete a theme class if using <code class="language-">auto</code>. To top it off, we save these settings in <code class="language-">localStorage</code>, which allows us to show the previous theme.</p>
<p>After we’ve added a few classes, we will modify CSS:</p>
<pre class="language-css"><code class="language-css"><span class="token atrule"><span class="token rule">@media</span> screen <span class="token keyword">and</span> <span class="token punctuation">(</span><span class="token property">prefers-color-scheme</span><span class="token punctuation">:</span> dark<span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
  <span class="token selector">:root:not(.theme-light):not(.theme-dark),
  :root:not(.theme-light):not(.theme-dark) img</span> <span class="token punctuation">{</span>
    <span class="token property">filter</span><span class="token punctuation">:</span> <span class="token function">invert</span><span class="token punctuation">(</span>100%<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token selector">:root.theme-dark,
:root.theme-dark img</span> <span class="token punctuation">{</span>
  <span class="token property">filter</span><span class="token punctuation">:</span> <span class="token function">invert</span><span class="token punctuation">(</span>100%<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<h2 id="wrap-up" tabindex="-1">Wrap up</h2>
<p>That’s it! You are ready to add the dark theme to your app without a designer’s help. But before you go and nail it, take a glance at the resume of the article to memorize the main points better.</p>
<p>We have the following process logic:</p>
<ul>
<li>If the light theme is chosen — do nothing;</li>
<li>If the dark one — always invert;</li>
<li>If neither a dark nor light theme is chosen — invert depending on the system's settings.</li>
</ul>
<p>“Generating” a dark theme by applying a filter is quite easy and quick.</p>
<p>Although, there can be some nuances regarding the page contents that are not to be inverted. In the majority of cases, you solve that problem by simple reinversion of such objects. But it doesn’t always work, so we need to use some hacks.</p>
<p>However, there are some drawbacks of this method:</p>
<ul>
<li>Not all of the colors invert “aesthetically”, which can tarnish the UI a little bit;</li>
<li>You cannot change the hues of light and dark to create different visual experiences of the interface;</li>
<li>And the last but not least thing to remember is that all of the colors get inverted. Thereby, your “brand colors” will change too, which can harm your corporate identity.</li>
</ul>
<p>Regardless of all the drawbacks, it is still a credible and fast way to add a dark theme to your app. Besides, it can be helpful when you want to experiment with different themes, make PoC (Proof of concept), and see if it suits your application.</p>

          ]]>
        </description>
        <pubDate>Mon, 07 Jun 2021 00:00:00 GMT</pubDate>
        <guid>https://sashaqred.com/en/blog/add-a-dark-theme-without-a-designer/</guid>
      </item>

  </channel>
</rss>
