<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Tomas Rezac</title>
    <description>The latest articles on Forem by Tomas Rezac (@rezi).</description>
    <link>https://forem.com/rezi</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%2F201340%2F8bbdff6c-42f8-4721-acf2-b30b3de9bde7.gif</url>
      <title>Forem: Tomas Rezac</title>
      <link>https://forem.com/rezi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/rezi"/>
    <language>en</language>
    <item>
      <title>[Boost]</title>
      <dc:creator>Tomas Rezac</dc:creator>
      <pubDate>Tue, 08 Jul 2025 09:36:25 +0000</pubDate>
      <link>https://forem.com/rezi/-4bdg</link>
      <guid>https://forem.com/rezi/-4bdg</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/rezi/css-counting-magic-converting-counter-values-to-variables-e9m" class="crayons-story__hidden-navigation-link"&gt;CSS Counting Magic: Converting Counter Values to Variables&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/rezi" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F201340%2F8bbdff6c-42f8-4721-acf2-b30b3de9bde7.gif" alt="rezi profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/rezi" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Tomas Rezac
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Tomas Rezac
                
              
              &lt;div id="story-author-preview-content-2655659" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/rezi" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F201340%2F8bbdff6c-42f8-4721-acf2-b30b3de9bde7.gif" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Tomas Rezac&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/rezi/css-counting-magic-converting-counter-values-to-variables-e9m" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Jul 8 '25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/rezi/css-counting-magic-converting-counter-values-to-variables-e9m" id="article-link-2655659"&gt;
          CSS Counting Magic: Converting Counter Values to Variables
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag crayons-tag--filled  " href="/t/showdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;showdev&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/css"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;css&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/javascript"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;javascript&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/discuss"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;discuss&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/rezi/css-counting-magic-converting-counter-values-to-variables-e9m" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/exploding-head-daceb38d627e6ae9b730f36a1e390fca556a4289d5a41abb2c35068ad3e2c4b5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;5&lt;span class="hidden s:inline"&gt; reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/rezi/css-counting-magic-converting-counter-values-to-variables-e9m#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              &lt;span class="hidden s:inline"&gt;Add Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            5 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>css</category>
      <category>showdev</category>
      <category>javascript</category>
      <category>discuss</category>
    </item>
    <item>
      <title>CSS Counting Magic: Converting Counter Values to Variables</title>
      <dc:creator>Tomas Rezac</dc:creator>
      <pubDate>Tue, 08 Jul 2025 08:54:58 +0000</pubDate>
      <link>https://forem.com/rezi/css-counting-magic-converting-counter-values-to-variables-e9m</link>
      <guid>https://forem.com/rezi/css-counting-magic-converting-counter-values-to-variables-e9m</guid>
      <description>&lt;p&gt;Have you ever needed to count elements or sum variables across elements and then use the result as a CSS variable? No JavaScript involved. I'll show you how it can be done.&lt;/p&gt;

&lt;p&gt;The solution is kind of insane, but beautiful. It took tens of hours of thinking, trying, and failing—again and again—until it was finally accomplished.&lt;/p&gt;

&lt;p&gt;Why would one even bother? It opens up fascinating possibilities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Changing layout based on content without needing media or container queries&lt;/li&gt;
&lt;li&gt;Dynamically adjusting cells in grid/flex based on their position in the container&lt;/li&gt;
&lt;li&gt;Determining whether a grid cell is in the first row or not&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Challenge
&lt;/h2&gt;

&lt;p&gt;From the beginning, it was clear that a full-featured solution must be implemented using CSS &lt;code&gt;counter()&lt;/code&gt;. You can count elements with the &lt;code&gt;:nth-child&lt;/code&gt; selector or with the brand new &lt;code&gt;sibling-index()&lt;/code&gt; function. However, both solutions only tell you the index within the parent element, not a sum of values. They cannot propagate the number to their parent element.&lt;/p&gt;

&lt;p&gt;CSS &lt;code&gt;counter()&lt;/code&gt;, on the other hand, can count elements or even sum variables. For example, you can define a counter in the &lt;code&gt;body&lt;/code&gt; element and then use &lt;code&gt;counter-increment: var(--any-number);&lt;/code&gt; on any child. Then display the sum in &lt;code&gt;body::after&lt;/code&gt;. The issue is that it can ONLY be shown in &lt;code&gt;::after&lt;/code&gt; or &lt;code&gt;::before&lt;/code&gt; pseudo-elements, nowhere else.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The challenge is: How do you turn the text content of an &lt;code&gt;::after&lt;/code&gt; pseudo-element into a variable?&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution: Converting Counter Values into Variables
&lt;/h2&gt;

&lt;p&gt;There's a neat, hacky solution to get an element's width in CSS as a CSS variable. We'll explore this technique shortly. First, to leverage this approach, we need to turn the text content value of an &lt;code&gt;::after&lt;/code&gt; element into its width.&lt;/p&gt;

&lt;p&gt;To better understand this concept, let's say we need to set the width of &lt;code&gt;&amp;lt;div&amp;gt;17&amp;lt;/div&amp;gt;&lt;/code&gt; to &lt;code&gt;17px&lt;/code&gt;. After spending ages figuring this out, I discovered it can be done with a custom font and a &lt;code&gt;@counter-style&lt;/code&gt;. Let's examine the latter first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@counter-style&lt;/span&gt; &lt;span class="n"&gt;countStyle&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;system&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;additive&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;additive-symbols&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10000&lt;/span&gt; &lt;span class="s2"&gt;'N'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1000&lt;/span&gt; &lt;span class="s2"&gt;'M'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt; &lt;span class="s2"&gt;'C'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt; &lt;span class="s1"&gt;"X"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="s1"&gt;"I"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above defines a counter style that describes how to convert a number to its visual representation on screen. For example, &lt;code&gt;myCounter&lt;/code&gt; with value &lt;code&gt;2344&lt;/code&gt; in this code:&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;.counter&lt;/span&gt;&lt;span class="nd"&gt;::after&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;myCounter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;countStyle&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;will be converted to &lt;code&gt;MMCCCXXXXIIII&lt;/code&gt;. The system works by starting with the highest number in &lt;code&gt;additive-symbols&lt;/code&gt; and checking how many times it fits into &lt;code&gt;2344&lt;/code&gt;. &lt;code&gt;N&lt;/code&gt; fits 0 times, &lt;code&gt;M&lt;/code&gt; fits 2 times (resulting in &lt;code&gt;MM&lt;/code&gt;), then those &lt;code&gt;2000&lt;/code&gt; are subtracted from the initial number, leaving us with &lt;code&gt;344&lt;/code&gt;. This remainder is then tested with &lt;code&gt;100 'C'&lt;/code&gt; and so on, until the last additive-symbol.&lt;/p&gt;

&lt;p&gt;Next, we apply a custom font to &lt;code&gt;MMCCCXXXXIIII&lt;/code&gt;. We define the font only for characters N, M, C, X, and I. &lt;code&gt;I&lt;/code&gt; is set to a &lt;code&gt;1px&lt;/code&gt; wide space, &lt;code&gt;X&lt;/code&gt; is set to a &lt;code&gt;10px&lt;/code&gt; wide space, and so on. The font itself is created via &lt;a href="https://www.glyphrstudio.com/app/" rel="noopener noreferrer"&gt;Glyphr Studio&lt;/a&gt; with a maximum character width set to 10,000 points. Because of this, we need to set the font-size to an astounding &lt;del&gt;&lt;code&gt;10000px&lt;/code&gt;&lt;/del&gt;(there is a new limit in chrome not allowing that big font. We need to stick with &lt;code&gt;1000px&lt;/code&gt; and then multiply the result by 10, or simply make the font 1,000 points). Character &lt;code&gt;I&lt;/code&gt; will be displayed as &lt;code&gt;1px&lt;/code&gt; wide while &lt;code&gt;N&lt;/code&gt; appears as &lt;code&gt;1000px&lt;/code&gt; wide.&lt;/p&gt;

&lt;p&gt;This is the key insight: we now have width derived from text content. An element with &lt;code&gt;MMCCCXXXXIIII&lt;/code&gt; will be exactly &lt;code&gt;2344px&lt;/code&gt; wide.&lt;/p&gt;

&lt;h3&gt;
  
  
  Converting Element Width to CSS Variable
&lt;/h3&gt;

&lt;p&gt;Let's return to &lt;strong&gt;how to turn element width into a CSS variable&lt;/strong&gt;. You can read the full article by Temani Afif on this topic: &lt;a href="https://frontendmasters.com/blog/how-to-get-the-width-height-of-any-element-in-only-css/" rel="noopener noreferrer"&gt;How to Get the Width/Height of Any Element in Only CSS&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The technique requires view-timeline animation and essentially states that if you know 3 of these 4 dimensions, you can calculate the 4th:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Dimension of the scroller&lt;/li&gt;
&lt;li&gt;Dimension of the subject&lt;/li&gt;
&lt;li&gt;Progress of the animation&lt;/li&gt;
&lt;li&gt;Offset of the subject&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We define a &lt;code&gt;::before&lt;/code&gt; element with width &lt;code&gt;1px&lt;/code&gt; (subject dimension) and align it to the very left. The animation progress and offset within the container is 0. This means we can calculate the dimensions of the scroller.&lt;/p&gt;

&lt;p&gt;Once we know the scroller width, we can use the &lt;code&gt;::after&lt;/code&gt; element (the one from above with counter value represented by custom font). This time we already know the scroller width, and again the animation progress and offset is 0. We can obtain the width of the subject (counter) as a CSS variable. Because we use the &lt;code&gt;timeline-scope&lt;/code&gt; property, the variable is available on the scroller (parent element).&lt;/p&gt;

&lt;h2&gt;
  
  
  Example
&lt;/h2&gt;

&lt;p&gt;Please see the code below for the complete solution. Works in Chrome only now!&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/rezi-the-lessful/embed/gbpNPaR?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Limitations
&lt;/h2&gt;

&lt;p&gt;There are several limitations introduced by this implementation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Browser Support&lt;/strong&gt;: Currently only works in Chrome and Safari Technology Preview (no Firefox support)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Layout Constraints&lt;/strong&gt;: Uses &lt;code&gt;overflow: auto&lt;/code&gt; on the count container. You cannot absolutely position elements outside of it unless using layer APIs like popover&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Size Limitations&lt;/strong&gt;: Requires defining a child in the container with width equal to the count. If the count exceeds the container width, counting stops working. This can be partially addressed by decreasing font size 10 time, so counter value &lt;code&gt;1&lt;/code&gt; translates to &lt;code&gt;0.1px&lt;/code&gt;, but this may result in incorrect rounding in some cases&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Practical Application: Detecting First Row in Grid
&lt;/h2&gt;

&lt;p&gt;If you need to determine whether a cell is in the first row of a grid, you can sum all the children's spans (since one child can occupy more than one column). Sum these spans using the technique mentioned above to get &lt;code&gt;--total-span-count-in-grid&lt;/code&gt;. Then use &lt;code&gt;clamp()&lt;/code&gt; with the maximum number of columns available in the grid:&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;--is-first-row&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;clamp&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
  &lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;calc&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;var&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;--max-columns&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;-&lt;/span&gt; &lt;span class="nt"&gt;var&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;--total-span-count-in-grid&lt;/span&gt;&lt;span class="o"&gt;)),&lt;/span&gt; &lt;span class="err"&gt;1&lt;/span&gt;
&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If elements span only across the first row, &lt;code&gt;--is-first-row&lt;/code&gt; equals &lt;code&gt;1&lt;/code&gt;; otherwise, it equals &lt;code&gt;0&lt;/code&gt;. Any CSS calculation can then be multiplied by this value to get either &lt;code&gt;0&lt;/code&gt; or the desired calculation if we're in the first row.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This technique opens up new possibilities for CSS-only solutions to complex layout problems. While it has limitations and browser support constraints, it demonstrates the creative potential of combining CSS counters, custom fonts, and view-timeline animations to achieve what was previously thought impossible without JavaScript.&lt;/p&gt;

