<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Miki Stanger</title>
    <description>The latest articles on DEV Community by Miki Stanger (@mimafogeus2).</description>
    <link>https://dev.to/mimafogeus2</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%2F185242%2F0c59e4df-0569-4765-9d4c-97e86e914365.jpg</url>
      <title>DEV Community: Miki Stanger</title>
      <link>https://dev.to/mimafogeus2</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mimafogeus2"/>
    <language>en</language>
    <item>
      <title>Randomness and Random Numbers in CSS</title>
      <dc:creator>Miki Stanger</dc:creator>
      <pubDate>Mon, 23 Jun 2025 16:04:02 +0000</pubDate>
      <link>https://dev.to/mimafogeus2/randomness-and-random-numbers-in-css-2gfl</link>
      <guid>https://dev.to/mimafogeus2/randomness-and-random-numbers-in-css-2gfl</guid>
      <description>&lt;p&gt;Recently, I created a &lt;a href="https://codepen.io/mimafogeus2/pen/zxGMBoZ" rel="noopener noreferrer"&gt;Missile Command kinda-clone&lt;/a&gt;, using HTML and CSS only. It was my first CSS-only game, and a really fun project to work on.&lt;/p&gt;

&lt;p&gt;One of the most useful tools when creating games is &lt;strong&gt;randomness&lt;/strong&gt;. Randomness can help by making every game different - different enemies and collectibles spawning in different locations, random cards for your deck, and much more. It’s also helpful for creating special effects (consider particle-based effects, where many particles move in different directions and speeds to create explosions for example) and game worlds (in my Missile Command clone, for example, you’ll have buildings with different heights at different positions every time, and each will have different windows whose light is randomly on or off).&lt;/p&gt;

&lt;p&gt;As of the time of writing, CSS &lt;strong&gt;doesn’t have a random number generator&lt;/strong&gt;.&lt;br&gt;
There might be one &lt;a href="https://css-tricks.com/almanac/functions/r/random/" rel="noopener noreferrer"&gt;in the future&lt;/a&gt;, but for now, we’ll have to create our own (kind of).&lt;/p&gt;

&lt;p&gt;But let’s begin at the beginning:&lt;/p&gt;
&lt;h2&gt;
  
  
  Who am I?
&lt;/h2&gt;

&lt;p&gt;I’m Miki Stanger, a freelance front-end developer and architect with 15 years experience in the industry.&lt;br&gt;
I like creating code that’s easy to maintain, and to work on both UX and the underlying stuff that makes the whole project work (so, for example, both perfecting the edge cases of a table behavior, and maybe implementing the underlying table logic myself).&lt;br&gt;
You can learn more about me and what I do in my &lt;a href="https://about.me/mikistanger" rel="noopener noreferrer"&gt;about.me page&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  A Note About Range Notations
&lt;/h2&gt;

&lt;p&gt;My math is very rusty, but among my few memories there’s the knowledge that there’s a way to write ranges.&lt;br&gt;
A range is written in as a coma-separated pair of numbers, with either square brackets, round parentheses, or one of each around them. A square bracket next to a number marks that the range &lt;strong&gt;includes&lt;/strong&gt; that number. A round parenthesis next to a number means that number is &lt;strong&gt;excluded&lt;/strong&gt; from that range.&lt;/p&gt;

&lt;p&gt;So, the notation: &lt;strong&gt;[1, 6)&lt;/strong&gt; means “All numbers between 1 and 6, excluding 6”. So, everything from 1 to 5.99999…&lt;/p&gt;

&lt;p&gt;My rusty math knowledge also means that I might lack terms or not use the most efficient way to do things here. But it works.&lt;/p&gt;
&lt;h2&gt;
  
  
  Random Numbers 101
&lt;/h2&gt;

&lt;p&gt;Programming languages usually provide you with a random number generator. In JavaScript, for example, that would be the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random" rel="noopener noreferrer"&gt;Math.random&lt;/a&gt; function, which returns a number at the range of [0, 1) (that’s a number between 0 and 1, including 0 and excluding 1).&lt;/p&gt;

&lt;p&gt;Using such a number, we can get a random number of any range, by applying some math.&lt;br&gt;
For example, given a minimum number &lt;em&gt;m&lt;/em&gt;, a maximum number &lt;em&gt;n&lt;/em&gt;, and a random number &lt;em&gt;r&lt;/em&gt; in the range of [0, 1), you could get to a number of the range of [m, n) with the following formula:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;m + r * (n - m)&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So, to get a number between 1 and 6 (excluding 6), we’ll do:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;1 + Math.random() * (6 - 1)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;In order to get an integer, we’ll simply round down the result, thus removing the fraction part from the number. We’ll also add 1 to the multiplier of &lt;em&gt;r&lt;/em&gt;, to include all numbers that are in the range of [m, m+1), which will be rounded down to m:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;floor(m + r * (n - m + 1))&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So in order to do a standard 1d6 (that’s a six-sided dice, or [1, 6] with integers only), we’ll do:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Math.floor(1 + Math.random() * (6 - 1 + 1))&lt;/code&gt;&lt;br&gt;
(or simply &lt;code&gt;Math.floor(1 + Math.random() * 6)&lt;/code&gt;)&lt;/p&gt;
&lt;h2&gt;
  
  
  Ma, They Took My Random Number Generator
&lt;/h2&gt;

&lt;p&gt;Back to CSS and it’s lack of a random number generator.&lt;/p&gt;

&lt;p&gt;Because we don’t have that in CSS, we’ll have to create one of our own.&lt;br&gt;
And because there’s no way I know to do that, we’ll have to create a &lt;strong&gt;pseudo-random number generator&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A pseudo-random number generator is a &lt;strong&gt;deterministic function that produces a sequence of numbers that seem random&lt;/strong&gt;. Optimally, this number is within a specific range, so we could work it into any range we want.&lt;/p&gt;

&lt;p&gt;CSS comes with some &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Values_and_Units/Using_CSS_math_functions" rel="noopener noreferrer"&gt;math functions&lt;/a&gt; which you can use. Among those functions (at least those that I recognized) I found two that I thought could be useful: &lt;code&gt;sin&lt;/code&gt; and &lt;code&gt;cos&lt;/code&gt;. Those are the best matches I found, but they aren’t perfect.&lt;/p&gt;
&lt;h2&gt;
  
  
  The &lt;strong&gt;sin&lt;/strong&gt; of using trigonometric functions (eh?)
&lt;/h2&gt;

&lt;p&gt;The sine and cosine functions are great for us in several ways:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;They accept any and all numbers. This makes our life easy when providing input.&lt;/li&gt;
&lt;li&gt;They output a number within the range of [-1, 1]. This could easily be converted to [0, 1], by doing &lt;em&gt;(1 + sin(r)) / 2&lt;/em&gt;, which can then be converted to any range of numbers.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;However:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The output is continuous. Close values will give you close numbers. This could create patterns that don’t feel random.&lt;/li&gt;
&lt;li&gt;The output is cyclical. sin(x) == sin(x % 360deg) &lt;em&gt;(The &lt;code&gt;%&lt;/code&gt; operator is the modulo operator. &lt;code&gt;a % b&lt;/code&gt; returns the remainder of &lt;code&gt;a / b&lt;/code&gt;)&lt;/em&gt;. This, together with the previous bullet, mean that not only close numbers give you close value, but numbers whose modulo 360 results are close will also behave the same way.&lt;/li&gt;
&lt;li&gt;The output has a non-random shape (of a wave). This means that by putting in numbers with a constant difference or a constant modulo difference, you’ll get numbers that reflect the shape of the function, which will, again, not feel random.&lt;/li&gt;
&lt;li&gt;[0, 1] is not [0, 1). The fact that the resulting range includes the number 1, means that when using the random integers formula, there’s a tiny chance you’ll get a number you don’t want.&lt;/li&gt;
&lt;li&gt;The function’s distribution is not linear.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For most of these issues, there are workarounds. The final result will not be perfect, but for our needs it’s good enough. Let’s move on and see how our implementation brings us to a good enough solution.&lt;/p&gt;
&lt;h2&gt;
  
  
  But what do we pass to the sine function?
&lt;/h2&gt;

&lt;p&gt;The sine function alone doesn’t help us; we’ll need to give it an input. That input is an important part of the pseudo-random secret sauce.&lt;/p&gt;

&lt;p&gt;The CSS tl;dr is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;—random-number: calc(var(--seed) * var(--index) * var(--index) * noise);&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let’s discuss.&lt;/p&gt;
&lt;h3&gt;
  
  
  calc
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;calc&lt;/code&gt; is a CSS function that allows us to combine different variables, operators etc. to do a complex calculation.&lt;/p&gt;
&lt;h3&gt;
  
  
  var
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;var&lt;/code&gt; is a function that allows us to retreive the value of a CSS variable/custom property&lt;/p&gt;
&lt;h3&gt;
  
  
  --seed
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;--seed&lt;/code&gt; is a CSS variable. It is set at the top of the project (under the &lt;code&gt;:root&lt;/code&gt; or &lt;code&gt;body&lt;/code&gt; selectors) and it equals to a large number. By using a large number, we prevent the numbers from being close to each other, and solve problem #1.&lt;br&gt;
Changing the seed will change the results of all of the random numbers in your project.&lt;/p&gt;
&lt;h3&gt;
  
  
  --index
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;--index&lt;/code&gt; is another CSS variable. When there’s a series of objects (In my game that could be, for example, the stars in the sky), each of the objects gets an index. You could do it with several &lt;code&gt;nth-child&lt;/code&gt; pseudo-selectors, but I was lazy and used the style 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;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"star"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"--index: 1"&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;"star"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"--index: 2"&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;"star"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"--index: 3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Another --index
&lt;/h3&gt;

&lt;p&gt;This handles some of our problems. A simple &lt;code&gt;seed * index * noise&lt;/code&gt;, where the index increments by 1 between objects, would give us a series of numbers with a constant difference between them. This also translate to a very visible pattern when doing a modulo operator on them, which means that it’ll show in a sine or a cosine function. As we discussed above, this will create a not-random-feeling pattern, which is the opposite of what we want.&lt;br&gt;
However, doing &lt;code&gt;seed * index * index * noise&lt;/code&gt;, which is actually &lt;code&gt;seed * index² * noise&lt;/code&gt;, means that the linear differences between indices will not translate to linear differences in the sine/cosine function’s output. Problems #2 and #3 - solved!&lt;/p&gt;
&lt;h3&gt;
  
  
  noise
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;noise&lt;/code&gt; is a hard-coded number (could be -1, 17, 8, 7003, anything). It’s different for every number generated, just so you won’t get the same number for the same seed and index. You don’t have to multiply the noise - you can add, subtract or divide it. You can also add multiple noise numbers (e.g. &lt;em&gt;r * 17 - 5&lt;/em&gt;).&lt;br&gt;
The noise is also an opportunity to introduce a unit to the number. By multiplying the random number by &lt;code&gt;39deg&lt;/code&gt;, &lt;code&gt;8px&lt;/code&gt;, &lt;code&gt;1%&lt;/code&gt; etc., we add that unit to the generic random number.&lt;/p&gt;
&lt;h2&gt;
  
  
  A Range of Possibilities
&lt;/h2&gt;

