<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Michael Großklaus</title>
    <description>The latest articles on DEV Community by Michael Großklaus (@mgrsskls).</description>
    <link>https://dev.to/mgrsskls</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%2F212400%2F40ec36c1-bd55-4b19-b5b4-29255293e620.jpeg</url>
      <title>DEV Community: Michael Großklaus</title>
      <link>https://dev.to/mgrsskls</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mgrsskls"/>
    <language>en</language>
    <item>
      <title>Mobile First versus Shared First CSS</title>
      <dc:creator>Michael Großklaus</dc:creator>
      <pubDate>Sat, 18 Feb 2023 00:00:00 +0000</pubDate>
      <link>https://dev.to/mgrsskls/mobile-first-versus-shared-first-css-38g9</link>
      <guid>https://dev.to/mgrsskls/mobile-first-versus-shared-first-css-38g9</guid>
      <description>&lt;p&gt;In this post I want to give a short example why there is in my opinion a better way than using a &lt;em&gt;Mobile First&lt;/em&gt; approach when writing CSS. Please note that for simplification I will refer to &lt;em&gt;viewports&lt;/em&gt; as well as &lt;em&gt;media queries&lt;/em&gt;, but the same concept also applies to anything that can be controlled by media and container queries.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mobile First
&lt;/h2&gt;

&lt;p&gt;When Responsive Web Design and media queries became a thing, it was considered best practice to follow a &lt;em&gt;Mobile First&lt;/em&gt; approach when writing CSS for a website. Besides being aligned with the conceptual &lt;em&gt;Mobile First&lt;/em&gt; approach (where you — in simple terms — first make sure that your &lt;em&gt;content&lt;/em&gt; and its structure works on small devices), this also had the advantage to support browsers that did not yet support media queries. For those you got the mobile view.&lt;/p&gt;

&lt;p&gt;An example for that would be something like this simple page layout:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;main&amp;gt;&amp;lt;/main&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;aside&amp;gt;&amp;lt;/aside&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;flex-direction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;column&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;gap&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="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* medium */&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;min-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;40em&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="p"&gt;{&lt;/span&gt;
        &lt;span class="py"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2rem&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="c"&gt;/* large */&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;min-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;60em&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="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;flex-direction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="py"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nt"&gt;main&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nt"&gt;aside&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case we first use &lt;code&gt;flex&lt;/code&gt; with columns, so that the &lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;aside&amp;gt;&lt;/code&gt; elements are below each other. This could be done without defining any CSS at all as they are both block elements, which would be rendered in a column anyway. But using &lt;code&gt;flex&lt;/code&gt; allows us to use &lt;code&gt;gap&lt;/code&gt; which is unfortunately not (yet) supported on block elements.&lt;/p&gt;

&lt;p&gt;In the first media query we increase this &lt;code&gt;gap&lt;/code&gt; for medium viewports.&lt;/p&gt;

&lt;p&gt;In the second media query we then set &lt;code&gt;flex-direction&lt;/code&gt; to its initial value &lt;code&gt;row&lt;/code&gt; , so the &lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;aside&amp;gt;&lt;/code&gt; elements are horizontally aligned. We also redeclare the &lt;code&gt;gap&lt;/code&gt;. Additionally we set &lt;code&gt;flex&lt;/code&gt; values for our two elements, so that &lt;code&gt;main&lt;/code&gt; takes more space than &lt;code&gt;aside&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you look at the media query for medium viewports, you also notice that the media query only uses &lt;code&gt;min-width&lt;/code&gt;, so it is basically open ended and has no limit in terms of width. As you can see all we do in there is defining the &lt;code&gt;gap&lt;/code&gt; (for medium viewports), which we then overwrite again in the next media query (for large viewports).&lt;/p&gt;

&lt;p&gt;Besides the fact that it is not necessary anymore to go for a &lt;em&gt;Mobile First&lt;/em&gt; approach when it comes to CSS since media queries have been supported in all major browsers for a long time now, this approach also comes with two downsides:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Error prone:&lt;/strong&gt; If we want to add or change CSS for small or medium viewports, this CSS would bleed up, i.e. affecting larger viewports. This means we might introduce a bug, if we do not undo our changes for larger viewports. So, writing CSS code this way is very error-prone because changes might have unintended side-effects.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Difficult to inspect:&lt;/strong&gt; Inspecting why something behaves a certain way becomes more difficult. You end up with properties being overwritten, sometimes even multiple times. This can be seen when looking at the devtools where you would have lots of striked through properties. This is a screenshot of the devtools for large viewports:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3dzobaPl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.mgrossklaus.de/notes/2023-02-18-mobile-first-versus-shared-first-css/devtools-mobile-first.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3dzobaPl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.mgrossklaus.de/notes/2023-02-18-mobile-first-versus-shared-first-css/devtools-mobile-first.webp" alt="Screenshot of Safari devtools showing multiple striked through CSS declarations" width="452" height="426"&gt;&lt;/a&gt;&lt;br&gt;This is of course a short example, but imagine how this quickly escalates in an actual, more complex website. Seeing lots of overwritten properties like this is usually a good indicator that the CSS is prone to errors and might be worth a refactoring.&lt;/p&gt;

&lt;h2&gt;
  
  
  Shared First
&lt;/h2&gt;