</description>
      <category>css</category>
      <category>showdev</category>
      <category>javascript</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Why Sugar.css grid is so revolutionary?</title>
      <dc:creator>Tomas Rezac</dc:creator>
      <pubDate>Thu, 07 Mar 2024 13:52:09 +0000</pubDate>
      <link>https://forem.com/rezi/why-sugarcss-grid-is-so-revolutionary-1b66</link>
      <guid>https://forem.com/rezi/why-sugarcss-grid-is-so-revolutionary-1b66</guid>
      <description>&lt;p&gt;In this article, I want to introduce you to a novel CSS grid concept that differs significantly from what we're accustomed to. You can witness it in action within the &lt;a href="https://sugar-css.com/doc/grid/examples"&gt;Sugar.css documentation&lt;/a&gt; and delve into its code within the &lt;a href="https://github.com/Rezi/sugar-css/blob/main/src/lib/scss/grid.scss"&gt;Sugar repository&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Traditional Approach of Bootstrap-like Grids
&lt;/h3&gt;

&lt;p&gt;For ages, we've relied on grids from libraries such as Bootstrap or Tailwind. Despite transitions from percentages to flex and later to grid, little has changed over the years. Both Bootstrap and Tailwind, along with many others, adhere to the same core principles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A predetermined number of columns in a container (usually 12).&lt;/li&gt;
&lt;li&gt;Generation of an extensive array of classes applied to cells to determine their column occupancy or offset.&lt;/li&gt;
&lt;li&gt;Adapting the grid layout based on window width through the application of different sets of classes, ensuring responsiveness across various devices.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, this approach comes with several drawbacks:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A fixed number of columns necessitates the use of media queries. The presence of media queries is particularly problematic for component-based front-end frameworks. Components should be agnostic to whether they occupy the full screen width or a mere 200 pixels. Yet, the width of the component is pivotal for effectively setting up cell classes in a responsive manner. This issue could potentially be addressed by transitioning from media queries to container queries.&lt;/li&gt;
&lt;li&gt;To ensure optimal responsiveness, one must define a cell class for every breakpoint, such as &lt;code&gt;col-sm-12 col-md-6 col-lg-4 col-xl-3&lt;/code&gt;. It results in lot of HTML markup, which is hard to read.&lt;/li&gt;
&lt;li&gt;The generation of a vast number of classes to cover all scenarios. While this can be partially mitigated by CSS cleansing (the automated removal of unused classes), it is not ideal for larger projects where most classes are already in use.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  The New Concept Introduced in the Sugar Grid
&lt;/h3&gt;

&lt;p&gt;The core principles of the Sugar grid diverge significantly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The container has no preset column count.&lt;/li&gt;
&lt;li&gt;Users specify the optimal width of a column, and once a new column fits within the container, it is automatically added.&lt;/li&gt;
&lt;li&gt;Instead of classes, CSS variables like &lt;code&gt;--span:2&lt;/code&gt; are utilised to span cells across multiple columns, eliminating the need for class generation.&lt;/li&gt;
&lt;li&gt;Another CSS variable can adjust the span count of a cell based on different container sizes, such as &lt;code&gt;--span-6:3&lt;/code&gt;. This means that if at least 6 columns fit within a container, the cell should span across 3 columns instead of the default &lt;code&gt;--span&lt;/code&gt; value.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This approach seemingly resolves all the drawbacks associated with traditional grids.&lt;/p&gt;

&lt;p&gt;We can also observe that this appears to be an intrinsic design, a design pattern that seems to be the holy grail after traditional responsive design. The distinction between intrinsic and responsive designs lies in the fact that the former does not rely on media queries and is instead driven by content.&lt;/p&gt;

&lt;p&gt;While I would like to categorise the Sugar grid as intrinsic, it would only be partially accurate. It utilises container queries under the hood, although they are not essential for core functionality. Presently, their necessity arises due to a peculiar behavior of spans in the grid. For instance, if a grid has two columns but one of its cells spans three, the grid becomes disrupted, causing content overflow. Ideally, it should automatically adjust to a span of two, but this functionality is currently lacking. Perhaps this issue will be resolved in future iterations of CSS.&lt;/p&gt;

&lt;h3&gt;
  
  
  Advantages of the Sugar Grid
&lt;/h3&gt;

&lt;p&gt;The results are staggering: a significant reduction in CSS. Sugar's grid comprises approximately 150 lines of code, compared to Bootstrap's 1500 lines. It's about 10 times smaller.&lt;br&gt;
It also eliminates the need for an excessive number of classes applied to cells in HTML. In scenarios like cards in a grid container, you simply apply &lt;code&gt;style="--span:2"&lt;/code&gt; to the container itself, and you're done. The span definition is passed from the container to the cells. If needed, you can still overwrite it on a cell level.&lt;br&gt;
For more complex situations, syntax like &lt;code&gt;--span:2; --span-6:3; span-12:6&lt;/code&gt; can be used, although such cases are rare and mostly pertain to specifying page or large component layouts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Disadvantages of the Sugar Grid
&lt;/h3&gt;

&lt;p&gt;It is different! It requires new way of thinking. It employs modern CSS features like container queries, and careful consideration is necessary regarding its usage. Since there are no breakpoints, aligning with traditional designers who insist on pixel-perfect designs with specific breakpoints might pose a challenge. It requires a shift in their mindset.&lt;br&gt;
It introduces an unnecessary div wrapper between the container definition and cells, primarily as a workaround for a Safari bug. Once resolved, this wrapper will be eliminated.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Is the Sugar grid revolutionary? I believe it is, albeit it requires some time for widespread adoption. The Sugar grid is rooted in the concept of intrinsic design, even if it's not a pure implementation.&lt;br&gt;
Sugar offers additional features like offsets and specialised grid behaviours for layouts. However, the focus of this article is to highlight a new direction in which CSS grid "frameworks" can evolve.&lt;br&gt;
Perhaps in the future, grids like Sugar will be further simplified. Currently, the main hindrances to such simplification are various bugs related to container queries (subgrids, Safari repeat column template).&lt;/p&gt;

&lt;p&gt;If you haven't already, you should definitely &lt;a href="https://sugar-css.com/doc/grid/examples"&gt;give it a try&lt;/a&gt;!&lt;/p&gt;




</description>
    </item>
    <item>
      <title>Creating Modern Accordion Components with CSS and HTML only</title>
      <dc:creator>Tomas Rezac</dc:creator>
      <pubDate>Wed, 21 Feb 2024 09:32:11 +0000</pubDate>
      <link>https://forem.com/ciklum_czsk/creating-modern-accordion-components-with-css-and-html-24pb</link>
      <guid>https://forem.com/ciklum_czsk/creating-modern-accordion-components-with-css-and-html-24pb</guid>
      <description>&lt;p&gt;Welcome to the first instalment of a series dedicated to crafting common components using only modern CSS and HTML.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: I am the author of Sugar.css, a minimalistic CSS Framework built with the latest CSS. You can explore a more advanced implementation of a similar accordion on &lt;a href="https://sugar-css.com/doc/components/accordion"&gt;Sugar.css&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Assignment
&lt;/h2&gt;

&lt;p&gt;What are the essential features of a proper &lt;strong&gt;Accordion&lt;/strong&gt;?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Toggle content on user input or programmatically&lt;/li&gt;
&lt;li&gt;Accessibility (toggle on Enter and Space)&lt;/li&gt;
&lt;li&gt;Disable access to accordion content (by keyboard) when the accordion is collapsed&lt;/li&gt;
&lt;li&gt;Stacking multiple accordions after each other&lt;/li&gt;
&lt;li&gt;Auto close other accordions in the group when one is opened&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Toggling Content
&lt;/h2&gt;

&lt;p&gt;Gone are the days when the only way to toggle styles permanently was through a label with a hidden checkbox. For the past few years, we've been able to use a combination of &lt;code&gt;details&lt;/code&gt; and &lt;code&gt;summary&lt;/code&gt; HTML elements to achieve this. The &lt;code&gt;details&lt;/code&gt; element hides all its content by default, except for a &lt;code&gt;summary&lt;/code&gt; element if placed as a direct child. The &lt;code&gt;summary&lt;/code&gt; serves as a toggle button; if not present, the text &lt;strong&gt;details&lt;/strong&gt; is added as a default toggle. By clicking the toggle, the rest of the hidden content is revealed. Let's observe the default behavior and design:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/rezi-the-lessful/embed/eYXoyBp?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Accessibility
&lt;/h2&gt;

&lt;p&gt;All accessibility features are built-in; the summary has a default tabindex and can be toggled by Space and Enter keys, so no additional work is needed here.&lt;/p&gt;

&lt;h2&gt;
  
  
  Disabling Access to Content When Collapsed
&lt;/h2&gt;

&lt;p&gt;While this functionality should work out of the box, there's an exception with Safari. If the &lt;code&gt;details&lt;/code&gt; element is collapsed and contains focusable elements like &lt;code&gt;input&lt;/code&gt; or &lt;code&gt;button&lt;/code&gt;, tabbing in Safari will be consumed by those hidden elements. To ensure consistent accessibility across browsers, a simple JavaScript solution is required. The content is wrapped, and the &lt;code&gt;inert&lt;/code&gt; attribute needs to be set to the wrapper once the &lt;code&gt;details&lt;/code&gt; is collapsed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Styling
&lt;/h2&gt;

&lt;p&gt;Let's apply some CSS to enhance the appearance. First, we'll remove the original triangle indicator. This can be done differently in various browsers, but the following CSS accomplishes 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;summary&lt;/span&gt;&lt;span class="nd"&gt;::marker&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nt"&gt;summary&lt;/span&gt;&lt;span class="nd"&gt;::-webkit-details-marker&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="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;summary&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;list-style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nb"&gt;none&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;Next, we'll style the summary to resemble an accordion header and prepare it for a new chevron:&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;summary&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;list-style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&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="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="nl"&gt;align-items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;justify-content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;space-between&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;pointer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;padding-block&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--padding&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nb"&gt;bold&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;details&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.75rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--border-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.1rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--chevron-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.8rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;system-ui&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To implement the new chevron for the base accordion, we'll create a rotated box with just two borders. The state of the opened accordion is selected by &lt;code&gt;details[open]&lt;/code&gt;:&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;details&lt;/span&gt; &lt;span class="nt"&gt;summary&lt;/span&gt;&lt;span class="nd"&gt;:after&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;content&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="py"&gt;margin-inline-end&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--padding&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--chevron-size&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--chevron-size&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="n"&gt;currentColor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--border-width&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--border-width&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rotate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;-45deg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;translateY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;-30%&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;transform&lt;/span&gt; &lt;span class="m"&gt;0.3s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;flex-shrink&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;details&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;open&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="nt"&gt;summary&lt;/span&gt;&lt;span class="nd"&gt;:after&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rotate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;45deg&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;Lastly, we'll add separators between accordions if there are more consecutive ones:&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;details&lt;/span&gt;&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;:last-of-type&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;margin-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--padding&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;padding-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--padding&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;border-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--border-width&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="no"&gt;gray&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;
  
  
  Grouping Accordions
&lt;/h2&gt;

&lt;p&gt;Another useful feature of &lt;code&gt;details&lt;/code&gt; is its acceptance of a &lt;code&gt;name&lt;/code&gt; attribute. All &lt;code&gt;details&lt;/code&gt; with the same name behave as a group. When you open one, the others in the same group automatically close.&lt;/p&gt;

&lt;p&gt;Check out the result &lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/rezi-the-lessful/embed/MWxRbyo?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Additional styles like colours and spacings can make it more vivid&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/rezi-the-lessful/embed/GReLaJL?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;As demonstrated, &lt;code&gt;details&lt;/code&gt; and &lt;code&gt;summary&lt;/code&gt; are fully capable of replacing JavaScript-driven accordions. They make implementation easier and provide immediate accessibility (with the exception of Safari, which requires a workaround for a specific edge case).&lt;/p&gt;