&lt;p&gt;Now that we have a random number, let’s convert it to the range we’d like.&lt;br&gt;
Our pseudo-random number is in the range of [-1, 1] (the possible range of outputs from both &lt;code&gt;sin&lt;/code&gt; and &lt;code&gt;cos&lt;/code&gt;). This is workable and easy for many ranges, but by converting the range to [0, 1] we could (almost) use the simple generic formulas from above to generate a number of any range.&lt;br&gt;
In order to do that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We add 1 to the result, bringing its range to [-1 + 1, 1 + 1] = [0, 2]&lt;/li&gt;
&lt;li&gt;We divide the result by two, bringing its range to [0, 1]&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;--random-number: calc((1 + sin(var(--seed) * var(--index) * var(--index) * 17)) / 2); // 17 is the noise
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And if we’d like to use this number for the range [1, 10], we could use our formula from above, which will result in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;--from-one-to-ten: calc(1 + var(--random-number) * 9)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Removing the Top
&lt;/h2&gt;

&lt;p&gt;There’s still one important difference between our pseudo-random numbers and the random numbers we get from &lt;code&gt;Math.random&lt;/code&gt; - the range is not exactly the same. Instead of a number within [0, 1). we get a number within [0, 1]. This means there’s a (tiny, tiny) chance of getting the number on the top of the range, which is especially problematic if you’d like to round down the the results to get an integer.&lt;/p&gt;

&lt;p&gt;I don’t have a perfect solution for this. All of the solutions I found will mean losing a small range of numbers. There’s one, though, where you only replace the 1 with 0, doubling the chance for the input value to be 0 but otherwise not changing the equation.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/sign" rel="noopener noreferrer"&gt;sign&lt;/a&gt; CSS function returns the sign of the number. You get -1 for a negative number, +1 for a positive number, 0 for zero (and -0 for a negative zero, which is &lt;a href="https://www.wikiwand.com/en/articles/Signed_zero" rel="noopener noreferrer"&gt;a thing in programming&lt;/a&gt;, but doesn’t matter to us right now).&lt;/p&gt;

&lt;p&gt;So, by doing &lt;code&gt;sign(1 - var(--random-number))&lt;/code&gt;, we’ll get a positive sign (the number 1) for any random number but exactly 1, for which we'll get 0.&lt;br&gt;
Multiplying this with our random number will set the number to 0 if &lt;code&gt;--random-number&lt;/code&gt; equals to one, which brings us to - [0, 1):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;--capped-random-number: var(sign(1 - var(--random-number)) * var(--random-number));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Having a double chance to get one number over the others is not perfect. However, we’re talking a lot of numbers and a very small chance. For many, probably most, cases - that should be good enough, and definitely better from the chance to get a 7 in a six-sided dice roll.&lt;/p&gt;

&lt;p&gt;Now we can use the random integers function from above. For a random integer between 1 and 10, in CSS, it'll look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;--random-integer: round(down, calc(1 + var(--capped-random-number) * (9 + 1));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that in order for &lt;code&gt;round&lt;/code&gt; to work, you either have to round a number without a unit, or pass a 3rd argument of &lt;code&gt;1&amp;lt;unit&amp;gt;&lt;/code&gt; (e.g. &lt;code&gt;1px&lt;/code&gt;), which tells it to round by this unit. You can read more about the &lt;code&gt;round&lt;/code&gt; CSS function &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/round" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What About the Non-Linear Distribution?
&lt;/h2&gt;

&lt;p&gt;While squaring &lt;code&gt;--index&lt;/code&gt; might help here, and while this might be visible mainly with a large number of random numbers with the same noise, I don’t have a true solution for this. However, for everything in my game - 80 stars, 12 buildings with 15 windows each, all affected by randomness - this was, again, good enough.&lt;/p&gt;

&lt;h2&gt;
  
  
  A New Experience Every Time
&lt;/h2&gt;

&lt;p&gt;So far, we managed to generate random numbers. But these will remain the &lt;strong&gt;same random numbers&lt;/strong&gt; every time you refresh, as &lt;code&gt;--seed&lt;/code&gt; is a constant for the whole project, &lt;code&gt;--index&lt;/code&gt; is a constant per element and the noise is a constant for every random number formula.&lt;/p&gt;

&lt;p&gt;If we could change the &lt;code&gt;--seed&lt;/code&gt; for every session, though, we’ll have a different experience every time - all of the random numbers will change.&lt;/p&gt;

&lt;p&gt;In order to do that, I piggybacked on a very important part of the game - the “Start Game” button.&lt;/p&gt;

&lt;p&gt;The HTML part looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"screen screen-main"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- Title screen stuff --&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;"start-game-button-wrapper"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"start-game-button"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"checkbox"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"start-game-button"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"checkbox"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"start-game-button"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"checkbox"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"start-game-button"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"checkbox"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"start-game-button"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"checkbox"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
            ...
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&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;"screen screen-game"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- Game stuff --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, in CSS, we do the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="err"&gt;.screen-game&lt;/span&gt; &lt;span class="err"&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="c"&gt;/* If there's any checked start-game-button, start the game */&lt;/span&gt;
    &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nd"&gt;:has&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;.start-game-button&lt;/span&gt;&lt;span class="nd"&gt;:checked&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="err"&gt;.screen-main&lt;/span&gt; &lt;span class="err"&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="nc"&gt;.screen-game&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;block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="err"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;/* The start game button wrapper is a grid containing all of the checkboxes, the "Start Game" label etc. */&lt;/span&gt;
    &lt;span class="nc"&gt;.start-game-button-wrapper&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;grid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="py"&gt;grid-template-columns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;repeat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="n"&gt;fr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;20&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;200px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="err"&gt;&amp;amp;:after&lt;/span&gt; &lt;span class="err"&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;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;"Start"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80px&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="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;pointer-events&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;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;300px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="err"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;/* The checkbox is now a tiny square. Those squares fill the start game button wrapper. */&lt;/span&gt;
    &lt;span class="nc"&gt;.start-game-button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;appearance&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="c"&gt;/* Hides the system checkbox UI */&lt;/span&gt;
        &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#3355ff&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;block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&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;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;15px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;/* Checking a checkbox selects two base seed numbers according to the position of the element within the parent */&lt;/span&gt;

    &lt;span class="c"&gt;/* That's the first base seed: */&lt;/span&gt;

    &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nd"&gt;:has&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;.start-game-button&lt;/span&gt;&lt;span class="nd"&gt;:checked:nth-child&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;20&lt;/span&gt;&lt;span class="nt"&gt;n&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;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="py"&gt;--seed1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4306301&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nd"&gt;:has&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;.start-game-button&lt;/span&gt;&lt;span class="nd"&gt;:checked:nth-child&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;20&lt;/span&gt;&lt;span class="nt"&gt;n&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="err"&gt;2&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="py"&gt;--seed1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5612987&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="o"&gt;...&lt;/span&gt;

    &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nd"&gt;:has&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;.start-game-button&lt;/span&gt;&lt;span class="nd"&gt;:checked:nth-child&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;20&lt;/span&gt;&lt;span class="nt"&gt;n&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="err"&gt;19&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="py"&gt;--seed1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1834393&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nd"&gt;:has&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;.start-game-button&lt;/span&gt;&lt;span class="nd"&gt;:checked:nth-child&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;20&lt;/span&gt;&lt;span class="nt"&gt;n&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="py"&gt;--seed1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;9229177&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;/* That's the second base seed: */&lt;/span&gt;

    &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nd"&gt;:has&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;.start-game-button&lt;/span&gt;&lt;span class="nd"&gt;:checked:nth-child&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;17&lt;/span&gt;&lt;span class="nt"&gt;n&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;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="py"&gt;--seed2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4306301&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nd"&gt;:has&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;.start-game-button&lt;/span&gt;&lt;span class="nd"&gt;:checked:nth-child&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;17&lt;/span&gt;&lt;span class="nt"&gt;n&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="err"&gt;2&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="py"&gt;--seed2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5612987&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="o"&gt;...&lt;/span&gt;

    &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nd"&gt;:has&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;.start-game-button&lt;/span&gt;&lt;span class="nd"&gt;:checked:nth-child&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;17&lt;/span&gt;&lt;span class="nt"&gt;n&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="err"&gt;16&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="py"&gt;--seed2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1834393&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nd"&gt;:has&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;.start-game-button&lt;/span&gt;&lt;span class="nd"&gt;:checked:nth-child&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;17&lt;/span&gt;&lt;span class="nt"&gt;n&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="py"&gt;--seed2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;9229177&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;/* By having a seed generated from two base seeds like this, we add much more options by adding a relatively small amount of rules and numbers. */&lt;/span&gt;
    &lt;span class="nt"&gt;--seed&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;--seed1&lt;/span&gt;&lt;span class="o"&gt;)&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;--seed2&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's some stuff going on here, so let’s explain it:&lt;/p&gt;

&lt;p&gt;The start button is made of many tiny checkboxes (designed to look like the button’s background). When you check one of those checkboxes, in addition to hiding the main screen and showing the game screen, the CSS variables &lt;code&gt;--seed1&lt;/code&gt; and &lt;code&gt;--seed2&lt;/code&gt; are set to two different numbers, which are added together to create &lt;code&gt;-seed&lt;/code&gt;.&lt;br&gt;
This means seed has 20 * 17 = 340 different options for a seed, which means that, depending on where the player clicked on the start button, they’ll get one of 340 different sets of random numbers.&lt;/p&gt;

&lt;h2&gt;
  
  
  To Sum It Up
&lt;/h2&gt;

&lt;p&gt;Using a pseudo-random number generator - even with a single seed - opens up interesting options. You could randomize positions, colors - anything that can accept that &lt;code&gt;calc&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;I hope you found this article as interesting as it was to me to write and implement it.&lt;/p&gt;

&lt;p&gt;If you have any question, comments, ideas or ways to improve upon these ideas - please let me know in the comments.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>css</category>
      <category>programming</category>
    </item>
    <item>
      <title>Tooltips! A Nice Singleton-y Way To Only Show One At A Time</title>
      <dc:creator>Miki Stanger</dc:creator>
      <pubDate>Tue, 17 Jan 2023 15:56:57 +0000</pubDate>
      <link>https://dev.to/mimafogeus2/show-one-tooltip-at-a-time-using-a-singleton-1dho</link>
      <guid>https://dev.to/mimafogeus2/show-one-tooltip-at-a-time-using-a-singleton-1dho</guid>
      <description>&lt;p&gt;Tooltips are trickier than they look; For the optimal experience, you have to handle many intricacies and edge cases.&lt;/p&gt;

&lt;p&gt;Positioning is complex, with its many edge cases (screen edges, scrolling). Hover behavior could be more intricate, so the tooltip remains open while you hover over it, and not just its target. Appearance and disappearance should both have delays and maybe animation. AND you don't want multiple tooltips appearing together and hiding each other.&lt;/p&gt;

&lt;p&gt;Today I'll write about this last part. Let me know if you'd like me to elaborate on the other parts :)&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;h2&gt;
  
  
  Who am I?
&lt;/h2&gt;

&lt;p&gt;I'm Miki Stanger, a front-end architect and advisor. I've been to the industry for 12 years. I currently accompany companies and help with architecture, decisions, mentoring and problem-solving.&lt;/p&gt;