&lt;p&gt;So, how can we change our CSS to avoid these issues? What works really well for me is an approach that I would call, for lack of a better name, &lt;em&gt;Shared First&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;In this approach you only define CSS outside of media queries which should be &lt;strong&gt;shared by viewport sizes&lt;/strong&gt; and everything that should only be applied for certain viewport sizes goes into a media query that matches only that viewport size:&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="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* small + medium */&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;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;59.96875em&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="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;flex-direction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;column&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="c"&gt;/* small */&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;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;39.96875em&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="p"&gt;{&lt;/span&gt;
        &lt;span class="py"&gt;gap&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="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* medium */&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;min-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;40em&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;59.96875em&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="p"&gt;{&lt;/span&gt;
        &lt;span class="py"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2rem&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="c"&gt;/* large */&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;min-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;60em&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="p"&gt;{&lt;/span&gt;
        &lt;span class="py"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nt"&gt;main&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nt"&gt;aside&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&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;Let's break this down:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Outside of all media queries, we first define everything, that is shared: in our case, all we do is turning &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; into a &lt;code&gt;flex&lt;/code&gt; element as this is the only thing that is shared throughout all viewport sizes.&lt;/li&gt;
&lt;li&gt;The first media queries covers small and medium viewports. In there all we define is that for these sizes we want to use a column layout. This makes sure that we later do not have to revert this.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;gap&lt;/code&gt; is then defined in each media query: We explicitely define it once for small, once for medium and once for large viewports. This allows us to change the values without accidentally affecting other viewport sizes.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Admittedly, these are a few more lines than in the &lt;em&gt;Mobile First&lt;/em&gt; approach, but shorter code does not always mean better code. Instead shorter code might be harder to understand and more difficult to maintain.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why does this work better?
&lt;/h3&gt;

&lt;p&gt;First of all, by declaring CSS only for the resolutions we actually need it, we avoid side effects.&lt;br&gt;
Let's say on small viewports we now want to change the font size, we can do that by adding it to the media query for small viewports without any side effects. In the &lt;em&gt;Mobile First&lt;/em&gt; approach this would have caused a regression if we did not revert that definition for larger viewports.&lt;/p&gt;

&lt;p&gt;Second of all, inspecting this is much easier. This is how devtools would look for our &lt;em&gt;Shared First&lt;/em&gt; example for large viewports:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ArRXkh_o--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.mgrossklaus.de/notes/2023-02-18-mobile-first-versus-shared-first-css/devtools-shared-first.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ArRXkh_o--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.mgrossklaus.de/notes/2023-02-18-mobile-first-versus-shared-first-css/devtools-shared-first.webp" alt="Screenshot of Safari devtools showing only to CSS declarations, none of them are striked throught" width="450" height="220"&gt;&lt;/a&gt;&lt;br&gt;
Much cleaner, isn't it?&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Using a &lt;em&gt;Mobile First&lt;/em&gt; approach when writing CSS is error prone and often leads to unintended side effects. It is also more difficult to investigate what is happening in the CSS.&lt;br&gt;
This can be avoided using a &lt;em&gt;Shared First&lt;/em&gt; approach where only the CSS, that should be applied for all viewport sizes, is defined outside of media queries. The CSS, that should only be applied for certain viewport sizes, will go into media queries that are defined to match only those exact viewport sizes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Disclaimer
&lt;/h2&gt;

&lt;p&gt;I know that there might be shorter and easier ways to write this layout example using custom properties, but this is simply an example to illustrate the issues and benefits.&lt;/p&gt;

</description>
      <category>css</category>
    </item>
    <item>
      <title>Implementing a theme toggle with HTML and CSS only (almost)</title>
      <dc:creator>Michael Großklaus</dc:creator>
      <pubDate>Sat, 04 Feb 2023 00:00:00 +0000</pubDate>
      <link>https://dev.to/mgrsskls/implementing-a-theme-toggle-with-html-and-css-only-almost-4an1</link>
      <guid>https://dev.to/mgrsskls/implementing-a-theme-toggle-with-html-and-css-only-almost-4an1</guid>
      <description>&lt;p&gt;Implementing a light and dark theme based on the user's OS settings has become quite popular. But often users just want their OS to be dark, while they prefer reading a website with a light background, for example. It is therefore considered good practice to offer a theme toggle, that allows the user to change the theme of the website regardless of their OS theme.&lt;/p&gt;

&lt;p&gt;To implement that, we would usually have some HTML like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;form&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;fieldset&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;legend&amp;gt;&lt;/span&gt;Color scheme&lt;span class="nt"&gt;&amp;lt;/legend&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"radio"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"theme"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"auto"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"theme-auto"&lt;/span&gt; &lt;span class="na"&gt;checked&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"theme-auto"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;auto&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"radio"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"theme"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"light"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"theme-light"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"theme-light"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;light&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"radio"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"theme"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"dark"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"theme-dark"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"theme-dark"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;dark&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/fieldset&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using JavaScript we would then listen to the &lt;code&gt;change&lt;/code&gt; event of those inputs and add a class to the &lt;code&gt;html&lt;/code&gt; or &lt;code&gt;body&lt;/code&gt; node. Based on that class, we would style the page accordingly:&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;.theme-dark&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;…&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using the &lt;code&gt;:has()&lt;/code&gt; pseudo-class, we can be implement this theme toggle without JavaScript. Instead of using a class, we can do 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="nd"&gt;:has&lt;/span&gt;&lt;span class="o"&gt;([&lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"theme"&lt;/span&gt;&lt;span class="o"&gt;][&lt;/span&gt;&lt;span class="nt"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"dark"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="nd"&gt;:checked&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;…&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To make sure that the user does not have to change the theme on every page visit, we should of course still use some JavaScript:&lt;/p&gt;

&lt;p&gt;We save the selection in the &lt;code&gt;localStorage&lt;/code&gt;, but then — instead of adding a theme class — we simply set the &lt;code&gt;checked&lt;/code&gt; attribute of the correct input in the theme toggle. Ideally we do this in an inline script (as opposed to some asynchronously fetched script) to avoid any possible layout flickering.&lt;/p&gt;

&lt;h2&gt;
  
  
  Complete example
&lt;/h2&gt;

&lt;h3&gt;
  
  
  HTML
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;form&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;fieldset&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"ThemeToggle"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;legend&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"ThemeToggle-title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Color scheme&lt;span class="nt"&gt;&amp;lt;/legend&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"radio"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"theme"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"auto"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"theme-auto"&lt;/span&gt; &lt;span class="na"&gt;checked&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"theme-auto"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;auto&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"radio"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"theme"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"light"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"theme-light"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"theme-light"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;light&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"radio"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"theme"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"dark"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"theme-dark"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"theme-dark"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;dark&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;    
  &lt;span class="nt"&gt;&amp;lt;/fieldset&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;renderTheme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;auto&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="s2"&gt;light&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="s2"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;theme&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;checkedInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`theme-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;checkedInput&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;checkedInput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;checked&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;renderTheme&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;theme&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  CSS
&lt;/h3&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="err"&gt;…&lt;/span&gt; &lt;span class="c"&gt;/* styling for light theme */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;html&lt;/span&gt;&lt;span class="nd"&gt;:has&lt;/span&gt;&lt;span class="o"&gt;([&lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"theme"&lt;/span&gt;&lt;span class="o"&gt;][&lt;/span&gt;&lt;span class="nt"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"light"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="nd"&gt;:checked&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;…&lt;/span&gt; &lt;span class="c"&gt;/* styling for light theme */&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="nt"&gt;html&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="err"&gt;…&lt;/span&gt; &lt;span class="c"&gt;/* styling for dark theme */&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="nd"&gt;:has&lt;/span&gt;&lt;span class="o"&gt;([&lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"theme"&lt;/span&gt;&lt;span class="o"&gt;][&lt;/span&gt;&lt;span class="nt"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"dark"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="nd"&gt;:checked&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;…&lt;/span&gt; &lt;span class="c"&gt;/* styling for dark theme */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  JavaScript
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.ThemeToggle input&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;change&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;onThemeChange&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;onThemeChange&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;target&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;value&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;renderTheme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;saveTheme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;saveTheme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;auto&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;theme&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;else&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="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;theme&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;theme&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;
  
  
  Browser support
&lt;/h2&gt;

&lt;p&gt;At the time of writing this (04.02.2023), &lt;code&gt;:has()&lt;/code&gt; is supported by all major browsers except for Firefox.&lt;/p&gt;

&lt;p&gt;To make sure this also works in Firefox, you can simply combine this approach with the class-based approach mentioned earlier — and throw it away as soon as Firefox turns green! :) (&lt;a href="https://caniuse.com/css-has" rel="noopener noreferrer"&gt;:has() on caniuse.com&lt;/a&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  Important
&lt;/h3&gt;

&lt;p&gt;Just be aware that you cannot combine both selectors as &lt;code&gt;:has()&lt;/code&gt; is invalid in Firefox. This means that the following snippet would not work at all in Firefox:&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;.theme-dark&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt;&lt;span class="nd"&gt;:has&lt;/span&gt;&lt;span class="o"&gt;([&lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"theme"&lt;/span&gt;&lt;span class="o"&gt;][&lt;/span&gt;&lt;span class="nt"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"dark"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="nd"&gt;:checked&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;…&lt;/span&gt; &lt;span class="c"&gt;/* styling for dark theme */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead you have to use the selectors separately:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.theme-dark {
  … /* styling for dark theme */
}

html:has([name="theme"][value="dark"]:checked) {
  … /* styling for dark theme */
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Optimizing CSS for theme toggles
&lt;/h2&gt;

&lt;p&gt;Since it can be a bit cumbersome to write CSS for theme toggles as you would have to duplicate some code in your CSS,&lt;a href="https://www.fynn.be/" rel="noopener noreferrer"&gt;Fynn&lt;/a&gt; wrote down a nice approach using custom properties, which I highly recommend: &lt;a href="https://fynn.be/blog/css-custom-property-toggles-themes/" rel="noopener noreferrer"&gt;CSS Custom Property toggles for themes&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>html</category>
      <category>css</category>
      <category>javascript</category>
    </item>
    <item>
      <title>From design to code</title>
      <dc:creator>Michael Großklaus</dc:creator>
      <pubDate>Tue, 13 Apr 2021 07:49:40 +0000</pubDate>
      <link>https://dev.to/factorial-io/from-design-to-code-4f72</link>
      <guid>https://dev.to/factorial-io/from-design-to-code-4f72</guid>
      <description>&lt;p&gt;Since 2020, we at &lt;a href="https://www.factorial.io/"&gt;Factorial&lt;/a&gt; do not only offer web development, but also conception and design for all kinds of web projects. And while in the past designing and developing websites has usually been a quite separate process, there are nowadays powerful tools for cooperation between designers and developers. At Factorial we try to make use of these tools as much as possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Designing in Figma
&lt;/h2&gt;

&lt;p&gt;Our designers work with &lt;em&gt;&lt;a href="https://www.figma.com/"&gt;Figma&lt;/a&gt;&lt;/em&gt;, a web-based design tool developed for easy collaboration with developers. We will show later how we actually make use of that, but for now let us have a look at how we structure the projects.&lt;/p&gt;

&lt;p&gt;Usually we create five so called “Pages” in &lt;em&gt;Figma&lt;/em&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Design Tokens&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Elements&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Patterns&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Template components&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Templates&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Design Tokens
&lt;/h3&gt;

&lt;p&gt;Design tokens are a set of styles — colors, typography, size and effect variables that are used across the product — to create a consistent and simple design language.&lt;/p&gt;

&lt;p&gt;On the &lt;em&gt;Design Tokens&lt;/em&gt; page, we create four different art boards: &lt;em&gt;Colors&lt;/em&gt;, &lt;em&gt;Typography&lt;/em&gt;, &lt;em&gt;Spacings&lt;/em&gt;, &lt;em&gt;Other&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7fvexllfmbl71mpyisg4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7fvexllfmbl71mpyisg4.png" alt="Screenshot of design tokens in Figma" width="800" height="484"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Colors&lt;/em&gt; can contain colors for typography but also decorational colors. It is important for us to separate these two to make sure that certain colors should only be used for certain things. E.g. a light grey might be good for background or an outline, but not good for typography as the contrast ratio could fail, especially for visually impaired users.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Spacings&lt;/em&gt; contain a list of pixel based spacing values (which will later be converted to rem in CSS), usually starting at 4 and containing not more than 8-10 values.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Typography&lt;/em&gt; lists all different types of text styles: headlines, copy, highlighted text, links and so on.&lt;/p&gt;

&lt;p&gt;On &lt;em&gt;Other&lt;/em&gt; we define values for elevation, borders, radiuses or certain effect styles needed for i.e. hover effects or overlays.&lt;/p&gt;

&lt;h3&gt;
  
  
  Elements, Patterns, Template Components and Templates
&lt;/h3&gt;

&lt;p&gt;The pages &lt;em&gt;Elements&lt;/em&gt;, &lt;em&gt;Patterns&lt;/em&gt;, &lt;em&gt;Template Components&lt;/em&gt; and &lt;em&gt;Templates&lt;/em&gt; are where the actual designs are located.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffx5edbn53u5p5oqi1cel.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffx5edbn53u5p5oqi1cel.png" alt="Screenshot of an elements page in Figma" width="800" height="484"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On &lt;em&gt;Elements&lt;/em&gt; we have small components which can be reused everywhere. These are usually components like buttons, icons, form elements, images, etc..&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Patterns&lt;/em&gt; are combinations of &lt;em&gt;elements&lt;/em&gt;, but not only. They are rather complex components like an image gallery, rich text, a slide show and so on. These components can later be picked by the editors of a CMS to compose the content of a website, e.g. using Drupal’s Layout Builder.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Template components&lt;/em&gt; are the single parts of a template: header, footer, navigation, search results, sidebar etc.. These elements can not be picked by editors, but are instead a fixed part of &lt;em&gt;templates&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Templates&lt;/em&gt; bring everything together: They connect &lt;em&gt;template components&lt;/em&gt; and &lt;em&gt;patterns&lt;/em&gt; and represent completes pages like a homepage, product page, search results, etc..&lt;/p&gt;

&lt;h2&gt;
  
  
  Importing design tokens via Figma API
&lt;/h2&gt;

&lt;p&gt;When naming pages, art boards and tokens in &lt;em&gt;Figma&lt;/em&gt;, it is very important that we follow a certain structure and naming convention. Not only does that make communication and reusability easier, it also allows us to use the Figma API to import our design tokens and components directly into our projects.&lt;/p&gt;

&lt;p&gt;Our frontend stack, which is part of every new project, contains a small script, which lets us do exactly that. When executing it, it will use the Figma API to connect to our Figma file, receive a JSON response containing all the necessary information about the designs and then create&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a &lt;em&gt;tokens.css&lt;/em&gt; file, which contains CSS custom properties based on the &lt;em&gt;Design Tokens&lt;/em&gt; page in our figma file,&lt;/li&gt;
&lt;li&gt;empty components based on the other four pages.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The custom properties are named accordingly to the design tokens in the Figma file. This makes sure that designers and developers talk about the same things and it simply makes it easier to remember how things are named. Since this file is auto-generated, it should never be changed manually. As soon as we run the import again, this file would be overwritten.&lt;/p&gt;

&lt;p&gt;For elements, patterns, template components and templates, the script creates empty components in the same hierarchy. If the Figma file e.g. contains a button component on the &lt;em&gt;Elements&lt;/em&gt; artboard, it would create&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;elements
└── button
    ├── button.css
    ├── button.twig
    ├── mocks.yaml
    └── schema.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It would only do that if this component does not exist yet to avoid overwriting components, which have already been implemented.&lt;/p&gt;

&lt;p&gt;While the button.css and button.twig (twig is template language of our choice) are part of the actual component, the mocks.yaml and schema.yaml are important for collaboration with backend developers and for our component library.&lt;/p&gt;

&lt;h2&gt;
  
  
  Developing in miyagi
&lt;/h2&gt;

&lt;p&gt;After having used &lt;em&gt;Pattern Lab&lt;/em&gt; for many years, it met our requirements as a component library less and less. It is a proven, well-working tool, but it started feeling too heavy and too slow for us. We also had increasing needs, which our component library should fulfil and Pattern Lab was simply not able to do that. After evaluating lots of different tools and not being able to find just one tool which would be able to deliver everything we need, I ended up creating a tool myself to serve that purpose. It is called &lt;em&gt;&lt;a href="https://www.miyagi.dev/"&gt;miyagi&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyqlznwbyz7oy6h7m7vca.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyqlznwbyz7oy6h7m7vca.png" alt="Screenshot of our Frontend Starter Kit in miyagi" width="800" height="508"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;miyagi&lt;/em&gt; is a node module, using an express server and can basically fit to any project structure you have. You can name your folders and components any way you want, nest them as deep as you want, use (almost) any kind of JavaScript templating language and all it needs in your project is one configuration file (it actually even works without a configuration file by trying to guess your template language, but you need to follow a certain naming convention for your folders and components then). This allows us to use it in different kinds of projects while keeping the project structure completely independent from our component library. If we ever want to use a different one, all we need to do is remove the configuration file.&lt;/p&gt;

&lt;h3&gt;
  
  
  Design tokens
&lt;/h3&gt;

&lt;p&gt;When you open the start page of a &lt;em&gt;miyagi&lt;/em&gt; instance, you will get a representation of our design tokens: &lt;em&gt;miyagi&lt;/em&gt; is able to parse our CSS files, look for custom properties with a certain naming (which we get out of the box thanks to our token importer) and create a nice overview of all design tokens we have in a project.&lt;/p&gt;

&lt;h3&gt;
  
  
  Customer theme
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;miyagi&lt;/em&gt; can also be styled in the CI of our customers: fonts, colors, a logo and basically any CSS can be added to give the customer the feeling that it is &lt;em&gt;their&lt;/em&gt; component library.&lt;/p&gt;

&lt;h3&gt;
  
  
  The schema files
&lt;/h3&gt;

&lt;p&gt;The schema files are &lt;a href="https://json-schema.org/"&gt;JSON schema&lt;/a&gt; files. We use them to agree on a certain data structure for a component, to make sure that the provided data by a backend service actually fits to that component. But they also allow our designers to come up with certain restrictions, e.g. a given text should only be 200 characters long etc..&lt;/p&gt;

&lt;p&gt;While these schema files are independent from a component library, they are still used by &lt;em&gt;miyagi&lt;/em&gt; to validate the mock data: If the given mock data for a component does not fit to the given JSON schema, it will render an error.&lt;/p&gt;

&lt;h3&gt;
  
  
  The mock files
&lt;/h3&gt;

&lt;p&gt;One aspect of component libraries is usually that they allow encapsulated and indepenent development of components. This means that frontend developers can build a component without having to wait for a backend service, which provides them data or an environment to render this component.&lt;br&gt;
To do that in &lt;em&gt;miyagi&lt;/em&gt;, we can create mock files to provide dummy data for our components. This can be static using yaml or json, but you can also use JavaScript files, which asynchronously export data coming from some API, for example. That way we can have many different variants of a component to make sure that the components always work, no matter how the data which is passed to the component looks like.&lt;/p&gt;

&lt;p&gt;Based on these mock files, &lt;em&gt;miyagi&lt;/em&gt; creates a separate view for every variant. We use these views also for our visual regression tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing for visual regressions using our component library
&lt;/h2&gt;

&lt;p&gt;To make sure that we do not accidentally create visual regressions, we use &lt;a href="https://garris.github.io/BackstopJS/"&gt;BackstopJS&lt;/a&gt; to test our components (including all elements, patterns, template components and templates) for exactly that.&lt;/p&gt;

&lt;p&gt;The way this works is that when creating a pull request in our git repository, we first create a static build of our component library (which will later be deployed to a dedicated instance, so everyone can have access to that: designers, developers, project managers, the client, …) and then use these static build files to compare them with the previous ones (which are checked in to the git repository).&lt;br&gt;
That means we have a direct coupling between our component library and our visual regression tests and are not depending on some third party services: there won’t be any downtime and nothing leaves our servers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Starting projects with the Frontend Starter Kit
&lt;/h2&gt;

&lt;p&gt;Last but not least, we tried to make things even easier, even more consistent and therefore even better and stable.&lt;/p&gt;

&lt;p&gt;Since websites do quite often consist out of the same (or at least very similar) components, we came up with the Frontend Starter Kit, which is basically made of all of the above:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a Figma file which contains accessible colors, typography, components like form elements and so on&lt;/li&gt;
&lt;li&gt;web components for form elements and other recurring components, which are published as node modules&lt;/li&gt;
&lt;li&gt;a Drupal theme providing form elements and other types of components&lt;/li&gt;
&lt;li&gt;all developed and tested for visual regressions using miyagi&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This Frontend Starter Kit works for many different types of projects and since we publish the components as node modules, we can even use them in Vue projects.&lt;/p&gt;

&lt;p&gt;Overall this allows us to have a standardized, well-proven and consistent base for all our projects, avoiding the “stupid” work which every project contains and letting us concentrate on the rest.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F147cmd69xyu6lcu3fcs0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F147cmd69xyu6lcu3fcs0.png" alt="Screenshot of our Frontend Starter Kit in Figma" width="800" height="484"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>design</category>
      <category>frontend</category>
      <category>workflow</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Reducing the file size of open source fonts</title>
      <dc:creator>Michael Großklaus</dc:creator>
      <pubDate>Sun, 12 Jan 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/mgrsskls/reducing-the-file-size-of-open-source-fonts-4f2j</link>
      <guid>https://dev.to/mgrsskls/reducing-the-file-size-of-open-source-fonts-4f2j</guid>
      <description>&lt;p&gt;I was recently working on the relaunch of my website and wanted to include the open source font &lt;a href="https://rsms.me/inter/"&gt;Inter&lt;/a&gt;. Unfortunately, even the &lt;em&gt;woff2&lt;/em&gt; versions of each different font style were at least 100kb large. As I wanted to use three font styles, this was, of course, way too much.&lt;/p&gt;

&lt;p&gt;Fortunately, I knew that it is possible to reduce the file size by only using certain characters. After a quick search I stumbled upon &lt;a href="https://fontforge.org/"&gt;FontForge&lt;/a&gt;, which allows you to edit all sorts of things in open source fonts (I honestly do not have a clue what most of them are). I opened the font files and saw that all kinds of characters (romanian, greek, kyrillic and other letters, characters for spacings, mathematical characters and so on) were included. Since my website does not have any user generated content, I knew pretty well what kind of characters I would need. Especially for bold and italic font styles, the amount of necessary characters is very limited. That way I was able to remove most of the characters and decrease the total size of all font files &lt;strong&gt;from 314kb to 39kb&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The steps to do that are the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open a font file via &lt;em&gt;File&lt;/em&gt; → &lt;em&gt;Open&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Go to &lt;em&gt;Element&lt;/em&gt; → &lt;em&gt;Font Info&lt;/em&gt; (opens a new window) → &lt;em&gt;Unicode Ranges&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Select an entry in the list&lt;/li&gt;
&lt;li&gt;In the other window deselect the characters you want to keep and remove the selected characters via &lt;em&gt;Encoding&lt;/em&gt; → &lt;em&gt;Detach &amp;amp; Remove Glyhphs…&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Repeat that for every entry in the &lt;em&gt;Unicode Ranges&lt;/em&gt; list&lt;/li&gt;
&lt;li&gt;Create a new font file via &lt;em&gt;File&lt;/em&gt; → &lt;em&gt;Generate Fonts…&lt;/em&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Admittedly, the UX could be a bit better and depending on your font files, this can be quite time-consuming. Nevertheless, it is fairly easy with &lt;em&gt;FontForge&lt;/em&gt; to remove characters from a font file and the results are definitely worth it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; At the time of writing this, the security restrictions on macOS Catalina do not allow you to open font files from your “Downloads”, “Documents” and “Desktop” folders. Move the font files in any other folder if &lt;em&gt;FontForge&lt;/em&gt; says “Unauthorized / Operation not permitted”. See also &lt;a href="https://github.com/fontforge/fontforge/issues/4082"&gt;this issue&lt;/a&gt; on GitHub.&lt;/p&gt;

</description>
      <category>frontend</category>
      <category>fonts</category>
      <category>performance</category>
    </item>
    <item>
      <title>My 2019</title>
      <dc:creator>Michael Großklaus</dc:creator>
      <pubDate>Fri, 03 Jan 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/mgrsskls/my-2019-57b8</link>
      <guid>https://dev.to/mgrsskls/my-2019-57b8</guid>
      <description>&lt;p&gt;2019 has been a quite different year for me. Since I started freelancing in 2011, I only worked on client projects the whole time (except for a few weeks of vacation every year). But as my son was born last year, I wanted to work a lot less. Besides going on parental leave (I took three months, another one will follow this year), I decided to not actively look for work. If no requests from potential clients came in, that would be fine. Instead, I would just enjoy slowing down, spending time with my family and from time to time working on private projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Private projects
&lt;/h2&gt;

&lt;p&gt;That way it also happened that I published my first node modules. After having used &lt;a href="https://patternlab.io/"&gt;PatternLab&lt;/a&gt; often, I wanted to create something similar, but more lightweight and better fitting to my needs. I finally started looking into &lt;a href="https://nodejs.org/"&gt;NodeJS&lt;/a&gt; and eventually released &lt;a href="https://www.npmjs.com/package/headman"&gt;headman&lt;/a&gt;. It is a component development tool based on &lt;a href="https://www.npmjs.com/package/express"&gt;Express&lt;/a&gt; and &lt;a href="https://www.npmjs.com/package/consolidate"&gt;Consolidate.js&lt;/a&gt; that allows you to develop your components in isolation and render them using dummy data. For me the latter is very practical as I often work on projects where backend and frontend development happens time-delayed.&lt;/p&gt;

&lt;p&gt;Two other node modules I published were &lt;a href="https://www.npmjs.com/package/typing-effect"&gt;typing-effect&lt;/a&gt; and &lt;a href="https://www.npmjs.com/package/text-truncation"&gt;text-truncation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The download numbers for all my node modules are low, but for me they all fulfill certain use cases that – as far as I can tell – haven’t been solved by any other node modules. And by far the most positive aspect in developing them was that it helped me a lot digging into NodeJS and to better understand &lt;a href="https://www.npmjs.com/"&gt;npm&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Client work
&lt;/h2&gt;

&lt;p&gt;Besides that I worked on a few clients projects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I helped &lt;a href="https://www.factorial.io"&gt;Factorial&lt;/a&gt; develop the new (but unfortunately not yet released) checkout process for &lt;a href="https://www.stellenwerk-hamburg.de/"&gt;Stellenwerk&lt;/a&gt;, a job portal for students. It uses Drupal and PatternLab in the background.&lt;/li&gt;
&lt;li&gt;Also together with Factorial as well as the developers of &lt;a href="https://www.guj.de/"&gt;G+J&lt;/a&gt;, I worked on the technical base for their new paid content websites. We wanted to make development as fast and convenient as possible and were able to implement a solution that required nothing else then starting a &lt;a href="https://www.npmjs.com/package/koa"&gt;Koa&lt;/a&gt; server, no build tools and so on required.&lt;/li&gt;
&lt;li&gt;For &lt;a href="https://www.hannes-hawaii-tours.de/"&gt;Hannes Hawaii Tours&lt;/a&gt;, I worked on a booking system together with &lt;a href="https://www.patrick-schnetger.com/"&gt;Patrick Schnetger&lt;/a&gt;. The challenge was that the booking system was supposed to be sold as a white label solution as well, allowing the clients to configure the booking process in a quite open way. That demanded a lot of thinking about the technical architecture. Using &lt;a href="https://rubyonrails.org/"&gt;Ruby on Rails&lt;/a&gt; and being just one backend and one frontend developer allowed me and Patrick to make really fast progress while at the same time being able to easily make bigger changes to the architecture when requirements changed. I still really enjoy working with Ruby on Rails as I like the tight coupling between frontend and backend (even though the current development rather goes in the other direction).&lt;/li&gt;
&lt;li&gt;I worked on an interesting small project for &lt;a href="https://www.fork.de/"&gt;Fork Unstable Media&lt;/a&gt;. Unfortunately, I had to sign an NDA for it.&lt;/li&gt;
&lt;li&gt;After having worked for the german LinkedIn competitor &lt;a href="https://www.xing.com"&gt;XING&lt;/a&gt; already around 2015, they asked me to support them again at the end of the year. Having never really worked with &lt;a href="https://reactjs.org/"&gt;React&lt;/a&gt; before, I was happy they trusted me to be able to dig into it quickly. I helped them improve their design system which is based on &lt;a href="https://github.com/xing/hops"&gt;Hops&lt;/a&gt; and uses React for templating. As the design system is for now only for internal use, it was refreshing to be able to use all sorts of new CSS features, not having to think about older browsers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Having worked with different technologies in 2019, I hope I can continue to do so in 2020. As we all know it is difficult to catch up with all the new frontend developments, especially when you can usually only do that in your spare time. Having a client trusting me to do so while working for them, was one of the most rewarding experiences in my freelance career so far.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;I will try to blog more and try to use my blog as a collection of useful technologies, patterns, tips, …&lt;/li&gt;
&lt;li&gt;Life with a family in a popular city is expensive, so I will increase my working time again while trying to miss as little as possible from the huge (metaphorical and real) steps my son makes every day.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>career</category>
      <category>frontend</category>
    </item>
    <item>
      <title>What can we do about climate change?</title>
      <dc:creator>Michael Großklaus</dc:creator>
      <pubDate>Thu, 10 Oct 2019 18:30:00 +0000</pubDate>
      <link>https://dev.to/mgrsskls/what-can-we-do-about-climate-change-4gi2</link>
      <guid>https://dev.to/mgrsskls/what-can-we-do-about-climate-change-4gi2</guid>
      <description>&lt;p&gt;When following discussions in the web development community, you can read a lot about topics like inclusion, diversity, accessibility and so on. While all these topics are important and should not be missed, I barely read about a topic that concerns me as no other: climate change.&lt;/p&gt;

&lt;p&gt;Thinking about why this is, it seems that climate change has nothing directly to do with the web development community. We are building websites, most of us from 9 to 5, some as freelancers, some as open source contributors, others engaging actively in the community.&lt;br&gt;&lt;br&gt;
How we deal with the climate change seems more of a private topic though. Everyone has to decide for themselves what they can do: eating less meat, flying less, taking the bike instead of the car, etc.&lt;/p&gt;

&lt;p&gt;But I think this is only partly true.&lt;/p&gt;

&lt;p&gt;Around two years ago, my client, their client and I had a kickoff meeting in London. My client and I flew there from Hamburg in the morning and back in the evening. There were around 15 people, but only three or four were actually talking. Yes, it is always good to get to know everyone in person, but there was nothing happening that required physical presence.&lt;/p&gt;

&lt;p&gt;I also see more and more conferences popping up everywhere. With every new technology, every new framework lots of conferences around that specific topic are organized. Developers from the whole world come together, meet people they maybe only know from chatting online, making new friends, going to workshops.&lt;/p&gt;

&lt;p&gt;I know that many of us love these events outside of our every day life.  And I know that you can not forbid people to fly around the world.&lt;/p&gt;

&lt;p&gt;But I really think that we need to think about if this is actually necessary.&lt;/p&gt;

&lt;p&gt;There are thousands of articles describing what is going to happen if we fail in stopping the earth from heating up. The earth in 20, 30, 50 or 100 years would probably be completely different from what it looks like now. All our new technologies and our knowledge might not mean anything at all anymore as instead we might fight with our neighbours about food, water and a place to live.&lt;/p&gt;

&lt;p&gt;Is it worth it?&lt;/p&gt;

&lt;p&gt;Again – I know that meeting someone digitally is not the same as meeting someone in person. But I think that whenever it is possible, it is our duty to do so. For too long we were living at the expense of future generations to just continue like this.&lt;/p&gt;

&lt;p&gt;You might say that the web development community is too small, that we do not have enough power or that not getting on a plane to fly to the next conference is not going to change anything.&lt;/p&gt;

&lt;p&gt;This is true. And not.&lt;/p&gt;

&lt;p&gt;Us alone not flying anymore does not change anything, yes. But if everyone thinks that way, we will definitely lose. Do you want to continue as we do just because others do not care? I do not. I do not want to feel guilty. I do not want to feel that I have not given everything just because other people do not care.&lt;/p&gt;

&lt;p&gt;It is my strong belief that we will only be able to stop the climate change with renunciation and technology.&lt;br&gt;&lt;br&gt;
And as no other industry in the world, we are able to build tools, that connect people in the whole world. We can stop jumping on planes and instead use the tools we have. Improve them. Build new ones. Spread them.&lt;/p&gt;

</description>
      <category>climatechange</category>
      <category>discuss</category>
    </item>
    <item>
      <title>How to organize components</title>
      <dc:creator>Michael Großklaus</dc:creator>
      <pubDate>Wed, 21 Aug 2019 00:00:00 +0000</pubDate>
      <link>https://dev.to/mgrsskls/how-to-organize-components-1cgi</link>
      <guid>https://dev.to/mgrsskls/how-to-organize-components-1cgi</guid>
      <description>&lt;p&gt;Probably the most popular way of organizing your components is &lt;a href="http://bradfrost.com/blog/post/atomic-web-design/"&gt;Atomic Design&lt;/a&gt; which was introduced by Brad Frost. I recently talked to a client of mine who said that they were succesfully using this methodology in all their projects and that it works really well for them. We had a short discussion about it as I was using it for a little bit as well, but was realizing pretty quickly that it would just not work for me. One reason for that is the following example: let’s say you have two atoms; a button and an icon. If a button could also have an icon included, you would include an atom in another atom. While you could of course do that, it simply feels wrong to me (there are also no atoms in atoms in the nature, are there?).&lt;br&gt;&lt;br&gt;
That is just one example for why I came up with my own way of organizing my components.&lt;/p&gt;

&lt;p&gt;Of course, this is an ongoing process and every project is different, but I basically follow these rules:&lt;/p&gt;
&lt;h2&gt;
  
  
  Which directories should I use?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;templates&lt;/strong&gt; - The actual page templates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;content&lt;/strong&gt; - Content components, which can be picked by the editors to fill pages with content.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;components&lt;/strong&gt; - All the remaining components that should not be used directly by editors (usually something like icons, buttons, etc.).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  When should I create a new component?
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;When using code in multiple components.&lt;/strong&gt; As soon as you use the same code (or slight variations of it) multiple times, make a component out of it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;When rendering entries of a collection.&lt;/strong&gt; If you render a collection, put the code for the entries (e.g. a teaser in a teaser list) in a separate component.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;When your component becomes too complex.&lt;/strong&gt; This is a rather vague rule, but as soon as you have the feeling that your component becomes too complex, move logical parts into their own component.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;While the first rule is essential to avoid code duplication, the second and third rule are basically only for reducing the complexity of a component. In that case, I would call the components “sub components”.&lt;/p&gt;
&lt;h2&gt;
  
  
  Where should I put my sub components?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;In a directory in its parent component.&lt;/strong&gt; As you are not using these components anywhere else, you can “scope” them by putting them in a sub directory of the actual component. This couples them tightly together and you can find all the code where you expect it.&lt;/p&gt;
&lt;h2&gt;
  
  
  What about components that are only used for templates?
&lt;/h2&gt;

&lt;p&gt;Components, that are used by multiple templates and only by templates (e.g. header, footer, navigation), live in a subdirectory called &lt;code&gt;_includes/&lt;/code&gt; in the &lt;code&gt;templates/&lt;/code&gt; directory. I prefix the name of this directory with &lt;code&gt;_&lt;/code&gt; to show that it is some sort of a helper directory and not a template.&lt;/p&gt;
&lt;h2&gt;
  
  
  Example
&lt;/h2&gt;

&lt;p&gt;The structure for an example project could look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;├── components
│ ├── button
│ ├── icon
│ ├── image
│ ├── input
│ └── video
│
├── content
│ ├── cta
│ ├── gallery
│ │ ├── thumbnails
│ │ │ └── thumbnail
│ │ └── view
│ ├── rte
│ └── teaser-list
│ └── teaser
│
└── templates
    ├── _includes
    │ ├── footer
    │ ├── header
    │ └── navigation
    ├── homepage
    ├── standard
    └── checkout
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again, this is nothing static and can be adapted for every project. Especially having a directory called &lt;code&gt;content&lt;/code&gt; with components that can be picked by editors, makes of course only sense when there is actually a CMS behind the scenes.&lt;/p&gt;

&lt;p&gt;If you are looking for a component library that plays well together with what I described in this post, feel free to check out &lt;a href="https://github.com/mgrsskls/headman"&gt;headman&lt;/a&gt;. It is a small node module that can be used to develop your components in isolation, no matter how you organize them.&lt;/p&gt;

</description>
      <category>frontend</category>
      <category>html</category>
      <category>components</category>
      <category>templates</category>
    </item>
    <item>
      <title>An easier way to use relative units</title>
      <dc:creator>Michael Großklaus</dc:creator>
      <pubDate>Thu, 15 Aug 2019 00:00:00 +0000</pubDate>
      <link>https://dev.to/mgrsskls/an-easier-way-to-use-relative-units-2pi4</link>
      <guid>https://dev.to/mgrsskls/an-easier-way-to-use-relative-units-2pi4</guid>
      <description>&lt;p&gt;Browsers nowadays perfectly zoom in and out when using &lt;code&gt;cmd&lt;/code&gt;-&lt;code&gt;+&lt;/code&gt; and &lt;code&gt;cmd&lt;/code&gt;-&lt;code&gt;-&lt;/code&gt;. Because of this, developers sometimes think that using relative units is actually not necessary. But as zooming that way only affects the current website, browsers also have a setting to change the default font size of the browser. Unfortunately, this has no effect when using absolute units. That is why using relative units &lt;em&gt;is&lt;/em&gt; important.&lt;/p&gt;

&lt;p&gt;As in the beginning of my developer career it was still completely normal to use &lt;code&gt;px&lt;/code&gt; for everything and getting Photoshop layouts with pixel measurements, I still think in pixels when developing. Of course, there are tools that help you with that: you can e.g. use &lt;code&gt;px&lt;/code&gt; in your code base which becomes converted to &lt;code&gt;rem&lt;/code&gt; when using a post- or pre-processor.&lt;/p&gt;

&lt;p&gt;But as it is better to not rely on tooling, I prefer to use one of my all-time favourite css tricks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;html {
  font-size: 62.5%;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;62.5%&lt;/code&gt; equals &lt;code&gt;10px&lt;/code&gt;, as the default font size of a browser is &lt;code&gt;16px&lt;/code&gt; (&lt;code&gt;16 * 0.625 = 10&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Now you can use &lt;code&gt;rem&lt;/code&gt; and your values are the same as your pixel values, the decimal separator just moved one digit to the left:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;body {
  font-size: 1.6rem; /* 16px */
}

.wrapper {
  max-width: 72rem; /* 720px */
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Don’t forget to explicitely set &lt;code&gt;font-size&lt;/code&gt; on the &lt;code&gt;body&lt;/code&gt; now, as &lt;code&gt;10px&lt;/code&gt; is too small for a default value.&lt;/p&gt;

&lt;p&gt;Note: I sometimes see developers using &lt;code&gt;html: { font-size: 10px; }&lt;/code&gt; to achieve the same, but this does not fix the zooming problem.&lt;/p&gt;

</description>
      <category>css</category>
      <category>a11y</category>
    </item>
    <item>
      <title>It does not always need to be the hippest code</title>
      <dc:creator>Michael Großklaus</dc:creator>
      <pubDate>Mon, 12 Aug 2019 00:00:00 +0000</pubDate>
      <link>https://dev.to/mgrsskls/it-does-not-always-need-to-be-the-hippest-code-202o</link>
      <guid>https://dev.to/mgrsskls/it-does-not-always-need-to-be-the-hippest-code-202o</guid>
      <description>&lt;p&gt;One reason frontend development never becomes boring is the fact that the available technologies evolve rapidly. Browser vendors publish new features every few weeks, which makes it hard to stay up-to-date. At the same time it also makes development much easier as as a lot of functionality gets introduced by the new browser versions.&lt;/p&gt;

&lt;p&gt;When working on a project, it is now completely normal for me to implement its layout or features with Flexbox – or in some (still rare) cases with CSS Grid. Both make implementing most layouts much easier.&lt;/p&gt;

&lt;p&gt;The last project I worked on seemed to be such a case. One main element of it was a teaser list, which was supposed to look roughly like this:&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;br&gt;
  &lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mL1nKuSW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.mgrossklaus.de/assets/posts/it-does-not-always-have-to-be-the-hippest-code/01.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mL1nKuSW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.mgrossklaus.de/assets/posts/it-does-not-always-have-to-be-the-hippest-code/01.png" alt=""&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;The important part here was that the vertical space between the teasers was the same as the horizontal space.&lt;/p&gt;

&lt;p&gt;A colleague of mine started implementing it using CSS Grid, splitting it up in 12 columns, repeating rows, spanning content over multiple columns etc.. It worked well and using CSS Grid seemed like an obvious choice, but unfortunately we realized that the Internet Explorer (yes, we still had to support it) didn’t support some of the CSS Grid features that were used. When my colleague went on holidays, I took over and tried to adapt the Grid solution, so that the IE would support it and we didn’t have to throw away all the code. I realized though that it just wouldn’t work, so I changed the code to use Flexbox which was actually not a big change. The main principle was the same: I just had to use some other attributes, doing minor tweaks and was done pretty quickly.&lt;/p&gt;

&lt;p&gt;Later on I realized that the horizontal space between the teasers was not correct yet. In my solution, the small teasers (2, 3, 5 and 6) were always aligned to the bottom of the big teasers (1 and 4). I had then pulled up teaser 2 and 5 with negative margins to put them in the right position. It looked perfect, but only due to some bad dummy data and other coincidences, making all the text boxes have the same height. Of course their height needed to be flexible though. So, with more realistic data it looked somewhat like this:&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;br&gt;
  &lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--L5mM5QpG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.mgrossklaus.de/assets/posts/it-does-not-always-have-to-be-the-hippest-code/02.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--L5mM5QpG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.mgrossklaus.de/assets/posts/it-does-not-always-have-to-be-the-hippest-code/02.png" alt=""&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;As you can see here, the space between teaser 1 and 2 became too big because the text box in teaser 1 pushed down everything.&lt;/p&gt;

&lt;p&gt;My next idea was to take the text boxes of teaser 1 and 4 out of the flow, using &lt;code&gt;position: absolute&lt;/code&gt; to align it at the bottom of the big image and then pulling it down by a fixed value. That way all small teasers would align to the bottom of the big images and I could pull down the teaser 3 and 6 by the same value I pulled down the text boxes of teaser 1 and 4.&lt;br&gt;&lt;br&gt;
The designer was fine with this solution, but the variable height of the text boxes in teaser 1 and 4 was a problem again as they could theoratically become higher and higher. Especially on smaller viewports, the text box becomes too high quickly as it is narrower and expands to the top:&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;br&gt;
  &lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_Kl8MFLY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.mgrossklaus.de/assets/posts/it-does-not-always-have-to-be-the-hippest-code/03.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_Kl8MFLY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.mgrossklaus.de/assets/posts/it-does-not-always-have-to-be-the-hippest-code/03.png" alt=""&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;I thought about it quite a bit and was sure that there is no way to implement this layout with flexbox.&lt;br&gt;&lt;br&gt;
Since there must be a way though, I was then trying to find another way to take the text boxes out of the elements flow – until &lt;code&gt;float&lt;/code&gt; came into my mind.&lt;br&gt;&lt;br&gt;
Having it used for many years for almost every layout I implemented, I had now almost completely forgotten it. Trying around a little bit, it took just a few minutes to actually have the complete solution. The steps for that roughly were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Float the text boxes of teaser 1 to the right. That way the text box doesn’t have any impact on the height of its containing teaser and the following teaser is automatically aligned to the bottom of the image (technically correct: to the bottom of the teaser).&lt;/li&gt;
&lt;li&gt;Now set &lt;code&gt;float: left&lt;/code&gt; on teaser 2 and &lt;code&gt;float: right&lt;/code&gt; on teaser 3. That way teaser 3 is placed next to the teaser 2. But most importantly, it aligns to the bottom of the text box in teaser 1. That is because a floated element still reserves space (unlike elements with &lt;code&gt;position: absolute&lt;/code&gt;), it just doesn’t influence its containers height.&lt;/li&gt;
&lt;li&gt;Now I could do the same with the teasers 4, 5 and 6, but inverted.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That was it. A modern looking layout with quite oldschool code, completely supported in all browsers without any polyfills or vendor prefixes.&lt;/p&gt;

&lt;p&gt;You can find a basic implementation here:&lt;/p&gt;

&lt;p&gt;See the Pen &lt;a href="https://codepen.io/mgrsskls/pen/9b1fcbee8879f6f6b6717e8255c286b0/"&gt;float layout&lt;/a&gt; by Michael Groklaus (&lt;a href="https://codepen.io/mgrsskls"&gt;@mgrsskls&lt;/a&gt;) on &lt;a href="https://codepen.io"&gt;CodePen&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Implementing this made me realize that there are so many things happening in the frontend world, that we almost forget about some of the most basic features. It is important to be aware of new developments as they can make a developers life much easier. But while new technologies often still need tooling around them, knowing the basics is crucial to write accessible, simple and widely supported code.&lt;/p&gt;

</description>
      <category>css</category>
      <category>flexbox</category>
      <category>grid</category>
      <category>layouts</category>
    </item>
  </channel>
</rss>