&lt;p&gt;However, there's one potential drawback: the lack of animation support. If animation is applied to collapsed content within &lt;code&gt;details&lt;/code&gt;, it runs only once when opened and cannot be rerun when opened next time. This limitation may need to be considered based on the specific design and functionality requirements of your project.&lt;/p&gt;

&lt;p&gt;Next time we will check the &lt;code&gt;dialog&lt;/code&gt; element. See you soon.&lt;/p&gt;

</description>
      <category>css</category>
      <category>a11y</category>
      <category>webdev</category>
      <category>html</category>
    </item>
    <item>
      <title>Sugar.css is futuristic and minimalistic CSS framework</title>
      <dc:creator>Tomas Rezac</dc:creator>
      <pubDate>Thu, 01 Feb 2024 22:15:39 +0000</pubDate>
      <link>https://forem.com/rezi/sugarcss-is-futuristic-and-minimalistic-css-framework-3ii6</link>
      <guid>https://forem.com/rezi/sugarcss-is-futuristic-and-minimalistic-css-framework-3ii6</guid>
      <description>&lt;p&gt;Before diving in, take a look at &lt;a href="https://sugar-css.com/" rel="noopener noreferrer"&gt;Sugar.css&lt;/a&gt; to get a feel for what we're discussing. The entire documentation site leverages Sugar.css to give it a stylish appearance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TLTR:&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Sugar.css&lt;/strong&gt; is an 8.4 kB CSS framework comprising only 7 CSS classes. The bulk of it revolves around enhancing the styling of native HTML components in an advanced manner.&lt;/p&gt;

&lt;p&gt;Check out an example of Sugar.css design:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmnl98innp9w6zjkzljqw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmnl98innp9w6zjkzljqw.png" alt="Sugar css logo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I Created Sugar CSS?
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;I grew weary of writing an excessive number of classes to style HTML for minimalistic and well-designed templates repeatedly.&lt;/li&gt;
&lt;li&gt;I found existing CSS frameworks to be overly complicated and extensive, often leading to the creation of unattractive HTML.&lt;/li&gt;
&lt;li&gt;I was dissatisfied with other minimalistic CSS frameworks, which made customisation a tedious and slow process.&lt;/li&gt;
&lt;li&gt;I sought a small but robust grid system that seamlessly integrates with component-based JS frameworks but couldn't find any that met my criteria.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What is Sugar.css
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;A minimalistic CSS framework (only 8.4 kB) that styles native HTML elements with minimal use of classes—just seven classes for containers, grids, light/dark themes, and secondary buttons.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fully-featured and responsive, including an advanced grid system, forms, tooltips, navigation, loaders, and more.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Built for customization, offering over 50 global custom properties to tailor your own Sugar.css build. It provides multipliers for spacings, roundness, and fatness to adjust sets of variables at once. Sugar.css also includes a built-in customization UI tool, including color accessibility checks.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Modular—create your build with just the features you need.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Designed with accessibility in mind, adding interactive elements like modals, tooltips, accordions, etc., without requiring JS or CSS hacks, all while supporting keyboard control.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Opinionated in styling certain elements (&lt;code&gt;article, section&lt;/code&gt;). Customize your build's source code or easily overwrite styles, as Sugar.css applies styles with zero selectivity.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What it is not
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;It is not a CSS-in-JS framework. It cannot be used piece by piece directly in JS. It is based on HTML syntax for styling, not classes or direct styles.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Does not encompass the extensive features common in large UI frameworks. It does not aim to provide CSS solutions for components requiring heavy JS support, such as autocompletes, filtering elements, sortable data-tables, etc. However, you can still use preset custom properties in your own components and style them similarly to Sugar.css.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Not suitable for older browsers; optimised for the latest versions of Chrome, Firefox, Safari, and other Chromium browsers.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why Use Sugar.css
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Consider it a CSS reset that you can build upon with CSS in JS or any preferred method.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fine-tuned with numerous features for its compact size. For example, the entire CSS framework, including the grid system, is the same size as a standalone Bootstrap grid.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Encourages the writing of semantic and accessible HTML—styles are applied only when all semantic requirements are met.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Brings consistency and simplicity to your design.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to use it
&lt;/h2&gt;

&lt;p&gt;How to Use Sugar.css&lt;br&gt;
Numerous options are available, including installing the npm package, loading via CDN, or building via the command line. &lt;a href="http://localhost:5173/doc" rel="noopener noreferrer"&gt;See all options&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try and leave feedback, please
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Sugar.css&lt;/strong&gt; is currently in beta, and your input is invaluable. I encourage you to test it out and share your feedback either here or through the &lt;a href="https://github.com/Rezi/sugar-css" rel="noopener noreferrer"&gt;Github repo&lt;/a&gt; by creating an issue. Your insights will contribute to refining and enhancing the framework.&lt;/p&gt;

&lt;p&gt;Thank you very much!&lt;/p&gt;

</description>
      <category>css</category>
      <category>framework</category>
      <category>semantic</category>
      <category>accessible</category>
    </item>
    <item>
      <title>Palette generator you will ️</title>
      <dc:creator>Tomas Rezac</dc:creator>
      <pubDate>Mon, 21 Feb 2022 08:47:45 +0000</pubDate>
      <link>https://forem.com/ciklum_czsk/palette-generator-you-will-5gmc</link>
      <guid>https://forem.com/ciklum_czsk/palette-generator-you-will-5gmc</guid>
      <description>&lt;p&gt;You can check it straight away &lt;a href="https://palette.rocks/"&gt;palette.rocks&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I made yet another Palette generator
&lt;/h2&gt;

&lt;p&gt;I have been using color generators for years. There are many great ones: Coolors, Adobe color, Color hunt, Paletton, Material color tool, Dopely, to name a few.&lt;/p&gt;

&lt;p&gt;They have eased my color palette selection a lot. But maybe they could help me even more.&lt;/p&gt;

&lt;p&gt;With such a tooling one would expect to generate the right palette within minutes. In reality, it is more like hours.&lt;/p&gt;

&lt;p&gt;Maybe you know the feeling of generating super nice palette, transferring it to an accessibility checker and finding out it has too low contrast or it doesn't pass color blind test. &lt;br&gt;
And once you generate a palette, which pass all the accessibility checks, it simply doesn't look well in your particular web project.&lt;/p&gt;

&lt;p&gt;I have been thinking about how to make the color selection easier and how to save some time. How to try hundreds of palettes in my web projects in fraction of time I used to need? I decided I would create my own generator, which would be able to achieve that!&lt;/p&gt;
&lt;h2&gt;
  
  
  What a color palette generator should do
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;It needs to generate palettes easily.&lt;/li&gt;
&lt;li&gt;It needs to give you all the accessibility feedback in real time.&lt;/li&gt;
&lt;li&gt;It should show all the necessary data and feedback in one place (colors, their relations, accessibility report, preview, ...)&lt;/li&gt;
&lt;li&gt;It should be easy to transfer generated palette to you project.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Most of the palette generators - mentioned in the introduction part - cover up to 2 of the above mentioned points. Mostly they don't provide any accessibility feedback. And if they do, they struggle with showing all the data at one place and in real time.&lt;/p&gt;

&lt;p&gt;None of the palette generators make it very easy to transfer the generated palettes to your web. In better cases, they generate CSS variables for you. But you still need to copy paste them; reload your browser; remember how it looks like; generate new palette... and repeat the process until you are happy with result.&lt;/p&gt;

&lt;p&gt;So I made my own palette generator, and tried to cover all the points.&lt;/p&gt;
&lt;h2&gt;
  
  
  The killer feature
&lt;/h2&gt;

&lt;p&gt;Probably the most innovative palette.rocks feature is the possibility to &lt;strong&gt;connect it to your web and remote control its color theme in real time&lt;/strong&gt;. Just spin the color wheel or generate random palette and see how it looks like in your web.&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://player.vimeo.com/video/678093527" width="710" height="399"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;In order to connect your web to palette.rocks you need to fulfill following requirements:&lt;/p&gt;

&lt;p&gt;1) Your web needs to use CSS variables for colors&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
:root {
  --your-color: #1ab291;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2) Add this script to your page, it will expose a global function &lt;code&gt;mapPaletterRocks(mappingObject)&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;script src="https://palette.rocks/script.js"&amp;gt;&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3) Call the &lt;code&gt;mapPaletterRocks(mappingObject)&lt;/code&gt; function with mapping object.&lt;br&gt;
Keys of that object are colors in pallete.rocks.&lt;br&gt;
Values are pairs of color variables used in your app.&lt;br&gt;
To the first in pair, a color from palette.rocks is mapped. To the second in pair, the most contrast color (white or black) is mapped.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mapPaletterRocks({
    a:['--primary-color', '--primary-contrast'],
    b:['--secondary-color', '--secondary-contrast'],
    c:['--tertiary-color', '--tertiary-contrast'],
    d:['--quaternary-color', '--quaternary-contrast'],
    customColors:[['--custom-color-1', '--custom-color-1-contrast']]
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;4) Click the &lt;strong&gt;Connect YOUR WEB to live preview&lt;/strong&gt; button in palette.rocks page. Paste your web's url into the input in top of the modal and submit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it out
&lt;/h2&gt;

&lt;p&gt;You can try it on my other project. Just go to &lt;a href="https://palette.rocks/"&gt;palette.rocks&lt;/a&gt;, follow the point 4 from above and submit this url &lt;br&gt;
&lt;code&gt;https://fluid-grid.com/&lt;/code&gt;&lt;br&gt;
Then click on the &lt;strong&gt;Desktop preview&lt;/strong&gt; on the 'mobile phone' to see it in full size. And start changing colors ;)&lt;/p&gt;

&lt;h2&gt;
  
  
  More unique features:
&lt;/h2&gt;

&lt;p&gt;And there are more advanced features in palette.rocks&lt;/p&gt;

&lt;h3&gt;
  
  
  1) Color Blender
&lt;/h3&gt;

&lt;p&gt;Blender mixes all your primary colors and generates colors in between. You can then easily pick some of those as custom supplementary colors.&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%2Fiwow3tbywg3e52m6a32l.gif" 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%2Fiwow3tbywg3e52m6a32l.gif" alt="Color Blender" width="372" height="364"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2) Reversed Backgrounds
&lt;/h3&gt;

&lt;p&gt;This - not that well known color phenomenon - is based on something called &lt;code&gt;color subtraction&lt;/code&gt;. I won't go for explanation, as it would require more than another article ;)&lt;br&gt;
You better see it in two following examples:&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%2Fpvomqa3euwe07003q0ib.gif" 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%2Fpvomqa3euwe07003q0ib.gif" alt="Reversed colors" width="350" height="356"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, the inner squares are perceived as identical colors, until you compare them side by side.&lt;br&gt;
In real world design, you would need such inner colors if you wanted to present just three colors in some kind of split color design. This tool helps to find 4 colors which are perceived as 3. &lt;br&gt;
On the other hand, if you manually find a mid color between those two backgrounds and use it as the inner squares, you end up with 3 colors which will be perceived as 4 by your brain :)&lt;/p&gt;

&lt;h2&gt;
  
  
  Future of palette.rocks
&lt;/h2&gt;

&lt;p&gt;There are definitely areas for more improvements: different color formats (LAB, CMYK) for manual inputs; locking custom colors (if you lock a custom color, it would change automatically, as you change your primary color); and there are probably more features, I don't think about now.&lt;br&gt;
I'll be glad for your feedback. What are YOU missing in palette.rocks?&lt;/p&gt;

</description>
      <category>html</category>
      <category>design</category>
      <category>webdev</category>
      <category>productivity</category>
    </item>
    <item>
      <title>CSS grid system for future web - Fluid Grid
</title>
      <dc:creator>Tomas Rezac</dc:creator>
      <pubDate>Wed, 09 Feb 2022 23:17:35 +0000</pubDate>
      <link>https://forem.com/rezi/css-grid-system-for-future-web-fluid-grid-19nb</link>
      <guid>https://forem.com/rezi/css-grid-system-for-future-web-fluid-grid-19nb</guid>
      <description>&lt;p&gt;You can directly try it at &lt;a href="https://fluid-grid.com/"&gt;fluid-grid.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But I encourage you to read on and get more info on why and how I created very different grid framework, than any other we currently have.&lt;/p&gt;