&lt;p&gt;I'm always looking for cool people and projects to help with! You can read more about me and find my up-to-date contact information here: &lt;a href="https://about.me/mikistanger/" rel="noopener noreferrer"&gt;https://about.me/mikistanger/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;=== &lt;/p&gt;

&lt;h2&gt;
  
  
  Tooltips (And When To Use Them)
&lt;/h2&gt;

&lt;p&gt;Tooltips are a way to show additional information about something. They are usually triggered by hovering over an element, and should show an explanation about how to read or use a part of your system.&lt;/p&gt;

&lt;p&gt;Because it is sensitive to mouse movement and positioned next to other elements, it is best used for short texts; Large bodies of text will require the user to not touch their mouse for a while (even though some people move the mouse, click or select texts while reading or while multitasking), and might create a huge box that overshadows or even hides the parts it explains.&lt;/p&gt;

&lt;p&gt;If your explanation is more than a few sentences long, you could use a modal instead; A modal is more persistent and allows the use of photos and videos, and are thus a better medium for larger amounts of content.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;The premise of a tooltip is supposedly basic - Hover over something and some element should appear in the right position. However, if you have several available tooltips next to each others, this could happen:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fon71899a11k0ullftlz3.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fon71899a11k0ullftlz3.gif" alt="Multiple tooltips appearing over each other" width="362" height="136"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;See how the tooltips hide each other? This kind of clutter not only looks messy, it could also steal the user's focus from the one tooltip they're looking for.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution
&lt;/h2&gt;

&lt;p&gt;When we show a tooltip, we could simply &lt;strong&gt;hide the other ones&lt;/strong&gt;. &lt;br&gt;
As with a lot of tooltip-related things, this is a bit more complex than it sounds, especially when working with React. There are two approaches here:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Have a single tooltip component and control it through props.&lt;/strong&gt; You could pass the method through a store or using context, so they're available globally. This is not a bad way, except that it might unnecessarily trigger a full re-render. We'll look at a different approach that doesn't require all of those additional mechanisms.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Have multiple tooltip instances that live where they are used, and some way to turn the others off&lt;/strong&gt;. This is also achievable with a store or a context, but there's a better way (imo) to do it - &lt;strong&gt;manage hiding tooltips with a singleton&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  What Is A Singleton?
&lt;/h2&gt;

&lt;p&gt;A singleton is a single instance of an object that's exposed to the whole system (as opposed to creating multiple objects or instances of a class).&lt;/p&gt;

&lt;p&gt;A singleton is useful when one needs to guarantee the same options and methods will be available and persistent throughout the project. A common example for a singleton is a logging instance - a single instance of a class, with its configuration and options, which guarantee that all logging will be done with the same options and policies everywhere.&lt;/p&gt;
&lt;h2&gt;
  
  
  The &lt;code&gt;PreventMultipleTooltipsManager&lt;/code&gt; Singleton
&lt;/h2&gt;

&lt;p&gt;Our tooltip is a component that wraps another component and surrounds it with a tooltip-triggering component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nc"&gt;WithTooltip&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;isVisible&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setIsVisible&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&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;setIsVisibleToFalse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;setIsVisible&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&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;setIsVisible&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;setIsVisibleToTrue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;setIsVisible&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;setIsVisible&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="c1"&gt;// Rest of logic&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TooltipWithStylesAndStuff&lt;/span&gt;
        &lt;span class="na"&gt;onMouseEnter&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;setIsVisibleToTrue&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; 
        &lt;span class="na"&gt;onMouseLeave&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;setIsVisibleToFalse&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;TooltipWithStylesAndStuff&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;OUTSIDE of the component, we'll create a class and initiate a single instance of it - a singleton:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PreventMultipleTooltipsManager&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;preventMultipleTooltipsManager&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PreventMultipleTooltipsManager&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This singleton is going to hold a function that hides the currently visible tooltip (its &lt;code&gt;setIsVisibleToFalse&lt;/code&gt; function). When a new tooltip is shown, the singleton will allow us to use the now-previous tooltip's function and replace it with the current one's. We can do both operations in a single method, as they're always going to be called together in the same order:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PreventMultipleTooltipsManager&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;currentHideFunc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;changeTooltip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hideFunc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentHideFunc&lt;/span&gt;&lt;span class="p"&gt;?.();&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentHideFunc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;hideFunc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;preventMultipleTooltipsManager&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PreventMultipleTooltipsManager&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We're also going to add an &lt;code&gt;unmount&lt;/code&gt; method. This method will be used in a tooltip's teardown, in case it is removed from the DOM while visible. We'll later use it in a teardown function that's the return value of a &lt;code&gt;useEffect&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PreventMultipleTooltipsManager&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;currentHideFunc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;changeTooltip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hideFunc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentHideFunc&lt;/span&gt;&lt;span class="p"&gt;?.();&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentHideFunc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;hideFunc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;unmount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hideFunc&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;hideFunc&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="nx"&gt;currentHideFunc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentHideFunc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;preventMultipleTooltipsManager&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PreventMultipleTooltipsManager&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The singleton is done. Now we can call it inside the component. We'll use its &lt;code&gt;changeTooltip&lt;/code&gt; method in &lt;code&gt;isSetVisibleToTrue&lt;/code&gt;, and its &lt;code&gt;unmount&lt;/code&gt; method in a &lt;code&gt;useEffect&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nc"&gt;WithTooltip&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;isVisible&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setIsVisible&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&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;setIsVisibleToFalse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;setIsVisible&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&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;setIsVisible&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;setIsVisibleToTrue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;preventMultipleTooltipsManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;changeTooltip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;setIsVisibleToFalse&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;setIsVisible&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;setIsVisible&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setIsVisibleToFalse&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;preventMultipleTooltipsManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unmount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;setIsVisibleToFalse&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;setIsVisibleToFalse&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="c1"&gt;// Rest of logic&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TooltipWithStylesAndStuff&lt;/span&gt;
        &lt;span class="na"&gt;onMouseEnter&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;setIsVisibleToTrue&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; 
        &lt;span class="na"&gt;onMouseLeave&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;setIsVisibleToFalse&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;TooltipWithStylesAndStuff&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's it! Look at it behaving nicely:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk8009y9trqzmft0ya5vb.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk8009y9trqzmft0ya5vb.gif" alt="Now, only the required tooltip shows, while the others disappear" width="362" height="136"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Tooltips are a wonderful case of how thinking about the small details makes a difference between buggy, unfriendly component and a delightful one. Today, we dug into one of several such details, learned what a singleton is, and use this knowledge to make our tooltips better.&lt;/p&gt;

&lt;p&gt;What do you think? How would you implement a solution to this same problem?&lt;/p&gt;

</description>
      <category>gratitude</category>
    </item>
    <item>
      <title>Enforce Git Hooks in Monorepos with Husky - But How?</title>
      <dc:creator>Miki Stanger</dc:creator>
      <pubDate>Tue, 13 Dec 2022 05:45:50 +0000</pubDate>
      <link>https://dev.to/mimafogeus2/enforce-git-hooks-in-monorepos-with-husky-but-how-3fma</link>
      <guid>https://dev.to/mimafogeus2/enforce-git-hooks-in-monorepos-with-husky-but-how-3fma</guid>
      <description>&lt;p&gt;&lt;strong&gt;How to set up Husky hooks in a monorepo? Why should you? How to use them to enforce linting policies among your team?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It's another day of work. It's a good day at work - you finish stuff! You commit your changes and push them, and go to make yourself some caffeinated beverage while you wait for the CI to do its thing.&lt;br&gt;
You come back and spill your beverage in shock as you see the CI failed! If only you could've prevented it!&lt;br&gt;
You remember your friend talking about git hooks, and setting up something for themselves, but their idea for sharing was to send you a file. "It's hard to enforce hooks in a monorepo", they said.&lt;/p&gt;

&lt;p&gt;After googling a bit, though, you find a nice article that explains how to do that (this one right here!). This is how it can be done:&lt;/p&gt;
&lt;h2&gt;
  
  
  Very TL;DR
&lt;/h2&gt;

&lt;p&gt;Husky hook -&amp;gt; Main monorepo script with scope -&amp;gt; Individual package scripts&lt;/p&gt;
&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Set up Husky &amp;amp; hooks in your main monorepo directory&lt;/li&gt;
&lt;li&gt;Your hooks should use your monorepo tool to run a script of a specific name by running it with scope.&lt;/li&gt;
&lt;li&gt;Each package that you'd like to have this hook work on should have a script of that same name defined in its &lt;code&gt;package.json&lt;/code&gt; file.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  What's this about?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks"&gt;Git hooks&lt;/a&gt; are scripts that run on different stages of the git workflow. They can be used for side effect, or to fail a flow.&lt;/p&gt;

&lt;p&gt;For example, the &lt;code&gt;pre-commit&lt;/code&gt; hook runs when you try to commit something. If it fails (exits with code 1), the commit will not be made.&lt;br&gt;
This is a perfect mechanism to enforce linting and tests before a developer's code is added to the repository.&lt;/p&gt;

&lt;p&gt;However, there's one problem with this approach - git hooks are a git mechanism - they are local, user-specific and live in your project's &lt;code&gt;.git&lt;/code&gt; folder. How can you enforce that nice pre-commit script for all users?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.npmjs.com/package/husky"&gt;Husky&lt;/a&gt; is a wonderful package that allows you to define hooks for your package, then install them. But...&lt;/p&gt;
&lt;h2&gt;
  
  
  The Monorepo Problem
&lt;/h2&gt;

&lt;p&gt;In monorepos, your repository is managed outside of your individual packages.&lt;/p&gt;

&lt;p&gt;When you try to use husky with an individual package, it just won't work.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Works okay&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; npx husky add .husky/pre-commit &lt;span class="s2"&gt;"&amp;lt;something to execute&amp;gt;"&lt;/span&gt;

&lt;span class="c"&gt;# Will not add your pre-commit hook to the right directory...&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; npx husky &lt;span class="nb"&gt;install&lt;/span&gt;

&lt;span class="c"&gt;# ...so it'll not run when it's supposed to :(&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Blah"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Solution
&lt;/h2&gt;

&lt;p&gt;I'm using pnpm &amp;amp; pnpm workspaces for my project, so I'll use those for the code examples. They are easily interchangeable with your package manager &amp;amp; monorepo tools of choice.&lt;/p&gt;

&lt;p&gt;Also, I'm demonstrating this with adding a pre-commit hook for linting. The same method should work with any other hook as well.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Install Husky in your main monorepo
&lt;/h3&gt;

&lt;p&gt;Go to your main monorepo directory, and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm i &lt;span class="nt"&gt;-D&lt;/span&gt; husky
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Set a main pre-commit script
&lt;/h3&gt;

&lt;p&gt;In your main monorepo's &lt;code&gt;package.json&lt;/code&gt; file, set a pre-commit script that runs a specific script in all projects.&lt;/p&gt;

&lt;p&gt;Most (or all) monorepo tools have an option to run scripts for all, or some of the packages, by defining a scope.&lt;/p&gt;

&lt;p&gt;In my current project, I make sure to use &lt;a href="https://docs.npmjs.com/cli/v9/using-npm/scope"&gt;npm's scope notation&lt;/a&gt; (not related to the previous scope I mentioned) for all package names (&lt;code&gt;@product/ui&lt;/code&gt;, &lt;code&gt;@product/api&lt;/code&gt; etc.). This allows me to run a script on all packages easily:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; pnpm run &lt;span class="nt"&gt;--filter&lt;/span&gt; &lt;span class="s2"&gt;"@product/*"&lt;/span&gt; myscript
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;main&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;package.json&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;scripts:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"pre-commit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pnpm run --filter &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;@product/*&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; pre-commit"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this opportunity, you could also add a &lt;a href="https://docs.npmjs.com/cli/v9/using-npm/scripts#prepare-and-prepublish"&gt;&lt;code&gt;prepare&lt;/code&gt; script&lt;/a&gt; that runs &lt;code&gt;husky install&lt;/code&gt;, so every user that sets up your project will have those hooks added to their .git directory automatically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;main&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;package.json&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;scripts:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"pre-commit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pnpm run --filter &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;@product/*&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; pre-commit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"prepare"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"husky install"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Add a husky pre-commit hook:
&lt;/h3&gt;

&lt;p&gt;Still in your main directory, create a husky pre-commit hook that runs your new pre-commit script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; npx husky add .husky/pre-commit &lt;span class="s2"&gt;"pnpm pre-commit"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Add pre-commit scripts for individual packages
&lt;/h3&gt;

&lt;p&gt;The last things you'll have to add are the individual pre-commit scripts for each package.&lt;br&gt;
I won't get into the actual linter configuration here, but I recommend using &lt;a href="https://www.npmjs.com/package/lint-staged"&gt;lint-staged&lt;/a&gt; with whatever linters you're already using. Lint-staged will only check staged files, so you'll save time and noise by only getting errors that are relevant to your current commit.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Individual&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;package's&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;package.json&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;scripts:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"pre-commit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lint-staged"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you don't add a pre-commit script to a project, nothing will break. This is good for many cases, but beware not to miss adding a hook script for a new project because of this.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. And that's that!
&lt;/h3&gt;

&lt;p&gt;The next time you'll commit something, all of your package's &lt;code&gt;pre-commit&lt;/code&gt; scripts will run.&lt;/p&gt;

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

&lt;p&gt;Git hooks &amp;amp; Husky are powerful tools that allow you to enforce running local scripts and keeping coding standards. By using this approach, you can enjoy the advantages of hooks and of proper script scoping (each project has its own script inside it) in a monorepo.&lt;/p&gt;

&lt;p&gt;What do you think? Would you approach this differently?&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Thanks to Yonatan Kra for his thoughtful and thorough review.&lt;/em&gt; Check out his &lt;a href="https://yonatankra.com/"&gt;blog&lt;/a&gt; for many good articles of an experienced developer.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The cover picture was made with DALL-E 2. I don't know a lot about fishing, so I'm trusting its judgement about how fishing rods look like.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>dx</category>
      <category>javascript</category>
      <category>husky</category>
    </item>
    <item>
      <title>Pure Reduce Functions - Use Aggregator As A State</title>
      <dc:creator>Miki Stanger</dc:creator>
      <pubDate>Mon, 09 May 2022 13:14:09 +0000</pubDate>
      <link>https://dev.to/mimafogeus2/pure-reduce-functions-use-aggregator-as-a-state-32h6</link>
      <guid>https://dev.to/mimafogeus2/pure-reduce-functions-use-aggregator-as-a-state-32h6</guid>
      <description>&lt;p&gt;when using &lt;code&gt;Array.reduce()&lt;/code&gt; for complex operations, we tend to resort to using side effects (e.g. defining a &lt;code&gt;let&lt;/code&gt; variable or an object out of scope. and modify them while iterating).&lt;/p&gt;

&lt;p&gt;Here's an easy way to keep your reduce functions pure by using the aggregator as a state.&lt;/p&gt;




&lt;p&gt;Consider a reduce function that's meant to sum all numbers, and add the largest number twice.&lt;br&gt;
We can declare a mutable variable before, use it to track the biggest number, then add it after we're done reducing:&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;let&lt;/span&gt; &lt;span class="nx"&gt;biggestNum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NEGATIVE_INFINITY&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;sum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;agg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;biggestNum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;biggestNum&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;agg&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sumAndBiggest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;biggestNum&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alternatively, we can encapsulate all related logic INSIDE the reduce 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;const&lt;/span&gt; &lt;span class="nx"&gt;sumAndBiggest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;agg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sourceArr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;sourceArr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;agg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;agg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;biggestNum&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="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;agg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;biggestNum&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;agg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;biggestNum&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="na"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;biggestNum&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NEGATIVE_INFINITY&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;You'll notice two things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We use the aggregator as a state - we include the actual sum we're reducing, and the biggest number we found so far.&lt;/li&gt;
&lt;li&gt;We use the 3rd and 4th arguments of the reduce function - the current index and the reference to the array we're iterating over - to see if the current iteration is the last. If it is, we add the largest number and return the final sum instead of a new state.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;This way adds a bit more logic to the reduce function. Why should we use it then?&lt;br&gt;
Because it allows us to encapsulate all of the data needed for the reduction operation. This means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You don't get leftover variables after the reduction is done. When someone reads the code or debugs it, they won't have to worry about those variables - &lt;code&gt;biggestNum&lt;/code&gt; in our first example - are being used elsewhere.&lt;/li&gt;
&lt;li&gt;Your reduce function is a pure function. Everything you need to look at happens inside the function and its arguments. This also means easier debugging.&lt;/li&gt;
&lt;li&gt;All of the state's data is garbage-collected when it's no longer used.&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;Unfortunately, TypeScript only offers an option to define one return value for &lt;code&gt;reduce&lt;/code&gt;, and doesn't allow us to differentiate between the final and intermediary return values.&lt;br&gt;
This means we'll have to define the reduce function as being able to return both types, and use the &lt;code&gt;as&lt;/code&gt; keyword to assert our aggregator's type before use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;IReduceState&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;biggestNum&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&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;sumAndBiggest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reduce&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;IReduceState&lt;/span&gt;&lt;span class="o"&gt;&amp;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;agg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sourceArr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;biggestNum&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;agg&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;IReduceState&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;index&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;sourceArr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;biggestNum&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="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;biggestNum&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;biggestNum&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="na"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;biggestNum&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NEGATIVE_INFINITY&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;If you have a nicer solution to this, please let me know!&lt;/p&gt;

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




&lt;p&gt;&lt;em&gt;Thank you, &lt;a href="https://yonatankra.com/"&gt;Yonatan Kra&lt;/a&gt;, for your kind review.&lt;/em&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>A Better Way to Manage Z-Indexes</title>
      <dc:creator>Miki Stanger</dc:creator>
      <pubDate>Sat, 04 Sep 2021 17:31:49 +0000</pubDate>
      <link>https://dev.to/mimafogeus2/a-better-way-to-manage-z-indexes-1nf</link>
      <guid>https://dev.to/mimafogeus2/a-better-way-to-manage-z-indexes-1nf</guid>
      <description>&lt;h4&gt;
  
  
  TL;DR
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Z-Indexes can get messy fast.&lt;/li&gt;
&lt;li&gt;It's common to pick z-indexes by guessing!&lt;/li&gt;
&lt;li&gt;There are some ways to improve this, but they only work to a point.&lt;/li&gt;
&lt;li&gt;We can automate our z-index values generation and solve most of those issues.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Problem with Keeping Track of Z-Indexes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Z-index is a relative CSS property.&lt;/strong&gt; It has no unit of measurement, other than the other z-indexes in the project. Those other values are usually spread all over the project, which leads to interesting phenomenons.&lt;/p&gt;




&lt;h2&gt;
  
  
  How Z-Indexes can get messy?
&lt;/h2&gt;

&lt;p&gt;Let's say you have a modal with the value of 999999.&lt;br&gt;
We add a date picker that should be behind it but above everything else, and write 99999 so you'll have some buffer.&lt;br&gt;
A year later, a colleague has to add an error popup. It should be over everything, including the modal. He sets its z-index to 9999999999. He didn't know (or forgot) about an old ad component a third colleague, long gone, added with a z-index value of 999999999999.&lt;br&gt;
Now, in one of the many pages of your project, an ad will pop up that hides the error.&lt;br&gt;
Another similar bug could make your date picker unusable, and another - hide your modal's "buy" button.&lt;/p&gt;
&lt;h3&gt;
  
  
  Using Orders of 10 to Improve Workflow
&lt;/h3&gt;

&lt;p&gt;One common way to guess less is to work with powers of 10. You add a lot of zeros to something that should be on top, and less zeros to things under it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* This is the most toppest thing ever! */&lt;/span&gt;
&lt;span class="nc"&gt;.modal&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;z-index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10000000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* Oh wait */&lt;/span&gt;
&lt;span class="nc"&gt;.error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;z-index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10000000000&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;Another common method is to use any number of 9s:&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;.modal&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;z-index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;99999999&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;z-index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;9999999999&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;That one is &lt;a href="https://psuter.net/2019/07/07/z-index"&gt;extremely popular&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As your project gets bigger, and more people are working on it, this tends to become a guessing game, where a developer writes a very big number and hopes for the best:&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;.old-thing&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;/* I want my thing to always be on top of other things */&lt;/span&gt;
  &lt;span class="err"&gt;...&lt;/span&gt;
  &lt;span class="c"&gt;/* This should be enough to always be on top. */&lt;/span&gt;
  &lt;span class="nl"&gt;z-index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1000000&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.new-thing&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="c"&gt;/**
  This should hide everything, even old-thing.
  It should also hide things we add in the future. 
 **/&lt;/span&gt;
&lt;span class="err"&gt;...&lt;/span&gt;
&lt;span class="c"&gt;/* I'll write a large number to make that happen */&lt;/span&gt;
&lt;span class="nl"&gt;z-index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10000000000&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/**
  Wow, I've had to guess a lot of zeros here.
  I can't change other z-indexes because I don't know what 
  I'll break, but I have to guarantee it'll always be on top.
 **/&lt;/span&gt;
&lt;span class="nc"&gt;.newer-thing&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;z-index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1000000000&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;"Whoops, new-thing still hides this. I'll fix that:"&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;.newer-thing&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;z-index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1000000000000000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We end up with different magnitudes of numbers that don't make sense except them being guesswork.&lt;br&gt;
It's also harder to understand the actual order of things as you use bigger and bigger number. When comparing &lt;code&gt;1000000000000000&lt;/code&gt; and &lt;code&gt;10000000000000000&lt;/code&gt;, you'll have to count zeros to understand who's hiding who.&lt;/p&gt;
&lt;h3&gt;
  
  
  Using multiples of 10, 100 or 1,000
&lt;/h3&gt;

&lt;p&gt;Another common workflow. It looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.menu&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;z-index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.sales-notice&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;z-index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;200&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;z-index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;300&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is an improvement over the orders of magnitude method; It's easier to tell which component will be on top of which, as we now have a single order of magnitudes and a uniform difference between the different z-indexes.&lt;br&gt;
However, it still suffers from some of the problems of the previous method; When you have to add a new layer between two existing one, you'd use the gap between the two and pick their average:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* This hides the menu and hides behind sales-notice */&lt;/span&gt;
&lt;span class="nc"&gt;.menu&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;z-index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.tooltip&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;z-index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;150&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* Hmm */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.sales-notice&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;z-index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;200&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;z-index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;300&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And just like that we lose the uniform gap advantage. When digging into the code (or a new section of it) for the first time, we can't intuitively know if the smaller difference between the menu and the tooltip means something.&lt;br&gt;
Also, as the project grows, developers will still start to guess numbers. They won't be gargantuan guesses, but it'll make the meaning of each number hard to understand.&lt;/p&gt;
&lt;h3&gt;
  
  
  Using an Object/Map/List to Manage Your Indexes.