&lt;p&gt;Two years ago I wrote an article about &lt;a href="https://dev.to/rezi/rethinking-responsive-webdesign-1a7g"&gt;rethinking web design&lt;/a&gt;. It got several thousands of reads and it triggered broad discussion on that topic. Most of feedback was positive, but people complained about need of web components in order to enable this new responsive style.&lt;/p&gt;

&lt;p&gt;Here I am two years later with reworked solution, which is pure CSS based. For now, it requires &lt;a href="https://github.com/GoogleChromeLabs/container-query-polyfill"&gt;Container queries polyfill&lt;/a&gt;. Fortunately, there has been one, done by Surma (Google dev)&lt;/p&gt;

&lt;h2&gt;
  
  
  New enhanced grid framework
&lt;/h2&gt;

&lt;p&gt;My first try was kind of prototype (more on that in mu above mentioned article). I used it on few projects and it worked well. But it was not ready for public use, as it required precise understanding of all predefined classes and ability to add custom ones, where needed.&lt;/p&gt;

&lt;p&gt;This time, there is full featured documentation, examples, custom grid builder (wizard) and more to come.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why new grid system?
&lt;/h2&gt;

&lt;p&gt;I had two main reasons:&lt;/p&gt;

&lt;p&gt;First of all, I have been working for past few years with various component framework and I found out, that current grid solutions based on @media queries simply don't fit to component driven web development. You cannot use media query driven grid inside component, because you don't know where the component will be used. Will the component be full screen wide or just 150px? You never know.&lt;/p&gt;

&lt;p&gt;Second of all, I started to be pissed off declaring &lt;code&gt;col-sm-6 col-md-4 col-lg-3 col-xl-2&lt;/code&gt; just for sake of telling an element, it should always be around 250px wide. Once I'd found our there is a way to tell an element, it should stick to a grid and be always around 250px wide, by simply using &lt;code&gt;col-1&lt;/code&gt;, there was no way back!&lt;/p&gt;

&lt;h2&gt;
  
  
  Fluid grid features
&lt;/h2&gt;

&lt;p&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;h4&gt;
  
  
  1. Container-query based
&lt;/h4&gt;

&lt;p&gt;Component's grid depends on components size itself, not on window width&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;h4&gt;
  
  
  2. Automatic
&lt;/h4&gt;

&lt;p&gt;Just define optimal grid cell size. Then tell how many cells your content requires. Rest is done automatically&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;h4&gt;
  
  
  3. Component ready
&lt;/h4&gt;

&lt;p&gt;You can use gird containers inside components now and don't worry how big the component is in your page&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;h4&gt;
  
  
  4 .Declarative
&lt;/h4&gt;

&lt;p&gt;Basic scenarios can be handled by minimal declarative rules &lt;code&gt;.f-col-1&lt;/code&gt;. No more imperative description &lt;code&gt;w-full sm:w-1/2 md:w-1/3 lg:w-1/4 xl:w-1/6 mb-4&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;h4&gt;
  
  
  5 .CLI
&lt;/h4&gt;

&lt;p&gt;You can create your own custom made fluid grid, with sizes fitting your needs. Just run &lt;code&gt;npx fluid-grid&lt;/code&gt; and command line wizard will guide thru.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It is way better to see it in action to really understand how  different it is compared to twitter bootstrap grid or a tailwind grid. Just go and check it &lt;a href="https://fluid-grid.com/"&gt;fluid-grid.com&lt;/a&gt; if you haven't done already.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;Fluid Grid is now in Beta, as there are several pending questions and the grid extent can change a bit.&lt;/p&gt;

&lt;p&gt;It would be great to start discussion on, what can be changed, improved,... Why some approaches are now used over others etc.&lt;/p&gt;

&lt;p&gt;For instance these are open topics for discussion:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Fluid grid greatly supports components' internal grids, tiles' grids and other smaller grids. However, usage for big layouts produces more code than desired. Should there be separate version for layouts only? Should a page layout support be omitted and outsourced to a developer. Or should there be some kind of breakpoints (I can think of 8, 13, 21 columns) like in the bootstrap grid system, when a grid is used for layouts?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Do we need more classes and more build in features, or should the grid stick to a bare minimum and try to be super lightweight?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Should the wizard let developer make even more customised builds, or should we rather stick to more uniform output?&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There are, for sure, more questions and hopefully answers. &lt;br&gt;
It would be great to discuss options and possibilities here or in the &lt;a href="https://github.com/Rezi/fluid-grid/issues/1"&gt;projet's github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Looking for your feedback, ideas, pull requests and offers for collaboration ;)&lt;/p&gt;

</description>
      <category>css</category>
      <category>webdev</category>
      <category>html</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Frontend checklist</title>
      <dc:creator>Tomas Rezac</dc:creator>
      <pubDate>Fri, 13 Nov 2020 13:09:26 +0000</pubDate>
      <link>https://forem.com/rezi/web-development-checklist-5413</link>
      <guid>https://forem.com/rezi/web-development-checklist-5413</guid>
      <description>&lt;p&gt;Few years ago I made a checklist for FE development and somehow forgot to let the world know about it. It is time to change it ;)&lt;/p&gt;

&lt;h3&gt;
  
  
  Let me introduce to you: &lt;a href="https://docket.work/"&gt;Docket&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;There are already some FE checklists around here, but none of them worked well for me. They are mostly static and don't persist your choices over time. Also, you cannot share your progress with colleagues. So I made &lt;a href="https://docket.work/"&gt;Docket&lt;/a&gt;, BTW its meaning, as in Cambridge Dictionary, is: "a list of all the things that someone has to deal with or discuss."&lt;/p&gt;

&lt;h2&gt;
  
  
  Docket's features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Provides templates with various checklists. &lt;/li&gt;
&lt;li&gt;You can edit delete/add/modify all items in a checklist.&lt;/li&gt;
&lt;li&gt;Every docket has its own unique URL, which can be shared with others.&lt;/li&gt;
&lt;li&gt;You can register/log in to see all your dockets.&lt;/li&gt;
&lt;li&gt;It is cooperation ready as it use sockets to emit changes. (more people can work on same docket at same time)&lt;/li&gt;
&lt;li&gt;Items can contain description with links to articles, etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Templates
&lt;/h3&gt;

&lt;p&gt;Docket contains following checklist templates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HTML head&lt;/li&gt;
&lt;li&gt;HTML&lt;/li&gt;
&lt;li&gt;HTML for public Web&lt;/li&gt;
&lt;li&gt;CSS&lt;/li&gt;
&lt;li&gt;Javascript&lt;/li&gt;
&lt;li&gt;Images&lt;/li&gt;
&lt;li&gt;Webfonts&lt;/li&gt;
&lt;li&gt;Code &amp;amp; Styleguide&lt;/li&gt;
&lt;li&gt;Accessibility&lt;/li&gt;
&lt;li&gt;Speed and performance&lt;/li&gt;
&lt;li&gt;Web launch &amp;amp; SEO&lt;/li&gt;
&lt;li&gt;Security&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can combine more templates in one docket ;)&lt;br&gt;
You can also add your own checklist groups.&lt;/p&gt;

&lt;h3&gt;
  
  
  How can you help?
&lt;/h3&gt;

&lt;p&gt;Try it. If you like it, you are encouraged to help me with improvements. &lt;br&gt;
You can submit Pull Request to &lt;a href="https://github.com/Rezi/docket/blob/master/src/app/checklist-service/default-checklist.ts"&gt;Docket repository&lt;/a&gt; on github with changes of templates, or you can come with your own template you would like to add to docket.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>discuss</category>
      <category>webdev</category>
      <category>performance</category>
    </item>
    <item>
      <title>Rethinking responsive Webdesign</title>
      <dc:creator>Tomas Rezac</dc:creator>
      <pubDate>Tue, 09 Jun 2020 21:14:37 +0000</pubDate>
      <link>https://forem.com/rezi/rethinking-responsive-webdesign-1a7g</link>
      <guid>https://forem.com/rezi/rethinking-responsive-webdesign-1a7g</guid>
      <description>&lt;h3&gt;
  
  
  From idea to revolutionary web grid system ;)
&lt;/h3&gt;




&lt;h2&gt;
  
  
  How do we it do nowadays?
&lt;/h2&gt;

&lt;p&gt;To be honest, most of us have never made a responsive web design, we all just make an adaptive webs (we only care about few specific screen sizes) and call it responsive!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2048%2F0%2A7QlneLKH895pUiv-.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2048%2F0%2A7QlneLKH895pUiv-.png" alt="from: [https://4eps1j19gvc31lua5g49gnl7-wpengine.netdna-ssl.com/wp-content/uploads/2016/03/responsive-design.png](https://4eps1j19gvc31lua5g49gnl7-wpengine.netdna-ssl.com/wp-content/uploads/2016/03/responsive-design.png)"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Responsive means something more like the above. &lt;strong&gt;But can we achieve it?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After years of giving a UX lecture at university, showing this picture, and telling students, that this is a true responsive design, I realised, that &lt;strong&gt;we cannot&lt;/strong&gt;. There is actually no  grid framework (known to me) fulfilling this assignment. &lt;/p&gt;

&lt;p&gt;As there is no ultimate solution for a responsive web design, most of us stick to something like Bootstrap grid system. Unfortunately, these kinds of grid systems are far from being responsive. And work badly with current FE tech stack.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;From now on, I'll often mention Bootstrap grid system. By doing so, I'll refer to a group of grid systems (Bootstrap, Foundation, Tailwind CSS and similar), not solely to Bootstrap.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What's wrong with Bootstrap-like grids?
&lt;/h2&gt;

&lt;p&gt;Best to start with an example. Just to illustrate that there are more grid systems similar to bootstrap, lets check a Tailwind's grid:&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;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"flex flex-wrap"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"w-full sm:w-1/2 md:w-1/3 lg:w-1/4 xl:w-1/6 mb-4 bg-gray-500"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"w-full sm:w-1/2 md:w-1/3 lg:w-1/4 xl:w-1/6 mb-4 bg-gray-400"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"w-full sm:w-1/2 md:w-1/3 lg:w-1/4 xl:w-1/6 mb-4 bg-gray-500"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"w-full sm:w-1/2 md:w-1/3 lg:w-1/4 xl:w-1/6 mb-4 bg-gray-400"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"w-full sm:w-1/2 md:w-1/3 lg:w-1/2 xl:w-1/6 mb-4 bg-gray-500"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"w-full sm:w-1/2 md:w-1/3 lg:w-1/2 xl:w-1/6 mb-4 bg-gray-400"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What can a developer complain about the code above?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;It is a lot of code.&lt;/li&gt;
&lt;li&gt;It is hard to read. &lt;/li&gt;
&lt;li&gt;It is hard to reason about. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What a developer wants, is to get rid of this &lt;code&gt;w-full sm:w-1/2 md:w-1/3 lg:w-1/4 xl:w-1/6 mb-4 bg-gray-400&lt;/code&gt; in favor of something like this: &lt;code&gt;col-2&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  What can a UX designer complain about the grid?
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;It doesn't support huge screens.&lt;/li&gt;
&lt;li&gt;It doesn't work well with components.&lt;/li&gt;
&lt;li&gt;It lets you declare behaviour for certain conditions, instead of letting you declare rules for that behaviour.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's investigate the 3 points above in closer detail.&lt;/p&gt;

&lt;h2&gt;
  
  
  1) Wide screen support
&lt;/h2&gt;

&lt;p&gt;Just try a random Web of yours on 4k or 8k monitor ;) &lt;/p&gt;

&lt;p&gt;Bootstrap-like grids are adaptive. It means they have some screen size limits; they use breakpoints. The biggest —  xl, mostly stands for something around 1200px. Anything above is indifferent, and you need to deal with its responsiveness on your own. A lot of web pages break or look totally awkward on huge screens.&lt;/p&gt;

&lt;h2&gt;
  
  
  2) Grid vs Components
&lt;/h2&gt;

&lt;p&gt;Bootstrap-like grids are almost useless inside components. Why?&lt;/p&gt;

&lt;p&gt;Let say, you have 400px wide component with four 200px wide child elements. Obviously, its content needs to behave differently than in another instance of the same component, which has a width of 800px, right? In first case you prefer 2x2 grid, in other case you prefer 4x1 grid. Unfortunately, as the component code is the same for both instances and screen size is given, you are f..... &lt;/p&gt;

&lt;h4&gt;
  
  
  Bootstrap example where a column width is defined by percents
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F9l25re067g1gpl0afy3e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F9l25re067g1gpl0afy3e.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As we are already in a component era (React, Angular, Vue, Svelte, Web Components)… bootstrap-like grids seems to be doomed, but there is nothing to replace them, at least nothing generic that could be used across all front end frameworks.&lt;/p&gt;

&lt;p&gt;You can still use bootstrap-like grids for page/app layout together with component oriented frameworks. But to be honest, when you see how much CSS code is generated for such grid system and how many classes you really use for a layout, it is better to get rid of it. You can always implement a custom solution.&lt;/p&gt;

&lt;h4&gt;
  
  
  Solution, fixing the Bootstrap example form above, seems to be definition of columns with min and max widths.
&lt;/h4&gt;

&lt;p&gt;Columns take desired widths and proportinally consume the leftovers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fsnfx6dzlo4po4h3cfyx8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fsnfx6dzlo4po4h3cfyx8.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3) Grid's behaviour
&lt;/h2&gt;

&lt;p&gt;Saying that bootstrap grid “lets you declare behaviour for certain conditions, instead of letting you declare rules for that behaviour” sounds bit philosophical. Translated to human language: &lt;/p&gt;

&lt;p&gt;Bootstrap grid lets you declare how big certain blocks should be at specific breakpoints (you need to cover all page-width cases with your own overly declarative code, as in the Tailwind example). Mostly, lot of code must be written to reach a simple goal: to make an element similarly big at any screen size. The additional value of bootstrap grid system is just aligning stuff to a grid.&lt;br&gt;
What we really want, is to declare a set of rules and let the grid's content to flow, span, shrink and stretch based on them.&lt;/p&gt;

&lt;p&gt;Could we skip all the declarations for all the screen sizes and make the alignment to a grid automatic? Yes we can!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I know you can have hard time, arguing with your designers/product owners, when switching to a smart 'automatic' grid. You just need to explain to them, that even if they provide you with designs for mobile, table and desktop, there always will be some designs in between, which they don't have under control. Design should be specified on level of guidelines, component libraries and very basic understanding of layout rules, not on few pixel perfect mocks&lt;/em&gt; ;)&lt;/p&gt;
&lt;h2&gt;
  
  
  New Grid — design brainstorming
&lt;/h2&gt;

&lt;p&gt;What characteristics should a truly responsive grid system have?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;To be &lt;strong&gt;independent from the screen size&lt;/strong&gt; (to be usable in components).&lt;/li&gt;
&lt;li&gt;Grid should contain elements &lt;strong&gt;aligned to a virtual grid&lt;/strong&gt; (grid gaps should align precisely in all directions)&lt;/li&gt;
&lt;li&gt;A child of a grid container could span across several virtual columns (optimally also across rows)&lt;/li&gt;
&lt;li&gt;Columns should have optimal sizes, given in &lt;strong&gt;units like &lt;code&gt;px&lt;/code&gt; or &lt;code&gt;rem&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;not in percents&lt;/strong&gt;. Problem of defining columns only in percents is, that it forces us to define element's behaviour for specific breakpoints -sm, md, lg. Percents represent different real sizes under different conditions. &lt;/li&gt;
&lt;li&gt;Grid should be defined by columns, not vice versa as in Bootstrap. Bootstrap grid has always 12 columns; it is too much for mobiles and too little for UltraHD.&lt;/li&gt;
&lt;li&gt;Columns should adjust to container size (if column is wider than container, it should shrink itself to container's width).&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  New grid — choosing a technology
&lt;/h2&gt;

&lt;p&gt;What technology to use? Available technologies seems to be Flex-box and CSS Grid. They both seem to fit to most of our requirements, but not to all of them. Let's see what these approaches miss:&lt;/p&gt;
&lt;h3&gt;
  
  
  Flex-box
&lt;/h3&gt;

&lt;p&gt;In case we want to avoid definition of columns in percents of parent element, we would need to define grid elements by something like: &lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.col {min-width:100px; max-width:200px;}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Unfortunately this works only as long as last .col element is aligned to right edge of grid container. If first row has 3 .col elements and the second only has two .cols, then the elements no longer align to an imaginary grid. This behaviour cannot be fixed. That's a no go for Flex-box.&lt;/p&gt;
&lt;h3&gt;
  
  
  Grid
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;display: grid&lt;/strong&gt; performs bit better, we can use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    grid-template-columns: repeat(auto-fit, minmax($grid-col-width, 1fr));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;auto-fit&lt;/code&gt; tells the grid to stretch existing columns, until there is space for a new one.&lt;br&gt;
&lt;code&gt;minmax()&lt;/code&gt; defines minimal and maximal width of a column. &lt;em&gt;Min&lt;/em&gt; is the desired column width, &lt;em&gt;max&lt;/em&gt; (1fr) tells the column to take 1/&lt;em&gt;number of columns&lt;/em&gt;, in case there are not enough columns, which would fill the container with their minimal widths.&lt;/p&gt;

&lt;p&gt;It does exactly the same as the flex-box example above, with the difference that it always fits to a grid, hurray! But it has another flaw. When you tell an element to span across three columns, but only two would fit the container. The spanned element leak outside the container. The only meaningful solution for this problem seems to be the long-wished Element Queries (queries where 'responsive conditions apply to elements on the page instead of the width or height of the browser.'). As they are still in a form of opened proposal, I had to program 'Element Queries' on my own. &lt;/p&gt;
&lt;h3&gt;
  
  
  Element Queries
&lt;/h3&gt;

&lt;p&gt;I first tried to use some kind of polyfill, but those with good support were slow and relatively big (around thousand lines of code). As I was aiming for super small grid framework I had to find another way. Best fit was to use new 'ResizeObserver' and 'customElements' JS APIs. Their support is not optimal (about 70% of world market), but they are fast and exactly fit to what needs to be done. Element Queries for my grid can be done in something like 35 lines of code, which is awesome.&lt;/p&gt;

&lt;p&gt;CSS and JS code required is 1KB (gzipped) and cover all the requirements form the brainstorming section. It actually does even more!&lt;/p&gt;

&lt;p&gt;I now skip implementation details and show you the results, what my grid can do ;)&lt;/p&gt;
&lt;h2&gt;
  
  
  The “Eq Grid”
&lt;/h2&gt;

&lt;p&gt;In order to avoid serving JS and CSS separately, adding event listeners to DOM elements, listening to DOM mutations and so on, I created a 'Custom Element' which you just need to import and init. It generates all the required CSS classes based on supplied parameters. All you need is:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm i eq-grid --save
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;then in you main.js file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;initEqGrid&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;eq-grid&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nf"&gt;initEqGrid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;px&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// ['column width', 'gap width', 'units', 'max-columns-span/collapse']&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From that time on, you can use &lt;code&gt;&amp;lt;eq-grid&amp;gt;&lt;/code&gt; element in your html templates, and all the magic is done in background.&lt;/p&gt;

&lt;p&gt;It works literally everywhere, in pure JS/HTML, React, Angular, Vue, Svelte and other modern frameworks.&lt;/p&gt;

&lt;p&gt;Let's see some examples… Just open following sandboxes in fullscreen mode and try to resize them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Grid used for content:
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Useful for something like article teasers, cards, thumbnails, etc&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/eq-grid-content-hq3x7"&gt;
&lt;/iframe&gt;
&lt;br&gt;
In the example above, you can see classes &lt;code&gt;.eq-col-3-5 .eq-col-2-4&lt;/code&gt;, which you can use to declaratively overwrite the automatic behaviour of the grid.&lt;/p&gt;
&lt;h3&gt;
  
  
  Nested grids:
&lt;/h3&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/eq-grid-nested-k5izt"&gt;
&lt;/iframe&gt;
&lt;br&gt;
By nesting, you can avoid some element breaking freely across 'rows'. It can be useful when creating layouts as shown below.&lt;/p&gt;
&lt;h3&gt;
  
  
  Grid used for Layouts:
&lt;/h3&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/eq-grid-layout-0c9w7"&gt;
&lt;/iframe&gt;
&lt;br&gt;
You can wonder, why the grid has so wide &lt;code&gt;.eq-col-1&lt;/code&gt; on the right side. How is that possible? The root grid here has only two &lt;code&gt;.eq-col-1&lt;/code&gt; columns, all the stuff on left is in a nested grid. Each column has min-width 100px and max 1fr (one part of parent's width). In this case the max value takes the lead. If there are many elements in the root grid, then min(100px) rule is used instead. This applies to the nested grid on the left side.&lt;/p&gt;

&lt;p&gt;Remember that content of child grids has no influence on its parents.&lt;/p&gt;

&lt;p&gt;This example has 3 nested grids, btw. With this nesting technique, you can be in better control of what , when and how should fold or stretch.&lt;br&gt;
 It can be useful for big layouts.&lt;/p&gt;
&lt;h2&gt;
  
  
  Eq Grid and rem
&lt;/h2&gt;

&lt;p&gt;There is one more cool feature this grid can offer, when you set it to use &lt;code&gt;rem&lt;/code&gt; units:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;initEqGrid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rem&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Anything using &lt;code&gt;rem&lt;/code&gt; units derives its size from font size of &lt;code&gt;HTML&lt;/code&gt; element. It give us power to scale columns by media queries on the &lt;code&gt;HTML&lt;/code&gt; element. If we use &lt;strong&gt;poly fluid sizing&lt;/strong&gt; technique, we can linearly scale font up. Below I scale a little up to 1280px. Then I start scaling in same pace as the window grows.&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: 14px;
}

@media screen and (min-width: 320px) {
  html {
    font-size: calc(14px + 4 * ((100vw - 320px) / 960));
  }
}

@media screen and (min-width: 1280px) {
  html {
    font-size: calc(18px + 158 * ((100vw - 1280px) / 10000));
  }
}