&lt;/h3&gt;

&lt;p&gt;Here you concentrate all of your z-indexes in the same place:&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;const&lt;/span&gt; &lt;span class="nx"&gt;zIndexes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;menu&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Use those value as CSS variables. We'll get to this soon.&lt;/span&gt;
&lt;span class="nx"&gt;injectZIndexes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.menu&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;z-index&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;--z-index-menu&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;z-index&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;--z-index-error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a major improvement over the previous methods; Because you manage all of your z-index values in a single place, it's easy to see their order. Just as importantly, you can understand their purpose, now that they're named.&lt;br&gt;
However, we still pick middle numbers when adding middle layers, so you can't easily tell the meaning of the numeric differences once your project grows:&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;const&lt;/span&gt; &lt;span class="nx"&gt;zIndexes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;menu&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;tooltip&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;125&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;modal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;loadingScreen&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can keep a constant, meaningful difference, but it'll require you to rewrite all of the values greater than the new addition.&lt;/p&gt;

&lt;h3&gt;
  
  
  To Summerize:
&lt;/h3&gt;

&lt;p&gt;All of the methods above requires you pick numbers in a meaningless way, many times with guesses. They all become messy as your codebase grows.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Better Way
&lt;/h2&gt;

&lt;p&gt;Optimally, we'll have an object with constant differences between each layer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;z-index-menu&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;z-index-modal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;z-index-error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;300&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 common thing to all of those methods, and source to most of those problems, and the reason we can't have nice things, is that &lt;strong&gt;you have to manage the values yourself&lt;/strong&gt;.&lt;br&gt;
Since you don't really care about the exact numbers, only about their relative values, we can do much better - &lt;strong&gt;we can let a tiny bit of code to take care of that for us&lt;/strong&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;makeZIndexes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;layers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="nx"&gt;layers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;agg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;layerName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;valueName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`z-index-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;layerName&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="nx"&gt;agg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;valueName&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&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;agg&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{});&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When we use it, we get an object with all the variables you need for z-indexes, &lt;strong&gt;nicely named and automatically numbered&lt;/strong&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;zIndexes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;makeZIndexes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;menu&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;modal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Which will give us:&lt;/span&gt;
&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;z-index-menu&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;z-index-modal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;z-index-error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;300&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 add another layer, just add its name in the array:&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;const&lt;/span&gt; &lt;span class="nx"&gt;zIndexes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;makeZIndexes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;menu&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;clippy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;modal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// The result:&lt;/span&gt;
&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;z-index-menu&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;z-index-clippy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;z-index-modal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;z-index-error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;400&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;h3&gt;
  
  
  What Did We Do Here?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;We created a &lt;strong&gt;small array of names.&lt;/strong&gt; By creating named variables, we can now know where each of them is used.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The numbers are automatically generated!&lt;/strong&gt; We don't have to guess and hope anymore. We outsourced this concern to a few lines of code.&lt;/li&gt;
&lt;li&gt;The names array is the only code we have to change to manage our z-Indexes. &lt;strong&gt;This is a very simple interface&lt;/strong&gt;, that is &lt;strong&gt;managed from a single place&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The top layers' values have changed!&lt;/strong&gt; Since the z-index number only matters because of its relation to other z-indexes, and since all of those indexes are managed here in a way that keeps their order, &lt;strong&gt;we don't care!&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Equal difference between every two adjacent layers. This is easier to read, as you don't have to figure out the reason for different differences.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How To Use Those Z-Indexes?
&lt;/h3&gt;