@media screen and (min-width: 11280px) {
  html {
    font-size: 176px;
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Result of the above, together with eq-grid in &lt;code&gt;rems&lt;/code&gt; is cool. Once window grows up to width over 1280px, everything (grid, fonts, etc) starts scaling up as if you zoom in. This way, you can see your web nicely on 8k monitor. You can set what the ratio between zooming and adding of new columns will be - simply by adjusting the font-size &lt;code&gt;18px + 158&lt;/code&gt; and &lt;code&gt;176px&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;See an example here (you need to click the 'Open Sandbox button', otherwise it wont work). Then zoom out a lot to see how it works ;)&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/eq-grid-poly-fluid-l5d0v"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I hope, the Eq Grid system is able to cover all common development/UX requirements. You can use it in very simple and automatic way and let the content flow like a water in the first picture. Or you can be more declarative and fine-tune how grid elements fold and shrink, based on the grid size. It is up to your requirements.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;It is truly responsive. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It can scale from zero to infinite without compromising UX. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It is also first implementation of my own idea, and it definitely can be improved, so... &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'll appreciate if you leave a comment — what you think about it, what you miss, or what you like/dislike. Any feedback will be appreciated! Maybe we can make usage of grid systems, in era of components, viable again.&lt;/p&gt;

&lt;p&gt;You can check eq-grid grid on npm: &lt;a href="https://www.npmjs.com/package/eq-grid" rel="noopener noreferrer"&gt;eq-grid on npm&lt;/a&gt;.&lt;br&gt;
It is the place where all Eq-grid classes are described - what they do, and how to use them.&lt;br&gt;
You can also check how the &lt;a href="https://codesandbox.io/s/eq-react-ijuzj" rel="noopener noreferrer"&gt;grid is used inside a React app&lt;/a&gt;&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>css</category>
      <category>discuss</category>
      <category>javascript</category>
    </item>
    <item>
      <title>SPA with 4x100% lighthouse score — Part 3: Weather forecast app</title>
      <dc:creator>Tomas Rezac</dc:creator>
      <pubDate>Wed, 01 Apr 2020 20:09:18 +0000</pubDate>
      <link>https://forem.com/ciklum_czsk/spa-with-4x100-lighthouse-score-part-3-weather-forecast-app-2dc7</link>
      <guid>https://forem.com/ciklum_czsk/spa-with-4x100-lighthouse-score-part-3-weather-forecast-app-2dc7</guid>
      <description>&lt;p&gt;After previous two parts of the series, we know &lt;a href="https://dev.to/rezi/spa-with-4x100-lighthouse-score-part-1-the-tech-stack-gac"&gt;why I've chosen Svelte and Sapper&lt;/a&gt; to reach our goal and how to build with them a super performant app. &lt;a href="https://dev.to/rezi/spa-with-4x100-lighthouse-score-part-2-building-an-app-48e6"&gt;We also have a functional ‘Hello world’ app&lt;/a&gt; with maximum lighthouse score. &lt;/p&gt;

&lt;p&gt;In this 3rd part, I'll show you "production quality app", I made, as proof anyone can build a nice looking functional app with a great load performance. I'll not explain line by line, how the app was made, but will rather share my experience of building this app and dealing with all the restrictions, I imposed to myself.&lt;/p&gt;

&lt;h3&gt;
  
  
  The app showcase
&lt;/h3&gt;

&lt;p&gt;Let's check few screens from the final app: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sx4moQnL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3142/1%2AulzlrBZblx7SPLMWSECgyA.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sx4moQnL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3142/1%2AulzlrBZblx7SPLMWSECgyA.jpeg" width="800" height="476"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://player.vimeo.com/video/403058207" width="710" height="399"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;UPDATE 28th of April 2020: As city search API was removed from open weather API, I provide temporarily link to a specific city (Prague) and you do not have a chance to search city from the main page (I'll try to fix it soon)&lt;br&gt;
&lt;a href="https://zealous-jang-4090ec.netlify.app/forecast?city=prague&amp;amp;id=3067696"&gt;You can try the app here&lt;/a&gt; or check its &lt;a href="https://github.com/Rezi/svelte-lighthouse/tree/feature/weather"&gt;code on github&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(Please don’t use it as regular app,  it is limited to 60 requests per hour using &lt;a href="https://openweathermap.org/forecast5"&gt;openweathermap &lt;/a&gt;API. The app is meant just as a demo)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Here is a list of features the app can do:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;PWA — works offline with cached data&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Is installable on phone as a web app&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Search forecast by city + Remember searched cities&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Each of six main weather parameters can be visualised as a chart&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Shows forecast via animated scenery (generated clouds with different size, opacity and colour based on rain, cloudiness and sun angle)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Shows animated rain and snow fall, based on it intensity&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Shows animated thunderstorms&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Shows times of sun/moon rise/set and animates sun/moon accordingly&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Shows sun/moon directions from East to West&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Shows sun/moon angle above horizon, based on time and period of year&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Shows phases of moon&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The app is not overloaded with functionality, but it is more than one needs from one screen of an app. &lt;/p&gt;
&lt;h3&gt;
  
  
  App size limitation
&lt;/h3&gt;

&lt;p&gt;Good news is, that with Sapper each screen is lazy loaded. If you can reach the best lighthouse score on every single page, then your app can be as big as your imagination. You can still prefetch any routes in advance, either once processor is free of work - you can leverage the new &lt;code&gt;window.requestIdleCallback()&lt;/code&gt; api. Or simply after user submits such an offer. Asking user to prefetch all routes makes sense, in case he/she is going to use your app in offline mode. &lt;/p&gt;

&lt;p&gt;The conclusion: the extent of an app does not really matter, because every page is lazy-loaded by default.&lt;/p&gt;
&lt;h3&gt;
  
  
  My journey to 100% lighthouse SPA
&lt;/h3&gt;

&lt;p&gt;You can think, I just took the optimised 'Hello World' app from last article and gradually turned it to the weather app without ever dropping below 100% in Lighthouse performance. Well I did not. I even dropped to something like 50% for a moment. Let’s check the hiccups I had, one by one.&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%2Fi%2F1lqzrc4lecx883y7czbz.jpg" 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%2Fi%2F1lqzrc4lecx883y7czbz.jpg" alt="Storm" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1) Requests chaining&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Sapper was build with some ideas in mind. One of them is not to load same things twice. In reality it means, if some component is loaded in several routes, it is bundled in a separate chunk. Also pages are not composed only from pre-rendered html and one JS file, but rather two or more, one for routing and minimal svelte api and one for the main component. It makes sense, you don’t want to load same components or the svelte and sapper api on every page again, you want to serve it from the service worker. With http2 many small request are actually good as they can be downloaded and parsed in parallel. The only drawback comes to play, when some code is dependent on code in different file. Unfortunately that is the case of Sapper builds.&lt;/p&gt;

&lt;p&gt;After I got warning by Lighthouse about Requests chaining, I decided to get rid of it. Beside rewriting Sapper from scratch, there was only one solution, to rewrite Sapper, just a little and let it generate &lt;code&gt;&amp;lt;link href="/client/index.ae0f46b2.js" rel="modulepreload"&amp;gt;&lt;/code&gt; for every single JS file. The &lt;code&gt;rel=modulepreload&lt;/code&gt; tells the browser to start downloading and parsing a file before it is requested from real code. &lt;/p&gt;

&lt;p&gt;As I was already at this, I also manually added links to 3rd party api: &lt;code&gt;&amp;lt;link href="//api.openweathermap.org" rel="preconnect"&amp;gt;&amp;lt;link href="//api.openweathermap.org" rel="dns-prefetch"&amp;gt;&lt;/code&gt; to &lt;code&gt;&amp;lt;svelte:head&amp;gt;&lt;/code&gt;. Those help with getting DNS info before you ever call it. All these little tweaks have real impact on Time To Interactive.&lt;/p&gt;

&lt;p&gt;If you are interested, there is a fork of Sapper on my Github with preload support. I changed what was needed, but was not 100% sure of what I was exactly doing ;) so there is no PR to Sapper — sorry. To be honest, Sapper source code would really appreciate some finishing touches, there are lines of dead code, some //TODOs etc.. Compared to very well maintained Svelte code base, I had a feeling that none cares much about Sapper. If you are good with Webpack or Rollup, I do encourage you to have a look and do something for Sapper community ;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2) Main thread overloaded&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Another warning by Lighthouse told me, that my main thread is too busy. It was a time to use some other threads :) If you are not familiar with javascript threads and &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers"&gt;Web Workers&lt;/a&gt; in particular, the important things to know are&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Worker's code is executed in parallel to code in main thread.&lt;/li&gt;
&lt;li&gt;It is executed in different file and &lt;/li&gt;
&lt;li&gt;main communication between worker's code and your main thread is done over &lt;code&gt;postMessage()&lt;/code&gt; api. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Post message api only lets you send strings back and forth, which is not very nice.&lt;/p&gt;

&lt;p&gt;Luckily there is a 2kb &lt;a href="https://github.com/GoogleChromeLabs/comlink"&gt;Comlink library&lt;/a&gt; wrapping this communication to a promise based api. Moreover, it lets you call remote functions as if they were in the same thread. With Comlink, I moved to separate threads all the calculations related to a position of sun and moon and moon phases. It was a perfect fit as Web Worker’s only bottleneck is size of data being transferred. You don’t want to send pictures through it, because the serialisation and deserialisation would be very expensive. In my case I just sent latitude, longitude and time to a worker and it returned back stuff like directions, angles, phases. Because these calculations are quite complex, I was able to save some meaningful time from the main thread. With Comlink you can outsource even trivial tasks, as the overhead is minimal. Here is little example:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;worker.js&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Comlink&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;comlink&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;workerFunctions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&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;n&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
              &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&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;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;Comlink&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;workerFunctions&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;main.js&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Comlink&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;comlink&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;workerFunctions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Comlink&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wrap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;worker.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="nx"&gt;workerFunctions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 3.0414093201713376e+64&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Below the fold&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Most significant drop in performance was caused by my cloud generator. I started with a naive implementation. I took all 40 records of weather forecast for next 5 days and for each of them, if it was rainy, I generated a cloud via Canvas. Generating 40 clouds is time and memory consuming, nothing one can afford when aiming for best-in-class performance. I needed to get rid of computations, which are related to stuff below the fold. So I implemented ‘infinity scroll’ with on demand cloud generation. As you scroll further, new clouds are generated. To avoid generation of same clouds twice (when you scroll back) I used powerful functional technique called memoization. &lt;/p&gt;

&lt;p&gt;It simply, by creation of closure, adds a caching ability to any pure function you want. If you later call a memoized function with same arguments, it skips any computation and gives you the result from cache. In my case, it granted me yet another advantage. My clouds are actually partly random (the generator function is not pure, ups :0 ). And I don’t want to see different clouds for same scroll positions, when I scroll backward. The memoization ensured that cloud is randomised only on first call, second time I got it from cache :)&lt;/p&gt;

&lt;p&gt;Let check together simple memoization function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;memoize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;func&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;memoized&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;args&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;key&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&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="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;args&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;Here is one example how to use it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;addOne&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;memoizedAddOne&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;memoize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;addOne&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;memoizedAddOne&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// value counted =&amp;gt; 2&lt;/span&gt;
&lt;span class="nf"&gt;memoizedAddOne&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// value served from cache =&amp;gt; 2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It makes sense to use this technique for any pure function, which is often called with same arguments. You shouldn’t use it for cases, where there are thousands of calls with different arguments as it would consume lot of memory by creating huge cache objects.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Lazy loaded functionality&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If we can avoid loading of any content or code to a browser, we should avoid it. Beside lazy loaded pages, we can use &lt;code&gt;IntersectionObserver&lt;/code&gt; to lazy load images, as user scrolls down a page. These are widely used techniques, which should be used where possible. Moreover, there is out of a box support for lazy loaded code in new versions of bundlers like Webpack or Rollup. It is called dynamic import, and it gives you the power to import  code on demand from inside functions.&lt;/p&gt;

&lt;p&gt;I used dynamic imports to load charting functionality once it is requested by user. You can see it in my app. Only after you click on one of the 6 icons, code responsible for drawing svg paths is downloaded and executed. &lt;/p&gt;

&lt;p&gt;In rollup the syntax is very straightforward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;showStats&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;smoother&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../../helpers/smooth-curve.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;smoother&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPath&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="err"&gt;…&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Final Results
&lt;/h3&gt;

&lt;p&gt;I am happy to say, that &lt;strong&gt;the Weather app got 4x100% in Lighthouse audit&lt;/strong&gt;. It is SPA, PWA, installable on phones, with some limited support for offline usage.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;As you can see, modern tools and libraries like Rollup and Comlink make lazy loaded and performant app architecture so easy. I would say, there is no excuse not to use similar techniques in web apps and JS heavy pages, especially in those dedicated to general public.&lt;/p&gt;

&lt;p&gt;I hope that the app I made is good enough example of what can be done in field of load performance. I am aware of poor animation performance of the app on slower machines. And I know, that too many animations triggered by scroll event is no-go. But this app has never been meant as something anyone should use in daily life. It was just satisfying for me to add more and more animations to it and make it more like a real world experience, rather than presenting some boring numbers.&lt;/p&gt;

&lt;p&gt;The animation performance could be improved by using OffscreenCanvas inside a web-worker, but as it’s not supported by all current browsers, I decided not to use it. Maybe one day, I’ll return to this series and make the animation flow in 60 fps, who knows.&lt;/p&gt;

&lt;p&gt;I hope you enjoyed the series and and learned something new. &lt;/p&gt;

&lt;p&gt;In case you haven't checked it yet, here is &lt;a href="https://zealous-jang-4090ec.netlify.app/forecast?city=prague&amp;amp;id=3067696"&gt;The weather app&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Aloha!&lt;/p&gt;

</description>
      <category>webperf</category>
      <category>showdev</category>
      <category>svelte</category>
      <category>javascript</category>
    </item>
    <item>
      <title>SPA with 4x100% lighthouse score - Part 2: Building an app</title>
      <dc:creator>Tomas Rezac</dc:creator>
      <pubDate>Tue, 24 Mar 2020 20:06:54 +0000</pubDate>
      <link>https://forem.com/ciklum_czsk/spa-with-4x100-lighthouse-score-part-2-building-an-app-48e6</link>
      <guid>https://forem.com/ciklum_czsk/spa-with-4x100-lighthouse-score-part-2-building-an-app-48e6</guid>
      <description>&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%2Fi%2Fpj1bz1007mekhs37j00u.jpg" 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%2Fi%2Fpj1bz1007mekhs37j00u.jpg" alt="Alt Text" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting up the app
&lt;/h3&gt;

&lt;p&gt;In last part we decided to go for &lt;strong&gt;JS&lt;/strong&gt;, &lt;strong&gt;Svelte js&lt;/strong&gt; framework/compiler and &lt;strong&gt;Sapper&lt;/strong&gt;. If you want to know more about my reasoning, why I picked these technologies, and you haven't read my previous article, &lt;a href="https://dev.to/rezi/spa-with-4x100-lighthouse-score-part-1-the-tech-stack-gac"&gt;go and read it&lt;/a&gt; ;)&lt;/p&gt;

&lt;p&gt;This time we will attempt to build together a demo app and hopefully reach our goal. In this part of the serie, we just finetune the Sapper &lt;em&gt;hellow world&lt;/em&gt; app to max lighthouse score. In next part we'll build on top of our skeleton and create a fancy weather forecast app. &lt;/p&gt;

&lt;p&gt;Beware, this is not an article about Svelte/Sapper, so we will go through the implementation in rather fast pace. But don't worry, if you dont know these technologies, I'll guide you through.&lt;/p&gt;

&lt;p&gt;Be sure you have &lt;code&gt;node js&lt;/code&gt;, &lt;code&gt;npm&lt;/code&gt; and &lt;code&gt;npx&lt;/code&gt; installed. Now let's get a &lt;em&gt;hello world&lt;/em&gt; Sapper app running.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx degit "sveltejs/sapper-template#rollup" lighthouse-app
cd lighthouse-app

npm install
npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our Sapper app is now running on &lt;code&gt;http://localhost:3000&lt;/code&gt;&lt;br&gt;
If we run the lighthouse audit now, we are in green numbers but we won't get 4x100%. There are more reasons for getting 100% score:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Page is server rendered every time we hit a url&lt;/li&gt;
&lt;li&gt;CSS assets are loaded via &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; element&lt;/li&gt;
&lt;li&gt;Image is not in future format (webP)&lt;/li&gt;
&lt;li&gt;Image doesn't have optimal size&lt;/li&gt;
&lt;li&gt;Page metadata are missing&lt;/li&gt;
&lt;li&gt;Missing apple touch icons&lt;/li&gt;
&lt;li&gt;App is not running neither on http2 nor https&lt;/li&gt;
&lt;li&gt;Http is not redirected to https&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's fix them one by one:&lt;/p&gt;
&lt;h3&gt;
  
  
  1) Page is server rendered all the time we hit a url
&lt;/h3&gt;

&lt;p&gt;This one is easy, Sapper has another set of commands for exporting web app as a set of static pages and assets. Just run the build and commands like this instead of the one metioned above&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run export &amp;amp; npx serve __sapper__/export
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Done app is running on &lt;code&gt;http://localhost:5000&lt;/code&gt; and the performace is already rated to 100% ;)&lt;/p&gt;

&lt;h3&gt;
  
  
  2. CSS assets are loaded via &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; element
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; behavior is not that bad, css files are made per component. So once you hit other page with same component, you already have the CSS cached by service worker. The default behavior seems to be good for apps with heavily repeating components, not for our app ;) Probably better for us, in terms of speed, is lighthouse's proposed way of injecting crucial css inline to the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; element and thus avoiding the render-blocking request for sepearate css files. Let's change it. &lt;br&gt;
Open &lt;code&gt;rollup.config.js&lt;/code&gt; and change &lt;code&gt;emitCss&lt;/code&gt; flag in svelte plugin to &lt;em&gt;false&lt;/em&gt;.&lt;br&gt;
There is one more render blocking request and it is the &lt;code&gt;global.css&lt;/code&gt;. Go to &lt;code&gt;src/template.html&lt;/code&gt; and comment it out (we will maybe use it later).&lt;br&gt;
For now lets use some base css directly in &lt;code&gt;template.html&lt;/code&gt;. Add following code above the commented stylesheet link:&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;style&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;html&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;box-sizing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;border-box&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;14px&lt;/span&gt; &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="m"&gt;6&lt;/span&gt; &lt;span class="err"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;100vw&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="m"&gt;1280&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="n"&gt;screen&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;min-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1280px&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="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="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;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Roboto&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;-apple-system&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BlinkMacSystemFont&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Segoe&lt;/span&gt; &lt;span class="n"&gt;UI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Oxygen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="n"&gt;Ubuntu&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Cantarell&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Fira&lt;/span&gt; &lt;span class="n"&gt;Sans&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Droid&lt;/span&gt; &lt;span class="n"&gt;Sans&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Helvetica&lt;/span&gt; &lt;span class="n"&gt;Neue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;sans-serif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;line-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#333&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Congratulations, you just made your app half way responsive.&lt;/p&gt;

&lt;p&gt;The weird font size calculation is my minified version of something called &lt;em&gt;poly fluid sizing&lt;/em&gt;. your base font size now scales from 14px to 20px as the screen grows to 1280px. To whatever you apply size in rems, will scale with it. 1rem is now something between 14-20px depending on screen width.&lt;/p&gt;

&lt;p&gt;Rebuild the app and voilà. Even with the regular &lt;code&gt;npm run dev&lt;/code&gt; we get 100% performance.&lt;/p&gt;

&lt;h3&gt;
  
  
  3) Image is not in future format (webP)
&lt;/h3&gt;

&lt;p&gt;Just take the image &lt;code&gt;static/great-success.png&lt;/code&gt; and turn it first to jpg (so we loose alpha channel). Then convert it for instance with &lt;a href="https://webp-converter.com/"&gt;webP convertor&lt;/a&gt;. Copy the image to the &lt;code&gt;static&lt;/code&gt; folder and adjust its html in &lt;code&gt;src/routes/index.svelte&lt;/code&gt; accordingly.&lt;/p&gt;

&lt;h3&gt;
  
  
  4) Image Doesn't have optimal size
&lt;/h3&gt;

&lt;p&gt;Not only it has wrong size, it is also one and only size the img tag offer. We can do better, we will use &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; with &lt;code&gt;srcset&lt;/code&gt; tag. Go to &lt;a href="https://www.responsivebreakpoints.com/"&gt;responsivebreakpoints.com&lt;/a&gt; and upload our webP img, set max resolution to 400 (it is the image max size it can get in our app). Save images to &lt;code&gt;static&lt;/code&gt; folder. Replace old &lt;code&gt;img&lt;/code&gt; tag with new &lt;code&gt;picture&lt;/code&gt; one, which was generated by the tool. Done.&lt;/p&gt;

&lt;h3&gt;
  
  
  5) Page metadata are missing
&lt;/h3&gt;

&lt;p&gt;Go to &lt;code&gt;src/routes/index.html&lt;/code&gt; and any other page(route) you want to optimize and add following metatag into the &lt;code&gt;&amp;lt;svelte:head&amp;gt;&lt;/code&gt; tag.&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;meta&lt;/span&gt;
    &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"description"&lt;/span&gt;
    &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"Welcome to sapper app"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we get 100% SEO rating on every page where we added the meta description&lt;/p&gt;

&lt;h3&gt;
  
  
  6) Missing apple touch icons
&lt;/h3&gt;

&lt;p&gt;Grab some nice simple icon and process it with &lt;a href="https://realfavicongenerator.net/"&gt;realfavicongenerator&lt;/a&gt;.&lt;br&gt;
Copy generated code to &lt;code&gt;src/template.html&lt;/code&gt; and files to &lt;code&gt;static&lt;/code&gt; folder&lt;/p&gt;

&lt;h3&gt;
  
  
  7) App is not running neither on http2 nor https
&lt;/h3&gt;

&lt;p&gt;Now it is time to move our app to the internet. We could also run our local server with npm spdy server and self signed certificates. But it won't prepare us for real internet conditions. We will deploy our app to &lt;a href="https://www.netlify.com/"&gt;Netlify&lt;/a&gt;. it is free for testing purposes, it is fast, works as CDN, and even provides serverless functions and auth middleware.&lt;/p&gt;

&lt;p&gt;Easiest setup is to create netlify app from github repository. Create a repository on github and push your &lt;em&gt;lighthouse-app&lt;/em&gt; to it.&lt;br&gt;
Now sign up for &lt;a href="https://netlify.com/"&gt;netlify&lt;/a&gt;. In your netlify &lt;em&gt;site&lt;/em&gt; page, click &lt;strong&gt;New site from git&lt;/strong&gt;. Once you are in step 3, you will be asked for &lt;em&gt;Build command&lt;/em&gt; and &lt;em&gt;Publish directory&lt;/em&gt;. Fill first with &lt;code&gt;npm run export&lt;/code&gt; and second with &lt;code&gt;__sapper__/export&lt;/code&gt;. Then click &lt;strong&gt;Deploy site&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Each time you push to a chosen branch in github, netlify hooks into it and builds and deploys a new version.&lt;/p&gt;

&lt;h3&gt;
  
  
  8) Http is not redirected to https
&lt;/h3&gt;

&lt;p&gt;This is already done, as Netlify provides https, http/2 and redirects out of the box.&lt;/p&gt;

&lt;p&gt;Now we only need to measure the results. Copy the url netlify generated for your web app. For me it is &lt;a href="https://eloquent-dijkstra-d8bd7b.netlify.com/"&gt;https://eloquent-dijkstra-d8bd7b.netlify.com/&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Now go to the new online measuring tool (lighthouse) &lt;a href="https://web.dev/measure/"&gt;web.dev&lt;/a&gt;. We should measure by this one, not by plugin in chrome or dev tools. The web.dev tool give you more stable results, not influenced by quality of your internet connection and your computer's power.&lt;/p&gt;

&lt;p&gt;Here are the results, not bad, considering, it took us 10 minutes from start to end. &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%2Fi%2Ftt39iz5gjedxhjz4paaf.gif" 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%2Fi%2Ftt39iz5gjedxhjz4paaf.gif" alt="Lighthouse audit results" width="634" height="183"&gt;&lt;/a&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%2Fi%2Fq02gb7x8jz3afa582sjg.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%2Fi%2Fq02gb7x8jz3afa582sjg.png" alt="Lighthouse audit times" width="800" height="156"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We have a skeleton for a future app. We are backed by full featured component based framework with all the things like 2-way-binding, state management, routing, animations etc. &lt;/p&gt;