&lt;p&gt;That depends on your style framework. This method is easy to implement in any method, CSS preprocessor or framework you'd like:&lt;br&gt;
Vanilla (JS-generated CSS variables) (&lt;a href="https://codesandbox.io/s/a-better-way-to-manage-z-indices-js-implementation-szr7q?file=/src/styles.css"&gt;example&lt;/a&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Z_INDEX_LAYERS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;menu&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;clippy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;modal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&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;zIndexes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;makeZIndexes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Z_INDEX_LAYERS&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Format as CSS variables and inject to a top HTML element&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;styleString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;zIndexes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(([&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;`--&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&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="nx"&gt;value&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="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.app&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;style&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;styleString&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.menu&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;...&lt;/span&gt;
  &lt;span class="nl"&gt;z-index&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;--z-index-menu&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;SASS (see it working &lt;a href="https://codesandbox.io/s/a-better-way-to-manage-z-indices-sass-implementation-ii80g?file=/src/styles.scss"&gt;here&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sass"&gt;&lt;code&gt;&lt;span class="nv"&gt;$z-layers-names&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"menu"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"sales-notice"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$z-layers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="err"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="err"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;@each&lt;/span&gt; &lt;span class="nv"&gt;$layer-name&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;$z-layers-names&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="err"&gt;;&lt;/span&gt;
  &lt;span class="nv"&gt;$css-var-name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"z-index-"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nv"&gt;$layer-name&lt;/span&gt;&lt;span class="err"&gt;;&lt;/span&gt;

  &lt;span class="nv"&gt;$z-layers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;map-merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;z-layers&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="err"&gt;($&lt;/span&gt;&lt;span class="na"&gt;css-var-name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.menu&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;...&lt;/span&gt;
  &lt;span class="nl"&gt;z-index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;map-get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$z-layers&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"menu"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The vanilla solution, by the way, is universal. Your CSS preprocessor, or CSS-in-JS framework, should support this &lt;a href="https://caniuse.com/css-variables"&gt;widespread&lt;/a&gt; feature.&lt;br&gt;
Simply run the JS part, inject to a DOM element and use CSS variables wherever you'd like.&lt;/p&gt;
&lt;h3&gt;
  
  
  Related Z-Index Grouping (and Why We Still Use Multiples of 100)
&lt;/h3&gt;

&lt;p&gt;We can use sequential numbers, but using multiples of a larger number gives us a convenient way to manage related z-indexes.&lt;br&gt;
For example, a modal close button is related to the modal, and will always move with it - even when changing the order of layers. We can easily express this in CSS:&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;.modal-close-button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;...&lt;/span&gt;
  &lt;span class="nl"&gt;z-index&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="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--z-index-error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;I implemented this as a universally usable npm library, by implementing it as an &lt;a href="https://www.npmjs.com/package/inventar"&gt;Inventar plugin&lt;/a&gt;.&lt;br&gt;
&lt;a href="https://www.npmjs.com/package/inventar"&gt;Inventar&lt;/a&gt; is a tiny, powerful, framework-agnostic theme &amp;amp; style logic manager. Among other things, it can convert your configuration to a function that injects CSS variables into an element's style.&lt;/p&gt;

&lt;p&gt;This is how it looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;makeInventar&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;inventar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;zIndex&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;inventar-z-index&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;Z_INDEX_LAYERS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;menu&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;clippy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;modal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;inject&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;makeInventar&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="na"&gt;zIndex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;transformers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;zIndex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Z_INDEX_LAYERS&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;Questions? Complements? Complaints? Chocolate surplus? Let's talk about it in the comments or in my &lt;a href="https://www.linkedin.com/in/miki-stanger-153bb365/"&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thanks to &lt;a href="https://yonatankra.com/"&gt;Yonatan Kra&lt;/a&gt; for his helpful, thorough review.&lt;/p&gt;

</description>
      <category>css</category>
      <category>html</category>
      <category>webdev</category>
    </item>
    <item>
      <title>CV - Tips, Tricks, Template</title>
      <dc:creator>Miki Stanger</dc:creator>
      <pubDate>Thu, 17 Dec 2020 17:27:34 +0000</pubDate>
      <link>https://dev.to/mimafogeus2/cv-tips-tricks-template-520l</link>
      <guid>https://dev.to/mimafogeus2/cv-tips-tricks-template-520l</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a translation and a minor update of a note I wrote in Facebook a while ago.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Disclaimer&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I have no professional experience in resume writing or any other HR subjects. I write with a combination of a "gut feeling" and the (generally positive) feedback that I've got from headhunters, recruiters and friends who read my CV and/or used its template with good results.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Another Disclaimer&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I am an Israeli developer, and I write my resume to target programming jobs in the industry and culture that I know. In other professions, industries and countries, people might look on CVs in different ways and for other things.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you encounter any errors or have different opinions, I'll be happy to hear about them in the comments :)&lt;/p&gt;

&lt;h1&gt;
  
  
  TL;DR
&lt;/h1&gt;

&lt;p&gt;My template gets constant positive feedback from recruiters and friends. One friend claims that twice as many people are calling her back since switching to this format.&lt;/p&gt;

&lt;p&gt;This is the current version of my template: &lt;a href="https://tinyurl.com/2ddur9tw"&gt;https://tinyurl.com/2ddur9tw&lt;/a&gt;&lt;br&gt;
This is a previous version which might be better for shorter resumes.: &lt;a href="https://goo.gl/AlkUie"&gt;https://goo.gl/AlkUie&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To use, open your preferred link, go to &lt;code&gt;file -&amp;gt; make a copy...&lt;/code&gt; and you'll get your own editable copy.&lt;/p&gt;

&lt;h1&gt;
  
  
  How to Use This Template (and general CV tips and tricks)
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Layout &amp;amp; Design
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Main Header
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The main header is you.&lt;/strong&gt; Your name, to be exact :)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A secondary title is not a must, but it might add some flavor or provide a place to talk about what you're looking for.&lt;/strong&gt; I've used "Front-End Developer and UX enthusiast who you’d totally like to hire".
The reader knows that I'm looking mainly for a front-end position, and they might remember me better for the unusual "who you'd totally like to hire" ending (for which I've got good feedback).&lt;/li&gt;
&lt;li&gt;Contact details - &lt;strong&gt;Name, email, phone number&lt;/strong&gt;. You can also add icons with the relevant username (if different from your full name in the title) to LinkedIn, GitHub etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Primary Column, Secondary Column
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The primary column includes the arguably most important part of your resume - &lt;strong&gt;your professional experience&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Education&lt;/strong&gt; could be moved to the primary column if you don't have a lot of professional experience and/or if it's important in your field.&lt;/li&gt;
&lt;li&gt;If you can fit all of your info in the primary column, there's no reason for you to use the secondary one. You can just delete it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You can change the secondary column's width.&lt;/strong&gt; I made it thinner over time, so the primary column would fit in a single page, but the width is flexible and should be fine as long as it's clear that it's the secondary column - that is, it's &lt;strong&gt;not nearly as wide as the primary one.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Design (for non-designers)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Work hard on fitting everything in a single page&lt;/strong&gt;. If you have a lot to write, consider using more concise phrasing, format the resume differently etc. However, make sure you're &lt;strong&gt;not omitting important stuff&lt;/strong&gt;, and that you're &lt;strong&gt;not making the font size too small&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;If you have a large block of text, consider splitting it to bullet points instead. This might make your description more readable and help you to save some real estate.&lt;/li&gt;
&lt;li&gt;Make sure that &lt;strong&gt;texts with different roles look different&lt;/strong&gt;. Titles, subtitles, emphasized terms etc. should look differently. In a nutshell and without learning design - try different font weights (bold vs. regular), underscores and color.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Don't change the role of font weights, colors etc.&lt;/strong&gt; For example, if you used bold letters to emphasize, don't use it for secondary lines as well; emphasizes and secondary lines should be discernible by their design.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IF&lt;/strong&gt; you choose to add a color - add &lt;strong&gt;a single, solid and readable one!&lt;/strong&gt; If you have to make an effort to read it (e.g. you colored some of your text yellow), then the recruiter will also have to do that, and they might just put your CV aside and move to others. If you need to add &lt;strong&gt;another&lt;/strong&gt; color, use a brighter or darker version of the first color, and &lt;strong&gt;that's it&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;** Use a solid and readable font.** Your CV is not the place for "crazy" fonts, handwritten fonts etc. Again - the more effort the recruiter will have to make reading your CV, the lower the chances they'll actually get through it. Also, &lt;strong&gt;use a single font for all of your CV&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How to fit my CV in a single page?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Make the font smaller&lt;/strong&gt;. Not too small, though - it should still be easily readable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Write time ranges differently&lt;/strong&gt; - In the newer version of my CV, I moved the time ranges so the years would be below each other. This made the dates thinner and allowed more space for the primary column.&lt;/li&gt;
&lt;li&gt;If you're using columns, &lt;strong&gt;try to expand the column that takes the most space&lt;/strong&gt;. You'll have to make sure no column gets too thin, as you don't want to have a line break every other word.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use concise language&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Writing Style
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Be focused and concise, don't write irrelevant text&lt;/strong&gt;. "Managed projects" is more readable and takes less space than "In my first six months at the company, I have managed two projects."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Focused and concise doesn't mean you shouldn't expand on responsibilities, technologies and the nature of what you did&lt;/strong&gt;. That's important. In long sentences, ask yourself - "can I remove this word and still send the same message?"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Be honest - don't lie, don't invent false professional/education history.&lt;/strong&gt; You'll probably be interviewing with people who are experienced interviewers, and who'll know to find out lies and "dirty tricks" in your resume.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Good grammar creates a good impression&lt;/strong&gt;. Know your commas and stops and make sure you don't have any typos.&lt;/li&gt;
&lt;li&gt;In many cases, &lt;strong&gt;humor can help you&lt;/strong&gt;. Don't make your CV a standup session, but inserting a joke or two - especially if they're relevant to your profession - might make the reader smile and give you an edge against the other CVs. In my case, writing that I "know how to quit VIM" actually got me a job interview :)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Sections
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Professional Experience
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Job history order&lt;/strong&gt; - start with your most recent job and go back in time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Write years, not months&lt;/strong&gt; - 2013-2015 looks better than Aug. 2013 -&amp;gt; Feb. 2015, and it takes less document real estate. Also, you'd usually talk about years of experience, not months.&lt;/li&gt;
&lt;li&gt;A single job title should include &lt;strong&gt;the position and the company name&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Keep a consistent writing style.&lt;/strong&gt; Whatever style you pick, stick with it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Some such styles:&lt;/li&gt;
&lt;li&gt;Was responsible for project management&lt;/li&gt;
&lt;li&gt;Managed project&lt;/li&gt;
&lt;li&gt;Did project management&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;&lt;p&gt;All of the styles I mentioned, and much more, are legitimate, but I'm a big fan of being &lt;strong&gt;short and to the point&lt;/strong&gt;, with emphasis on the &lt;strong&gt;job's nature and your achievements&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Emphasize important words and terms&lt;/strong&gt; (like I'm doing in this article). These include &lt;strong&gt;relevant technologies &amp;amp; buzzwords&lt;/strong&gt;, &lt;strong&gt;significant projects&lt;/strong&gt; and &lt;strong&gt;achievements that stand out&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You can &lt;strong&gt;shorten descriptions of older/less relevant jobs&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Education
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Sections order - &lt;strong&gt;start with your most recent degree and go back in time&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;The title should include &lt;strong&gt;the degree title and the institution where you studied.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;You can add info about &lt;strong&gt;relevant projects, research and graduation with honors&lt;/strong&gt;. I assume that if you've got relevant academic education, you can skip adding additional info about your high school extras and earlier degrees.&lt;/li&gt;
&lt;li&gt;You can skip some of the details in degrees that are not relevant for the position you're applying to.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Emphasize important words and terms.&lt;/strong&gt; These include elective studies (in highschool), graduation with honors, titles of interesting and relevant &lt;em&gt;projects&lt;/em&gt; you did during your studies etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Military Service (if applicable)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;In some countries that have compulsory military service, it's worth mentioning in your resume. Add your service under the professions part if it's relevant, and in its own section otherwise.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Skills
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Relevant &lt;strong&gt;technologies, software etc.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;If you've looked into, or played with, other technologies that you didn't get to use enough professionally, you can add them in a separate section. I've added them under "recent interests".&lt;/li&gt;
&lt;li&gt;Write essential technologies and software first, then add those that are not directly relevant but could help in different sections. For example, a web developer should write about HTML, CSS, JS frameworks etc. first. In a different section, they'll mention their Photoshop experience, which is not directly related but could improve processes in some cases.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Portfolio/Examples (only available in the newer template)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Add links to a &lt;strong&gt;personal website&lt;/strong&gt;, &lt;strong&gt;portfolios&lt;/strong&gt;, &lt;strong&gt;projects you were involved in&lt;/strong&gt;, &lt;strong&gt;GitHub user&lt;/strong&gt; and anything else that could show what you can do.&lt;/li&gt;
&lt;li&gt;If you have a long link, and it's not important to show the domain (e.g. a portfolio in a hosting website), &lt;strong&gt;consider using a URI shortener&lt;/strong&gt; (e.g. tinyurl.com). This will make it easier to type, and take up less space in your CV.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Languages
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;If a language is your &lt;strong&gt;mother tongue&lt;/strong&gt;, write that.&lt;/li&gt;
&lt;li&gt;If it's not your mother tongue, write your &lt;strong&gt;reading, writing and spoken levels&lt;/strong&gt;. If they're all the same, just write the proficiency once ("French - fluent").&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Before Sending Your New CV
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;I highly recommend letting &lt;strong&gt;friends and/or people from your industry read your CV&lt;/strong&gt; and ask for their feedback - both for their contents and for proofreading.&lt;/li&gt;
&lt;li&gt;Both PDF and Word files are acceptable, but unless specifically asked to provide a specific format, &lt;strong&gt;PDF is the way to go&lt;/strong&gt;; More people can open it, and it guarantees consistent formatting and layout.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's it. Good luck!&lt;/p&gt;

</description>
      <category>resume</category>
      <category>programming</category>
      <category>watercooler</category>
      <category>productivity</category>
    </item>
    <item>
      <title>The Time I Wrote a White Noise Generator To Improve Loading Times</title>
      <dc:creator>Miki Stanger</dc:creator>
      <pubDate>Sat, 28 Nov 2020 13:10:00 +0000</pubDate>
      <link>https://dev.to/mimafogeus2/the-time-i-wrote-a-white-noise-generator-to-improve-loading-times-j0k</link>
      <guid>https://dev.to/mimafogeus2/the-time-i-wrote-a-white-noise-generator-to-improve-loading-times-j0k</guid>
      <description>&lt;p&gt;The year was 2011. I was a young, &lt;del&gt;aspiring&lt;/del&gt; software developer working in my first position in the industry.&lt;br&gt;
In our app we had a web view, which we could use to display dynamic content. As mobile connections were at 2011 speeds, we had a hard limit on the size of each web view page - including all assets and code. That was usually not a problem, until one day I had to implement a design where the background was a just-slightly-grainy grey.&lt;/p&gt;

&lt;p&gt;Grainy = white noise.&lt;/p&gt;

&lt;p&gt;That was a problem. To understand why, let's talk about image compression.&lt;/p&gt;

&lt;h2&gt;
  
  
  Image Compression in a Nutshell
&lt;/h2&gt;

&lt;p&gt;The images you see on the web are usually compressed; they can use many different methods in order to save space, but all of them can be categorised into two groups:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lose some of the data (This is called &lt;strong&gt;lossy compression&lt;/strong&gt;, and includes &lt;a href="https://en.wikipedia.org/wiki/JPEG#JPEG_compression"&gt;JPEG&lt;/a&gt; and &lt;a href="https://en.wikipedia.org/wiki/GIF#Compression_example"&gt;GIF&lt;/a&gt;, among others)&lt;/li&gt;
&lt;li&gt;Represent the data in a way that takes less space (this is called &lt;em&gt;lossless&lt;/em&gt; compression, and you might best know it through the &lt;a href="https://en.wikipedia.org/wiki/Portable_Network_Graphics#Compression"&gt;PNG&lt;/a&gt; format)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Lossy compression of images is commonly visible. &lt;a href="https://commons.wikimedia.org/wiki/File:Asterisk_with_jpg-artefacts.png"&gt;JPEG artifacts&lt;/a&gt; are a known side effect of JPEG compression, and in GIFs you could recognise "rounding up" of colors, as it's limited to 256 colors. these methods could work fine for many things (e.g. photography in JPEG), but might be too much where small, clean lines/details are needed. Our white-noise-textured background fell in the second category.&lt;/p&gt;

&lt;p&gt;If not JPEG or GIF, I tried to save the background as PNG. The file was BIG.&lt;/p&gt;

&lt;h2&gt;
  
  
  You Can't (Losslessly) Compress White Noise
&lt;/h2&gt;

&lt;p&gt;The main method I know for lossless compression involves finding repeating patterns, replacing them with a shorter representation and keeping the replacement in a dictionary. For example, in the string &lt;code&gt;aaabaaacaaad&lt;/code&gt;, we have the substring &lt;code&gt;aaa&lt;/code&gt; appearing a few times.&lt;br&gt;
We can shorten it by replacing it with a single character; let's call it 1. We now have a compressed string - &lt;code&gt;1b1c1d&lt;/code&gt;, and a dictionary - &lt;code&gt;1:aaa&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;When displaying the string, we use the dictionary to replace the shorter substring back to the original one, and get &lt;code&gt;aaabaaacaaad&lt;/code&gt; again.&lt;/p&gt;

&lt;p&gt;Different formats differ in the way they find what to replace, how many times they return the process, and what of many other optimisations they use. However, these basics are commonly used.&lt;/p&gt;

&lt;p&gt;Back to our noisy image - white noise is, by definition, random; it is a noise generated by receiving different values in a range with equal probability. Being random, the chance for repeating patterns is very low, and lossless compression in the way we described is not very effective.&lt;/p&gt;

&lt;h2&gt;
  
  
  What If We Won't Load This At All?
&lt;/h2&gt;

&lt;p&gt;We'll still have noise, don't worry, but we'll have to deal with it differently:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Load the page, with a background color or image, but without the noise.&lt;/li&gt;
&lt;li&gt;Use a canvas to generate white noise&lt;/li&gt;
&lt;li&gt;Give the canvas a very low opacity&lt;/li&gt;
&lt;li&gt;Position it above the background&lt;/li&gt;
&lt;li&gt;Mission accomplished!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;White noise, being simply a uniform random grey value on each pixel, is very easy to generate. You iterate over the canvas' width and height, and color each pixel in a random shade of grey.&lt;/p&gt;

&lt;p&gt;It worked! The total page size was now small enough to fit our hard size limit. However, my state of the art iPhone 4, and all other mobile devices we've tested, took a noticeable time to run this code. This wasn't a good user experience, so we've had to find another solution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tiles
&lt;/h2&gt;

&lt;p&gt;The next plan was to use the existing generator to generate a much smaller area of white noise, and use it as a tile, a repeating background. As it has a low opacity and was used as a texture, people wouldn't easily see it repeats, and we'd have a nice, elegant background texture that is quick to generate.&lt;br&gt;
This also meant that I couldn't simply use the canvas above the background, as I'd then have to fill it. Instead, I used an invisible canvas to generate the white noise tile, then used that tile, in the format of a &lt;a href="https://css-tricks.com/data-uris/"&gt;data URI&lt;/a&gt;, as a background to a different element.&lt;br&gt;
Thankfully, canvases have long provided this functionality &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL"&gt;natively&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is the process we ended up with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Load the page, with a background color or image, but without the noise.&lt;/li&gt;
&lt;li&gt;Use an invisible canvas to generate a low-opacity white noise tile.&lt;/li&gt;
&lt;li&gt;Get the canvas image as a &lt;a href="https://css-tricks.com/data-uris/"&gt;data URI&lt;/a&gt;, and use that as a &lt;a href="https://caniuse.com/?search=multiple%20backgrounds"&gt;second &lt;code&gt;background-image&lt;/code&gt;&lt;/a&gt; for the container element (together with the untextured image), or as a background-image over a color background.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since the texture was very subtle, we could get away with texture repetition without it being noticed - as long as the tile wasn't very small. A few attempts to balance tile size (repetition vs. loading speed) and how visible the repetition was, and we've had a working feature :)&lt;/p&gt;

&lt;h2&gt;
  
  
  What Should We Take From This?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;It's good to know how things work, even if you only work with them indirectly. By knowing some basics about image compression and white noise, for example, I could find the source of a non-trivial front-end problem, and solve it.&lt;/li&gt;
&lt;li&gt;Beware of focusing too much on the things you directly need for work. Learning a bit about things outside of your daily line of work, in addition to being interesting, could help you out later.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Having Moment.js Replacements Is Not Enough</title>
      <dc:creator>Miki Stanger</dc:creator>
      <pubDate>Mon, 09 Nov 2020 06:50:09 +0000</pubDate>
      <link>https://dev.to/mimafogeus2/having-moment-js-replacements-is-not-enough-1cp4</link>
      <guid>https://dev.to/mimafogeus2/having-moment-js-replacements-is-not-enough-1cp4</guid>
      <description>&lt;h1&gt;
  
  
  Having Moment.js Replacements Is Not Enough
&lt;/h1&gt;

&lt;p&gt;A few months ago, moment.js announced the project is now in &lt;a href="https://momentjs.com/docs/#/-project-status/"&gt;maintenance mode&lt;/a&gt;. They discourage you from using it in new projects, offer alternatives (including their own &lt;a href="https://moment.github.io/luxon/"&gt;Luxon&lt;/a&gt;) and tell of a bright future with &lt;a href="https://github.com/tc39/proposal-temporal"&gt;Temporal&lt;/a&gt; (a stage 2 proposal at the time of writing).&lt;br&gt;
This is good; Moment.js has served us well, but it currently a huge library that doesn’t support tree-shaking, and isn't as performant as other libraries.&lt;br&gt;
It is, however, still a dependency of many, many libraries, and it’ll take a long time for this to change (if at all - not all libraries have active development going on).&lt;br&gt;
As long as you’re installing one of these packages, you’re going to have a &lt;br&gt;
&lt;a href="https://inventi.studio/en/blog/why-you-shouldnt-use-moment-js"&gt;non-tree-shakable, 232kb (68kb if your dependency developer knew to ignore locales) hunk of code&lt;/a&gt;, in addition to whatever time-and-date library you’d like to be using.&lt;br&gt;
In my case, I usually end up using moment.js in my larger projects, as I see no reason to include two libraries to work with dates, especially when one is this huge.&lt;/p&gt;

&lt;h2&gt;
  
  
  What can we do?
&lt;/h2&gt;

&lt;p&gt;We CAN wait a few years, but most companies won’t appreciate this kind of timeline. Another solution is to &lt;strong&gt;provide moment API wrappers for the new generation of time and date libraries.&lt;/strong&gt; I’ll explain:&lt;/p&gt;

&lt;p&gt;Let’s say I’m publishing my own cool new time and date library. Let’s call it Chair.js, as coming with good names for libraries is hard (I spent more time finding a name for &lt;a href="https://www.npmjs.com/package/inventar"&gt;Inventar&lt;/a&gt; than coding its first release), and I looked at a chair just now.&lt;/p&gt;

&lt;p&gt;Chair is light, fast and simple to work with. It sits well in its role (eh?). Its API is modern and immutable, which means that it’s different than moment.js.&lt;br&gt;
I also know that developers wouldn’t use Chair if they have moment.js installed as a dependency of a dependency already, which they probably do. So I’ll make sure that these dependencies of dependencies can easily use Chair instead, by providing an additional package - chair-moment-wrapper.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;chair-moment-wrapper:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Has a similar interface to that of moment.js, or at least its most used features. This interface calls Chair behind the scenes.&lt;/li&gt;
&lt;li&gt;Has Chair as a &lt;a href="https://nodejs.org/es/blog/npm/peer-dependencies/"&gt;peer dependency&lt;/a&gt;, so no additional copy of it is installed.&lt;/li&gt;
&lt;li&gt;Is optimally tree-shakable.&lt;/li&gt;
&lt;li&gt;Isn’t necessarily optimized for performance. It can get away with being at moment.js’ ballpark.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As a module, it should add itself as moment, or the user should install it with an alias (&lt;code&gt;npm install —save moment@npm:chair-moment-wrapper&lt;/code&gt; or &lt;code&gt;yarn add moment@npm:chair-moment-wrapper&lt;/code&gt;).&lt;br&gt;
It should also replace the moment global object, if it’s added.&lt;/p&gt;

&lt;p&gt;With this kind of wrappers for major time and date libraries, most developers could pick one knowing they wholly and completely replace moment.js - even for their dependencies. They could reap most of Chair’s size and performance benefits and, most importantly, could more quickly get to the day where they won’t have to use it.&lt;/p&gt;

&lt;p&gt;So, developers of &lt;a href="https://moment.github.io/luxon/"&gt;Luxon&lt;/a&gt;, &lt;a href="https://date-fns.org/"&gt;date-fns&lt;/a&gt;, &lt;a href="https://js-joda.github.io/js-joda/"&gt;js-joda&lt;/a&gt;, &lt;a href="https://day.js.org/"&gt;Day.js&lt;/a&gt; - What do you think?&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Inventar - A Framework to Keep Your Styles Tidy</title>
      <dc:creator>Miki Stanger</dc:creator>
      <pubDate>Wed, 02 Sep 2020 20:24:29 +0000</pubDate>
      <link>https://dev.to/mimafogeus2/inventar-a-framework-to-keep-your-styles-tidy-258k</link>
      <guid>https://dev.to/mimafogeus2/inventar-a-framework-to-keep-your-styles-tidy-258k</guid>
      <description>&lt;h4&gt;
  
  
  Keeping your styles tidy is hard.
&lt;/h4&gt;

&lt;p&gt;You end up with duplicates (or worse, almost similar values) of colors and sizes, you have to guess your z-index and hope for the best, you have to duplicate your values if you need them anywhere else, and you're not sure if the same values have the same roles and now any change you have to do there is a terrible experience...&lt;/p&gt;

&lt;p&gt;The solutions tend to either be forgotten, to be coupled with a very opinionated styling framework (which means a major refactoring and a lot of settling over other things), or not enforceable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CSS variables&lt;/strong&gt; are beginning to be seen in the wild, and can solve some of these problems. On their own, however, they might just become a part of the mess.&lt;/p&gt;

&lt;h4&gt;
  
  
  That's where &lt;strong&gt;Inventar&lt;/strong&gt; comes in.
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.npmjs.com/package/inventar"&gt;Inventar&lt;/a&gt;&lt;/strong&gt; is a tool and a system to manage all of your design variables from a single place - no matter if you're using vanilla CSS, SASS, Styled Components, Tailwind or any other styling language/system(s).&lt;/p&gt;

&lt;p&gt;As long as you can use TypeScript, JavaScript or CSS variables, Inventar could work for you.&lt;/p&gt;

&lt;p&gt;Let's look at a basic example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;makeInventar&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;inventar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;jsInventar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cssInventar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;inject&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;makeInventar&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;warmGray&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#e0ded8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#2233aa&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;makeInventar&lt;/code&gt; takes your configuration and returns three variables: &lt;code&gt;jsInventar&lt;/code&gt;, &lt;code&gt;cssInventar&lt;/code&gt; and &lt;code&gt;inject&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;jsInventar&lt;/code&gt; returns the configuration in JS formatting conventions. For this configuration, it'll look the same as the input:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;warmGray&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#e0ded8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#2233aa&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;cssInventar&lt;/code&gt; returns the same configuration, but with CSS variables formatting conventions:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;--warm-gray&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#e0ded8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;--blue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#2233aa&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;inject&lt;/code&gt; accepts a DOM element and adds the css variables into its style attribute. You can provide your own custom injection logic as an option, and Inventar comes with an &lt;code&gt;injectRoot&lt;/code&gt; function that you can import and provide there.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;wrapperElement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementsByClassName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;wrapper&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="nx"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wrapperElement&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means &lt;strong&gt;you can use the same values everywhere&lt;/strong&gt; - CSS will read the injected variables from a wrapping object, and JS styling solution (or dynamic logic based on styles) could use the JS Inventar object as a source.&lt;/p&gt;

&lt;h4&gt;
  
  
  Derivatives allow you to understand what's going on better
&lt;/h4&gt;

&lt;p&gt;After defining two basic colors, I'd like to use them. I could just use them in CSS as they are, but this could make it harder to understand why a variable is used in every place, and could make refactoring hard.&lt;/p&gt;

&lt;p&gt;Instead, Inventar allows you to define variables as functions of other variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;makeInventar&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;inventar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;zIndex&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;inventar-z-index&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;tinycolor&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;tinycolor2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;jsInventar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cssInventar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;inject&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;makeInventar&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;warmGray&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#e0ded8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#2233aa&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

  &lt;span class="na"&gt;backgroundColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;warmGray&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;linkColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;linkHoverColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;tinyColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;linkColor&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;brighten&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;backgroundColor&lt;/code&gt;, &lt;code&gt;linkColor&lt;/code&gt; and &lt;code&gt;linkHoverColor&lt;/code&gt; have functions as their values. These are &lt;strong&gt;derivatives&lt;/strong&gt; - they are used to derive a value from other values.&lt;/p&gt;

&lt;p&gt;In this case, the configuration separates from the color definitions (hex value) to their roles (background, link), which makes things more understandable and much easier to change and refactor.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;linkHoverColor&lt;/code&gt; has additional logic, to generate a brighter version of &lt;code&gt;linkColor&lt;/code&gt;. You can use any calculations as long as a string or a number is returned.&lt;/p&gt;

&lt;p&gt;This is what we're getting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;warmGray&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#e0ded8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#2233aa&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;backgroundColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#e0ded8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;linkColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#2233aa&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;linkHoverColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#3b4cc3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Transformers can generate, alter and remove variables
&lt;/h4&gt;

&lt;p&gt;With some basics done, we'd like to use Inventar to store our z-index value from all around the object.&lt;/p&gt;

&lt;p&gt;We CAN just add them...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;zIndexHeader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;zIndexModal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;zIndexErrorMessage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...and having them all in one place is better than the &lt;a href="https://psuter.net/2019/07/07/z-index"&gt;usual chaos&lt;/a&gt; of z-index values, but you might still find yourself changing other numbers in order to add a new layer between two existing ones. A transformer can make this a completely painless process.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Transformers&lt;/strong&gt; are Inventar's plugins. They can alter a variable's name and value, return one or more other variables, and even remove it if needed. In the example above, we provide a single transformer to a single variable. You can provide multiple transformers as well, or apply them &lt;a href="https://www.npmjs.com/package/inventar#global-transformers"&gt;globally&lt;/a&gt;. In this example, we'll use &lt;a href="https://www.npmjs.com/package/inventar-z-index"&gt;inventar-z-index&lt;/a&gt;, which takes in an array of layer names, and uses it, and the initial value and variable name, to generate the variables you need instead.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;makeInventar&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;inventar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;zIndex&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;inventar-z-index&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;tinycolor&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;tinycolor2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;jsInventar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cssInventar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;inject&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;makeInventar&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;warmGray&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#e0ded8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#2233aa&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

  &lt;span class="na"&gt;backgroundColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;warmGray&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;linkColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;linkHoverColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;tinyColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;linkColor&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;brighten&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="nx"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;

  &lt;span class="na"&gt;zIndex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;transformers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;zIndex&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;header&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;modal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;errorMessage&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll end up with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;warmGray&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#e0ded8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#2233aa&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;backgroundColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#e0ded8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;linkColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#2233aa&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;zIndexHeader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;zIndexModal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;zIndexErrorMessage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are many potential uses for transformers - removing values in certain cases, renaming them according to values or conventions, and linting are just a few.&lt;/p&gt;

&lt;p&gt;And there you have it - a single place to manage your style variables in a clear, tidy way.&lt;/p&gt;




&lt;p&gt;I believe Inventar has many advantages over the existing solutions:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your values are now separated from your styling solution&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;...and that's a good thing!&lt;/p&gt;

&lt;p&gt;Having a single place where all of your colors, sizes, asset paths and other style variables allow you to use them &lt;strong&gt;anywhere&lt;/strong&gt;. Vanilla CSS, SASS, JSS, Styled Components, Tailwind CSS, plain inline style (please don't)... As long as you can use CSS variables there, you can use it with Inventar. You can also use them in whatever JS logic you need.&lt;/p&gt;

&lt;p&gt;This could also make future refactoring or styling language changes so much easier.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your values are now in a single place&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With a single source of truth, it is much easier now to enforce styles ("Hey, I see you used a color string outside of our Inventar"). Maybe you can even write a linter rule for that :)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It's much easier to name things&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Instead of seeing a single color value scattered all over, you'll see names. The Inventar format allows you to separate names and roles, define which values are derived from other value, so you'll know exactly why is it there and what do you need to change.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You can easily use, and reuse, style-values-dedicated logic&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Writing &lt;a href="https://www.npmjs.com/package/inventar#derivatives"&gt;derivatives&lt;/a&gt; and &lt;a href="https://www.npmjs.com/package/inventar#transformers"&gt;transformers&lt;/a&gt; is very easy. Also, if someone publishes their derivative or transformer, you could use them as well, no matter what's your style language/system. Inventar being a tiny library (&amp;lt;2kb gzipped at the time of writing), it might find use as transformers adapter.&lt;/p&gt;

&lt;p&gt;There's also one disadvantage:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This will only work in browsers that support css variables&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://caniuse.com/#feat=css-variables"&gt;CSS variables&lt;/a&gt; and &lt;a href="https://caniuse.com/#feat=proxy"&gt;proxies&lt;/a&gt; (which are used behind the scenes) are widely supported, but you won't be able to use Inventar with Internet Explorer and Opera Mini.&lt;/p&gt;




&lt;p&gt;One thing I hope to see is &lt;strong&gt;tighter cooperation between developers and designers&lt;/strong&gt;. An Inventar configuration could be written in a pairing session, working on a single file and, in many cases, being simple enough for both participants to understand. Combined with easy enforcement, I believe this will prove to be a very effective way to enforce both the designer's wishes and future changes they might decide on.&lt;/p&gt;

&lt;p&gt;In the future, an Inventar configuration can be the single source of truth for designers as well - maybe with plugins to their software or a dedicated UI, that reads the same file and submits pull requests with changes.&lt;/p&gt;

&lt;p&gt;Currently, the project is in a very early stage. I'll appreciate any feedback in the comments :)&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Your Buttons Get Wider on Hover Because You Change Their Font Weight? Here's How to Solve This.</title>
      <dc:creator>Miki Stanger</dc:creator>
      <pubDate>Fri, 19 Jul 2019 16:27:22 +0000</pubDate>
      <link>https://dev.to/mimafogeus2/your-buttons-get-wider-on-hover-because-you-change-their-font-weight-here-s-how-to-solve-this-4bhi</link>
      <guid>https://dev.to/mimafogeus2/your-buttons-get-wider-on-hover-because-you-change-their-font-weight-here-s-how-to-solve-this-4bhi</guid>
      <description>&lt;p&gt;&lt;strong&gt;So,&lt;/strong&gt; A while ago, I've had to implement a design that called for buttons that change their &lt;code&gt;font-weight&lt;/code&gt; on hover. Now here's the problem with this:&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%2Fuploads%2Farticles%2Fnlqvkx3fekppjs2tvau6.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnlqvkx3fekppjs2tvau6.gif" alt="Example of a jumpy button" width="385" height="238"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can try it here:&lt;br&gt;
&lt;iframe height="600" src="https://codepen.io/mimafogeus/embed/RXwaYL?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;This width change is bad. It breaks the layout by pushing adjacent elements, the width change itself feels jumpy, and it all makes for an unpleasant user experience. That's probably not what the designer intended.&lt;/p&gt;

&lt;p&gt;Let's solve this.&lt;/p&gt;

&lt;h1&gt;
  
  
  Set Constant Width
&lt;/h1&gt;

&lt;p&gt;Setting the button's width in CSS is the best and easiest solution here... if there are only a few widths you should set.&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="o"&gt;//&lt;/span&gt; &lt;span class="nt"&gt;Button&lt;/span&gt; &lt;span class="nt"&gt;definition&lt;/span&gt;
&lt;span class="nc"&gt;.constant-width-button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;120px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c"&gt;/* Colors and shape and position and stuff */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="nt"&gt;Modifier&lt;/span&gt; &lt;span class="nt"&gt;class&lt;/span&gt;
&lt;span class="nc"&gt;.constant-width-button--larger&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;240px&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;However, if you're making a reusable button that has text-dependent, dynamic width, playing around with a margin value that'll fit is a major hassle. It is also prone to breakage with different fonts. Here's how I solved it:&lt;/p&gt;

&lt;h1&gt;
  
  
  Duplicate the Label With CSS
&lt;/h1&gt;

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

&lt;h2&gt;
  
  
  The TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;We use CSS to create an invisible pseudo-element that contains a bold version of the text. This element stretches the button, which now has the larger width when you don't hover over it.&lt;/li&gt;
&lt;li&gt;We wrap the element's text with a span, and use absolute positioning to put it over its parent button.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;🎉🎉🎉PROBLEM SOLVED 🎉🎉🎉&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  You're getting a:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Button component that just works.&lt;/li&gt;
&lt;li&gt;Canonical, valid HTML that shows correctly with or without styling, no matter what reader you use.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Let's go through the code:&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  HTML
&lt;/h2&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;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt; &lt;span class="na"&gt;data-label=&lt;/span&gt;&lt;span class="s"&gt;"Hover Me Please"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"button__label"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    Hover Me Please
  &lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are two major additions in the HTML:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An additional &lt;code&gt;span&lt;/code&gt; was added around the label. We need that to apply some CSS to it.&lt;/li&gt;
&lt;li&gt;The label was duplicated into a &lt;code&gt;data-&lt;/code&gt; attribute. This will be used in a &lt;code&gt;:before&lt;/code&gt; pseudo-element.
You could, theoretically, use that same attribute to generate an &lt;code&gt;:after&lt;/code&gt; element and just remove the span and its content, but that would not be semantic - if the CSS won't load, the button wouldn't have any label that way.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  CSS
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The button itself wasn't changed much:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;We added &lt;code&gt;position: relative&lt;/code&gt; so we could use absolute positioning on the children without them overflowing.&lt;/li&gt;
&lt;li&gt;We moved the padding values to variables, so we could reuse them and be clear about it:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--vertical-padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--horizontal-padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--vertical-padding&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;--horizontal-padding&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;relative&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="err"&gt;//&lt;/span&gt; &lt;span class="err"&gt;Design&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;green&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;none&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;#fff&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;24px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.button&lt;/span&gt;&lt;span class="nd"&gt;:hover&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The pseudo-element:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Takes its text from the data-label.&lt;/li&gt;
&lt;li&gt;Renders it in bold weight.&lt;/li&gt;
&lt;li&gt;Is hidden, but its box still affects its surroundings.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.button&lt;/span&gt;&lt;span class="nd"&gt;:before&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;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data-label&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="nl"&gt;visibility&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;hidden&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;h3&gt;
  
  
  The label:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;positioned absolutely&lt;/li&gt;
&lt;li&gt;Fits the content area of the button.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.button__label&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;top&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;--vertical-padding&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;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;--vertical-padding&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;left&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;--horizontal-padding&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;right&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;--horizontal-padding&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;h1&gt;
  
  
  Example: Implementation in React
&lt;/h1&gt;

&lt;p&gt;This logic, if you'll use it, will probably be repeated throughout your code. I used it in a component, which allowed me to take care of the label duplication and encapsulate styling.&lt;br&gt;
I used CSS modules in my project, but you can use whatever styling method you prefer.&lt;br&gt;
Also, note that this example assumes that the button's label is a string and not other React elements.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;styles&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;./MyButton.module.scss&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;restOfProps&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;buttonClassNames&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;className&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="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;button&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="nx"&gt;className&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="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'button'&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;buttonClassNames&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;data-text&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;restOfProps&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;button__label&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Being a stickler for clean HTML and not liking to do CSS tweaks on every reuse of a component, this is the best solution I've found for this problem so far.&lt;br&gt;
Please let me know if you've got better ones in the comments :)&lt;/p&gt;

&lt;h1&gt;
  
  
  Thanks
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://github.com/gerev" rel="noopener noreferrer"&gt;Amir Grozki&lt;/a&gt;, for coming up with the duplicate label idea and discussing what's the best implementation for it.&lt;/p&gt;

</description>
      <category>css</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