&lt;p&gt;In the next part of this serie we try to turn this &lt;em&gt;hello world&lt;/em&gt; app to a more real-world app example: a Weather forecast app. We will see whether we are able to keep our perfect score with a growing complexity and connection to an external API.&lt;br&gt;
In the meantime you should go and learn some basics about &lt;a href="https://svelte.dev/"&gt;svelte&lt;/a&gt; and &lt;a href="https://sapper.svelte.dev/"&gt;sapper&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;See you next time ;)&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>svelte</category>
      <category>webperf</category>
      <category>webdev</category>
    </item>
    <item>
      <title>SPA with 4x100% lighthouse score - Part 1: The tech stack</title>
      <dc:creator>Tomas Rezac</dc:creator>
      <pubDate>Sun, 22 Mar 2020 16:07:29 +0000</pubDate>
      <link>https://forem.com/ciklum_czsk/spa-with-4x100-lighthouse-score-part-1-the-tech-stack-gac</link>
      <guid>https://forem.com/ciklum_czsk/spa-with-4x100-lighthouse-score-part-1-the-tech-stack-gac</guid>
      <description>&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%2Fi%2F0tzn6a3io9id0io61wqc.jpg" 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%2Fi%2F0tzn6a3io9id0io61wqc.jpg" alt="Alt Text" width="800" height="640"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The goal
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;To choose a right tech stack for building an SPA (Single Page App), which gets the best possible score in lighthouse audit&lt;/strong&gt;. Just telling in advance, we are looking for something simple and elegant, not for a pure JS solution or some rocket science.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why to even attempt?
&lt;/h3&gt;

&lt;p&gt;There are multiple benefits: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Our app will get best technical rating by Google Search Engine. SEO is no longer the only indicator, responsible for sorting of search results. Performance and accessibility are getting more and more important. See more in &lt;a href="https://www.searchmetrics.com/knowledge-base/google-lighthouse-ranking-factors"&gt;searchmetrics.com&lt;/a&gt; study from 2019.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Users of Our app will be happy even when opening it with slow device or poor network connection. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We’ll be forced to make simple app, containing only what’s really needed. With no bloated, hard to understand functionality.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Lighthouse indices
&lt;/h3&gt;

&lt;p&gt;Lighthouse audits sorts out its findings and proposals into four categories: &lt;strong&gt;Performance&lt;/strong&gt;, &lt;strong&gt;Accessibility&lt;/strong&gt;, &lt;strong&gt;Best Practices&lt;/strong&gt; and &lt;strong&gt;SEO&lt;/strong&gt;. All of them are important for ranking in google search, but some are more important than others. Getting the last three to 100% won’t be that hard. We just need to add all required metadata to a page and set up server correctly. Getting Performance to 100% is different story — we will focus on that later. Based on &lt;a href="https://www.searchmetrics.com/knowledge-base/google-lighthouse-ranking-factors"&gt;searchmetrics.com&lt;/a&gt; study &lt;em&gt;Lighthouse Ranking Factors 2019&lt;/em&gt;, top ranked search results (when searching generic keywords like “shoes”) have something in common. They are best in: Performance, time to First Contentful Paint, following best Practices, size of DOM, they use webP images and run over https or better http/2. &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%2Fi%2Fuekjp36mgt336qvug956.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%2Fi%2Fuekjp36mgt336qvug956.png" alt="searchmetrics white paper showing top 20 search results in google search with data about their speed of FCP" width="800" height="384"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;searchmetrics white paper showing top 20 search results in google search with data about their speed of FCP&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;SEO and Accessibility are also important for them, but correlation between its score and ranking in top 20 is not obvious, and rather misleading. &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%2Fi%2Ffuik0z0nc585i1eei45o.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%2Fi%2Ffuik0z0nc585i1eei45o.png" alt="searchmetrics white paper showing top 20 search results in google search with data about their SEO" width="800" height="404"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;searchmetrics white paper showing top 20 search results in google search with data about their SEO&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Why making fast SPA is so hard in real world?
&lt;/h3&gt;

&lt;p&gt;We as developers experience era of fancy frontend frameworks. Interactive functionality, which we could have dreamed about 10 years ago, is not only possible these days, it is often matter of minutes to implement. It has been a giant leap for developers, but rather small one for users. Our networks are 10 times faster; our computers are 10 times more powerful, our frameworks are 10 times more awesome, but it takes similar time to load average web page, as 10 years ago. How is that even possible?&lt;/p&gt;

&lt;p&gt;You always wish to have extraordinary web, which is step ahead of your competition. Full of interactivity, animations, images and videos. Your product owner wants it as soon as possible. Your stake holders want all stats about visitors. Your FCO wants more ads to make more money, and so it goes on. It is a common pattern I have been observing over 13 years of my professional  carrier as web developer ;) In the end, requirements in its MVP (Minimal Viable Product) are already so demanding and way beyond what users really need and want. Performance is simply not a priority for most of software teams. As Jeremy Wagner says in: &lt;em&gt;&lt;a href="https://css-tricks.com/innovation-cant-keep-the-web-fast"&gt;Innovation Can’t Keep the Web Fast&lt;/a&gt;&lt;/em&gt;. A “hello world” app with all above mentioned requirements would already be 1 or 2 MB. Especially if you use something like React, or Angular. We can do better. We should start creating web for users again!&lt;/p&gt;

&lt;p&gt;Let’s try to learn from big companies and their successful products. Facebook for instance; its SPA homepage (on desktop) is loaded in about 10s, has 9.8MB and it is all done in 350requests. There are some advanced techniques used, but it is not point of this article. I can just say it performs better than it looks. To be honest, its visible content is loaded in 4s; it is not bad, considered the app complexity. But do we need all that complexity on initial load? I would say no. Just check facebook.com lighthouse ratings; it is a disgrace.&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%2Fi%2F4gz8rgtcne41q3xzm4lq.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%2Fi%2F4gz8rgtcne41q3xzm4lq.png" alt="facebook.com in desktop chrome MacBook Pro 2018 i7 (Simulated Slow 4G, 4x CPU Slowdown)" width="800" height="311"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;facebook.com in desktop chrome MacBook Pro 2018 i7 (Simulated Slow 4G, 4x CPU Slowdown&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Maybe it is not that great idea to have one of the most loaded pages on the internet as a model. We will try to achieve 4x100% with far less complex app. You always need to think twice, whether an app really needs to be so huge and complex. Especially when you can lazy load most of the stuff.&lt;/p&gt;

&lt;h3&gt;
  
  
  Choosing right tech stack for 4x100%
&lt;/h3&gt;

&lt;p&gt;I am sorry, but very probably your favourite framework won’t do it. To choose one which will, we need to understand all restrictions and requirements.&lt;/p&gt;

&lt;p&gt;First of all, we know, we need a JS code because we want to build SPA. We cannot do that with server rendered pages, without JS.&lt;/p&gt;

&lt;p&gt;We need either vanilla JS or a lightweight framework. I can tell you straight away, that if we aim for 4x100% on slower mobile devices, we cannot use any of the holy trinity of JS frameworks (React, Angular, Vue).&lt;/p&gt;

&lt;p&gt;Just for illustration, a React hello world app itself (one dummy screen with no routing and data management) won’t pass the audit with 100% Performance. Not even Next.js (server prerendered and optimised React app) can reach the desired Lighthouse Performance score, as it still contains a minified build of React and only gets us to 96% for Performance.&lt;br&gt;
 Why? Because even if an app is prerendered on server, React itself is still loaded for later hydration. Processing(parsing and compiling) React code by a browser is quite expensive operation, responsible for the score. It is important to understand that cost of 100kB of HTML is very different from 100kB of Javascript. An option is to load javascript in async mode, after page is rendered. But it has negative impact on other measured index —” Time to interactive”. Not even mentioning that above stated score was achieved with blank page, on very fast machine on local server. Once we start adding our own code and move the app to internet, we will be doomed; the score will drop significantly.&lt;/p&gt;

&lt;p&gt;Inferno, Preact and some other clones of react would be able to reach the score, but we won’t use them. They wouldn’t give us the luxury of full featured framework and we would end up with writing a lot of pure JS code along those libraries. &lt;/p&gt;

&lt;p&gt;Fortunately there is an unspoken demand for what we are attempting here. There are frameworks, popping up right now, trying to please both, users and developers. They offer rich features and minimal trace at the same time. For our purpose we need two things from such a framework: &lt;/p&gt;

&lt;p&gt;1) to support easy lazy-loading of almost anything and &lt;br&gt;
2) minimal or no size on its own. &lt;/p&gt;

&lt;p&gt;Splitting code to lot of smaller bundles enables parallelisation of JS parsing and releases the main thread for more important tasks (see more on V8 blog &lt;a href="https://v8.dev/blog/cost-of-javascript-2019"&gt;The cost of Javascript&lt;/a&gt;). If we want to fulfil the second point, we will need to get rid of a framework. But as long we want to keep framework’s declarative syntax, there is only one solution to this riddle: a compiler.&lt;/p&gt;

&lt;h3&gt;
  
  
  A compiler
&lt;/h3&gt;

&lt;p&gt;Probably the most endorsed “framework” of this type is &lt;strong&gt;Svelte&lt;/strong&gt;. With &lt;strong&gt;Sapper,&lt;/strong&gt; its pre-rendering counterpart, they fit to our requirements. So let’s focus on them.&lt;/p&gt;

&lt;p&gt;It will be good to know, what are the limits of compiler compared to regular framework, The fundamental difference is in a way how its code is run in a browser. With regular FE framework you are able to load such a framework by &lt;code&gt;&amp;lt;script src=”framework.js”&amp;gt;&lt;/code&gt; tag and then just write your code inside another &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag. None of the modern frameworks encourage developers to follow this pattern, and with most of them you would have hard time to get it running this way. But you can, it gives you power to dynamically create components and inject them during runtime to your app. It can be “must have” in some apps, but — let me exaggerate a bit — in 99.9% of them you don’t need it. For those 99.9% you can use a compiler.&lt;/p&gt;

&lt;p&gt;Svelte is a compiler, which accepts code written in similar manner as in React, Angular or Vue (component centred architecture), but is compiled to direct DOM manipulation instructions. If you don’t use a feature of Svelte, it won’t be output to a production bundle. If your page is just 10KB of HTML and CSS, then the svelte generated page will have about 10KB. If you use one two-way binding in that page, you will get maybe 0.1KB extra. Definitely there won’t be a 100KB framework handling your one two-way binding ;) With Svelte you can write high level declarative code and you will end up with highly optimised native JS code. With svelte we know, we start with minimal burden.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Jamstack
&lt;/h3&gt;

&lt;p&gt;So we chose the tech, but there are still decisions to do. Should we just bundle an app to big JS file, prerender it  on server, or serve it as static assets? Best option seems to be be the last one. It has several advantages: 1) Code is split to smaller chunks. 2) Content loaded for first meaningful paint is served from a static HTML file which can be easily served over CDN. &lt;/p&gt;

&lt;p&gt;Once static page is loaded, we can fetch JS and add some dynamic functionality to it. We can even do some api requests and customise the page for a user. This approach is called Jamstack, it is successor of statically generated pages. Jamstack is bringing API and more custom content to static generators. Jamstack stands for &lt;strong&gt;J&lt;/strong&gt;S, &lt;strong&gt;A&lt;/strong&gt;pi, &lt;strong&gt;M&lt;/strong&gt;arkup.&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%2Fi%2F90qc0lkz4dby4u7ltx4s.jpg" 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%2Fi%2F90qc0lkz4dby4u7ltx4s.jpg" alt="Jamstack = JS + API + Markup" width="800" height="640"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fortunately for us Sapper does support static generation of pages. It also provide basic setup of service worker for subsequent loads of the app. It comes with some minor bells and whistles supporting prefetching of pages before you hit them, basic in app routing. It all in cost of 13KB (before G-zipping). &lt;/p&gt;

&lt;p&gt;FE is just one side of the puzzle. We will also need reliable CDN server, fast API (cloud - optimally - distributed database). User Authentication and authorization of requests. These BE and middleware related topics are not main focus of this article, but we will touch them, because…&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;Part 2&lt;/strong&gt; of this serie, we will try to prove our tech stack in a demo app. &lt;strong&gt;We are gonna build and deploy &lt;em&gt;hello world&lt;/em&gt; SPA&lt;/strong&gt;… In &lt;strong&gt;Part 3&lt;/strong&gt; we will turn our dummy app into real weather forecast SPA. &lt;/p&gt;

&lt;p&gt;See you next time;)&lt;/p&gt;

</description>
      <category>lighthouse</category>
      <category>javascript</category>
      <category>svelte</category>
      <category>webperf</category>
    </item>
  </channel>
</rss>
