<?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: Edwin</title>
    <description>The latest articles on DEV Community by Edwin (@ekeijl).</description>
    <link>https://dev.to/ekeijl</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%2F328928%2F83669894-fe2b-4857-99bc-7fbd494525c4.png</url>
      <title>DEV Community: Edwin</title>
      <link>https://dev.to/ekeijl</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ekeijl"/>
    <language>en</language>
    <item>
      <title>Native CSS nesting now supported by all major browsers!</title>
      <dc:creator>Edwin</dc:creator>
      <pubDate>Tue, 29 Aug 2023 19:44:42 +0000</pubDate>
      <link>https://dev.to/ekeijl/native-css-nesting-now-supported-by-all-major-browsers-3925</link>
      <guid>https://dev.to/ekeijl/native-css-nesting-now-supported-by-all-major-browsers-3925</guid>
      <description>&lt;p&gt;Today, &lt;a href="https://www.mozilla.org/firefox/117.0/releasenotes/" rel="noopener noreferrer"&gt;Firefox version 117&lt;/a&gt; has been released, which adds support for native nesting of CSS rules! With that addition, all modern &lt;strong&gt;desktop&lt;/strong&gt; browsers now support it! 🎉&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%2Flwlia2xnlsdf2p61q5ox.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flwlia2xnlsdf2p61q5ox.png" alt="Firefox 117 adds support for CSS nesting"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Do keep in mind that there are some mobile browsers that do not yet support it. Together, these account for about 3% of the global browser market share.&lt;/p&gt;

&lt;p&gt;Now that this feature is well supported, we can ask ourselves if using a CSS pre-processor is still worth the hassle.&lt;/p&gt;

&lt;p&gt;But first, a quick refresher.&lt;/p&gt;

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

&lt;p&gt;CSS nesting allows you to group related selectors together and potentially reduce the number of rules you need to write:&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;/* CSS */&lt;/span&gt;
&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

   &lt;span class="err"&gt;&amp;amp;.danger&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
     &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;red&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="nt"&gt;img&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;1rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1rem&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;/* HTML */&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="nt"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"danger"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;I&lt;/span&gt; &lt;span class="nt"&gt;am&lt;/span&gt; &lt;span class="nt"&gt;red&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;img&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;It allows you to nest multiple levels, but please don't go overboard with nesting too deeply, as this adds up to the specificity of these CSS rules. This makes it hard to read and reuse your styles - nobody likes pyramids of doom! Deep nesting is simply unnecessary in most cases.&lt;/p&gt;

&lt;h2&gt;
  
  
  Do you really still need a CSS pre-processor?
&lt;/h2&gt;

&lt;p&gt;Many people use so called &lt;em&gt;CSS pre-processors&lt;/em&gt; such as &lt;a href="https://sass-lang.com/guide/" rel="noopener noreferrer"&gt;SCSS&lt;/a&gt; or &lt;a href="https://lesscss.org/features/" rel="noopener noreferrer"&gt;LESS&lt;/a&gt; that add fancy features to CSS, such as variables, mixins and functions, but they need to be transformed back to regular CSS during the build process of your app, so the browser can understand it.&lt;/p&gt;

&lt;p&gt;The features they offer are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Nesting&lt;/strong&gt; - There are only small differences between native CSS nesting and LESS/SASS. CSS is a little more strict, because you need to prepend every nsted selector with &lt;code&gt;&amp;amp;&lt;/code&gt; and you need to define regular styling &lt;em&gt;before&lt;/em&gt; the nested styling.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Variables&lt;/strong&gt; - Native CSS variables have &lt;a href="https://caniuse.com/css-variables" rel="noopener noreferrer"&gt;good browser support&lt;/a&gt; for a while now. The benefit of using native variables is that they are available at run time in your browser, which allows you to easily tweak them in your developer tools. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mixins&lt;/strong&gt; - This allows you to reuse a set of rules inside another selector. I never really found a good use case for mixins. They were available to me when I was still using Bootstrap with LESS, but using them seemed a little complex, because you always need to look up what they do and the resulting CSS output is not always clear. If you're thinking of using them for browser prefixes (e.g. &lt;code&gt;-webkit-transform&lt;/code&gt;), I would suggest checking out &lt;a href="https://github.com/postcss/autoprefixer" rel="noopener noreferrer"&gt;PostCSS autoprefixer&lt;/a&gt; instead.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Partials/Modules&lt;/strong&gt; - CSS has an &lt;code&gt;@import&lt;/code&gt; rule that works well enough (at the cost of an extra HTTP request). Combine imports with &lt;a href="https://www.bram.us/2021/09/15/the-future-of-css-cascade-layers-css-at-layer/" rel="noopener noreferrer"&gt;CSS layers&lt;/a&gt; and you have a pretty solid setup to modularize your CSS.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Functions&lt;/strong&gt; - These allow you to write CSS in a similar fashion as functions in other programming languages. I always found that they bloat your CSS output and they are hard to read as well. I would avoid using functions unless you have a really good use case for it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Operators&lt;/strong&gt; - Need math in your styles? &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/calc" rel="noopener noreferrer"&gt;CSS calc&lt;/a&gt; gets the job done.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All in all, most features that pre-processors offer can nowadays be replaced by native CSS, so you can ask yourself whether an extra pre-processing tool in your stack is still worth it.&lt;/p&gt;

&lt;p&gt;In large projects, it is still a good idea to use &lt;a href="https://postcss.org/" rel="noopener noreferrer"&gt;PostCSS&lt;/a&gt;, which will translate new CSS features to something that browsers understand today.&lt;/p&gt;

</description>
      <category>css</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Practical CSS :has() selector examples</title>
      <dc:creator>Edwin</dc:creator>
      <pubDate>Fri, 25 Nov 2022 15:12:28 +0000</pubDate>
      <link>https://dev.to/ekeijl/practical-css-has-selector-examples-291b</link>
      <guid>https://dev.to/ekeijl/practical-css-has-selector-examples-291b</guid>
      <description>&lt;p&gt;This article showcases how the CSS &lt;code&gt;:has()&lt;/code&gt; selector can be used in everyday use cases. &lt;/p&gt;

&lt;p&gt;For more examples, click the menu button or &lt;a href="https://34ugft.csb.app/" rel="noopener noreferrer"&gt;open the Codesandbox fullscreen&lt;/a&gt;. The source code is  &lt;a href="https://codesandbox.io/s/css-has-use-cases-34ugft" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/34ugft"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Browser support
&lt;/h2&gt;

&lt;p&gt;Currently, the &lt;code&gt;:has()&lt;/code&gt; selector is available in all modern browsers! 🎉🎉🎉&lt;/p&gt;

&lt;p&gt;&lt;a href="https://caniuse.com/css-has" rel="noopener noreferrer"&gt;https://caniuse.com/css-has&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Basics
&lt;/h2&gt;

&lt;p&gt;In short, the &lt;code&gt;:has()&lt;/code&gt; selector allows you to select a "parent" element based on a specific condition that you specify within the parentheses.&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;/** Select form that has a label */&lt;/span&gt;
&lt;span class="nt"&gt;form&lt;/span&gt;&lt;span class="nd"&gt;:has&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt;&lt;span class="o"&gt;)&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="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/** x:has(a,b,c) --&amp;gt; x has (a OR b OR c) */&lt;/span&gt;
&lt;span class="nt"&gt;form&lt;/span&gt;&lt;span class="nd"&gt;:has&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"text"&lt;/span&gt;&lt;span class="o"&gt;],&lt;/span&gt; &lt;span class="nt"&gt;input&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"checkbox"&lt;/span&gt;&lt;span class="o"&gt;],&lt;/span&gt; &lt;span class="nt"&gt;input&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"number"&lt;/span&gt;&lt;span class="o"&gt;])&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="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/** x:has(a):has(b):has(c) --&amp;gt; x has (a AND b AND c) */&lt;/span&gt;
&lt;span class="nt"&gt;form&lt;/span&gt;&lt;span class="nd"&gt;:has&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"text"&lt;/span&gt;&lt;span class="o"&gt;])&lt;/span&gt;&lt;span class="nd"&gt;:has&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"checkbox"&lt;/span&gt;&lt;span class="o"&gt;])&lt;/span&gt;&lt;span class="nd"&gt;:has&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"number"&lt;/span&gt;&lt;span class="o"&gt;])&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="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#000&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 interesting, because it allows us to select the &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; and apply styles to it &lt;em&gt;based on a condition&lt;/em&gt;. From there, we can also select elements further down the chain again!&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;/** select input within a form that has a label */&lt;/span&gt;
&lt;span class="nt"&gt;form&lt;/span&gt;&lt;span class="nd"&gt;:has&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;input&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="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="no"&gt;red&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 pattern opens up new ways of conditionally styling elements that were not possible before (without JS, hacks or workarounds). Things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Layout elements based on the contents of a container element.&lt;/li&gt;
&lt;li&gt;Style a container based on the number of items inside it.&lt;/li&gt;
&lt;li&gt;Applying styles to a container based on pseudo-classes (&lt;code&gt;:hover&lt;/code&gt;, &lt;code&gt;:disabled&lt;/code&gt;, &lt;code&gt;:invalid&lt;/code&gt;) of a descendant.&lt;/li&gt;
&lt;li&gt;Select the "previous sibling" element using &lt;code&gt;:has(+ .other)&lt;/code&gt;. Before, we could only select the "next sibling" element using the &lt;code&gt;+&lt;/code&gt; operator.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Thoughts
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;CSS &lt;code&gt;:has&lt;/code&gt; almost requires you to reset the way you think about selecting elements, as we could only write selectors that go further down the element tree, but now we can select elements back up as well.&lt;/li&gt;
&lt;li&gt;Sometimes we need JavaScript to add classes to elements based on the state or data in our application. See the "Todo list" example, where we show a message when all checkboxes are ticked. The common way to achieve this is to iterate over the to-do item data, check if all items are 'completed' and conditionally render a message (or add a class) based on that data. But with &lt;code&gt;:has()&lt;/code&gt;, we can apply styles to some element &lt;strong&gt;based on the UI state of a completely different element&lt;/strong&gt;. For simple cases, you may no longer need JS at all!&lt;/li&gt;
&lt;li&gt;When styling an element based on its contents, we need to think about &lt;strong&gt;separation of concerns&lt;/strong&gt;. Adding some spacing to a &lt;code&gt;&amp;lt;figure&amp;gt;&lt;/code&gt; element if it has an &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; or &lt;code&gt;&amp;lt;figcaption&amp;gt;&lt;/code&gt; totally makes sense, but where do we draw the line? This applies to every type of CSS selector, I suppose, but with &lt;code&gt;:has()&lt;/code&gt; you may be more easily tempted to write your CSS like a giant &lt;code&gt;if&lt;/code&gt;-statement with a million cases.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What more?
&lt;/h2&gt;

&lt;p&gt;Can you think of an awesome use case for &lt;code&gt;:has()&lt;/code&gt;? let us know! 👇👇👇&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%2Fhoh45e9snsjc10f43fos.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%2Fhoh45e9snsjc10f43fos.gif" alt="to infinity and beyond" width="245" height="257"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>css</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Building a TODO app without a bundler</title>
      <dc:creator>Edwin</dc:creator>
      <pubDate>Sat, 04 Jun 2022 17:27:56 +0000</pubDate>
      <link>https://dev.to/ekeijl/no-build-todo-app-using-htm-preact-209p</link>
      <guid>https://dev.to/ekeijl/no-build-todo-app-using-htm-preact-209p</guid>
      <description>&lt;p&gt;Do you remember the time before front-end frameworks and build tools, where you would sprinkle some JavaScript on top of your HTML to create interactivity? Code up your HTML documents, preview them in the browser without tools like Webpack, then push them to your webserver using FTP? &lt;br&gt;
I sure do. 👴&lt;/p&gt;

&lt;p&gt;What if I told you, you can build modern web apps and have still have a smooth development workflow without any build tools?&lt;/p&gt;

&lt;p&gt;In this article I'm going to implement the &lt;a href="https://todomvc.com/"&gt;TodoMVC&lt;/a&gt; app without any build tools and only use native JS functions supported by evergreen browsers (sorry Internet Explorer, &lt;a href="https://dev.to/ekeijl/internet-explorer-retires-on-june-15-what-can-a-developer-do-11d3"&gt;it's time for you to leave&lt;/a&gt;). &lt;/p&gt;

&lt;p&gt;I will use some libraries related to React, but you could write the app using anything you prefer (or no library at all). &lt;strong&gt;What matters most is the fact that we simplify our development process by cutting out tools that are required to work with these modern day frameworks&lt;/strong&gt;. The starting point is just a HTML document with a &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; that initializes our app, while SPAs often start from an &lt;code&gt;index.js&lt;/code&gt; &lt;em&gt;entry point&lt;/em&gt; and try to control the document from there.&lt;/p&gt;

&lt;p&gt;Here's the &lt;a href="https://github.com/ekeijl/todomvc-htm-preact"&gt;source code&lt;/a&gt; and the end result:&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/vyefvj?initialpath=%23/all"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h1&gt;
  
  
  Single Page Apps
&lt;/h1&gt;

&lt;p&gt;When building an interactive web app, developers usually reach for frameworks such as React, Angular, Vue, Svelte, to name a few. These frameworks are mostly abstractions and best practises to help you create modular code while staying productive. They all come with a set of supporting tools to smooth out the development process: translate modern JavaScript features to something all target browsers understand, manage dependencies, optimize the output code, etc.&lt;/p&gt;

&lt;p&gt;These interactive client side apps are often &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/SPA"&gt;Single-Page Applications&lt;/a&gt;: a web application that loads a single document and then updates the page content using JavaScript by loading other modules and fetching data from a REST API.&lt;/p&gt;

&lt;p&gt;Not every website needs to be a SPA, mind you. In fact, the approach below could be used in a good old multi-page website, where you sprinkle the JS on top of the page to create the interactive ToDo functionality.&lt;/p&gt;

&lt;h1&gt;
  
  
  Goals
&lt;/h1&gt;

&lt;p&gt;We are going to build a simple TODO application such as &lt;a href="https://todomvc.com/examples/backbone/"&gt;this one&lt;/a&gt;, which is fully client side and has a clear scope.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Implement the TodoMVC app using &lt;a href="https://github.com/tastejs/todomvc/blob/master/app-spec.md"&gt;this specification&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Use only native &lt;a href="http://kangax.github.io/compat-table/es6/"&gt;ES6 browser features&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;No build tools (Babel, Webpack, etc).&lt;/li&gt;
&lt;li&gt;We still want to be able to use NPM packages.&lt;/li&gt;
&lt;li&gt;Supports latest stable version of Chrome, Firefox, Safari, Edge.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Why would you go buildless?
&lt;/h1&gt;

&lt;p&gt;Let's start with the main reasons we still need bundlers in 2022:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The NPM ecosystem is built around packages being able to run in &lt;a href="https://nodejs.org/en/"&gt;NodeJS&lt;/a&gt;, not primarily for the web. NPM packages are expected to use the &lt;a href="https://nodejs.org/api/modules.html"&gt;CommonJS format&lt;/a&gt; to ensure everything is compatible with each other. Publishing a package using pure &lt;a href="https://nodejs.org/api/esm.html"&gt;ES Modules&lt;/a&gt; would break that compatibility. Seems backwards, right?&lt;/li&gt;
&lt;li&gt;Packages use a short-cut method of importing other packages by their package name, without an extension (&lt;em&gt;bare imports&lt;/em&gt;), e.g.: &lt;code&gt;import groupBy from lodash/groupBy&lt;/code&gt; instead of &lt;code&gt;import groupBy from './node_modules/lodash/groupBy.js&lt;/code&gt;. Tooling is needed to fix the module resolution.&lt;/li&gt;
&lt;li&gt;Bundlers take care of a lot of implicit stuff, like polyfilling missing features. A lot of NPM packages expect this stuff to just work.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://www.pika.dev/"&gt;Pika&lt;/a&gt; is doing an awesome job at rethinking this whole process and it questions why we need web bundlers today at all. Check out this great talk:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/2Wwx-lF5NhE"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;The reason to ditch all this tooling seems obvious: it simplifies development, because you only need to deal with native JavaScript. No tools to learn, no configurations to manage, no more waiting for your app to start up.&lt;/p&gt;

&lt;p&gt;You get some additional benefits too:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your development environment is exactly the same as your production environment, which can make debugging easier.&lt;/li&gt;
&lt;li&gt;No security risk of installing third party code during development. NPM packages can basically run any code on your machine using post-install scripts.&lt;/li&gt;
&lt;li&gt;Modules are cached individually. Updating a single module means other modules stay cached. This is more of a hassle when using Webpack.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Downsides of going buildless
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;No pre-processing, so you lose access to tools like TypeScript, LESS/SASS (for CSS).&lt;/li&gt;
&lt;li&gt;No support for modular CSS yet. &lt;a href="https://css-tricks.com/css-modules-the-native-ones/"&gt;Import assertions&lt;/a&gt; are far from production ready.&lt;/li&gt;
&lt;li&gt;No minification or tree-shaking of application code.&lt;/li&gt;
&lt;li&gt;Slight performance hit compared to loading bundled JS. Large JS files still compress better than smaller individual files. So there is some benefit in bundling all code into a single file. HTTP/2 might resolve some of that issue, but I haven't seen concrete numbers yet. See also &lt;a href="https://github.com/lukejacksonn/perflink/issues/15"&gt;this discussion&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Managing module imports can become messy, resulting in long relative import paths &lt;code&gt;../../../module/subModule/component.mjs&lt;/code&gt;. Webpack has &lt;a href="https://webpack.js.org/configuration/resolve/#resolvealias"&gt;aliases&lt;/a&gt; to make your life easier. Luckily, JS &lt;a href="https://caniuse.com/import-maps"&gt;import maps&lt;/a&gt; solve most of this problems and are available in all major browsers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You win some, you lose some.&lt;/p&gt;

&lt;h1&gt;
  
  
  Using third party libraries in a buildless setup
&lt;/h1&gt;

&lt;p&gt;Just because we aren't allowed to use build tools, does not mean we can't use any NPM libraries. To load them, we have several options.&lt;/p&gt;

&lt;p&gt;Content Delivery Networks (CDNs) are free online services that serve NPM packages over the network. Examples are &lt;a href="https://www.jsdelivr.com/"&gt;jsDelivr&lt;/a&gt;, &lt;a href="https://unpkg.com/"&gt;unpkg&lt;/a&gt; and &lt;a href="https://www.skypack.dev/"&gt;SkyPack&lt;/a&gt;. We will be using these services to import the libraries we want to use.&lt;/p&gt;

&lt;p&gt;You can import those packages using a script tag, for example:&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;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ES modules allow you to import directly from a URL:&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;groupBy&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;https://unpkg.com/lodash-es@3.10.1/collection/groupBy.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://dev.to/open-wc/developing-without-a-build-1-introduction-26ao"&gt;Learn more about ES imports in this article&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Libraries for the buildless route
&lt;/h1&gt;

&lt;p&gt;We are looking for libraries that use ES modules, so we can drop them into our app and use them like any other library.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://lit.dev/"&gt;Lit Element&lt;/a&gt; which builds on top of the web components standard. (&lt;a href="https://dev.to/open-wc/on-the-bleeding-edge-3cb8"&gt;example app&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/FranckFreiburger/vue3-sfc-loader"&gt;Vue Single File Component loader&lt;/a&gt; allows you to sprinkle Vue on top of any HTML document. (&lt;a href="https://paulhammant.com/2021/02/16/buildless-sfc-vuejs-applications/"&gt;example app&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/developit/htm"&gt;HTM&lt;/a&gt; - a library that lets you write components using JSX-like syntax using template string.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://symbiotejs.org"&gt;Symbiote&lt;/a&gt; - framework that allows you to write class based Custom Elements, focused on building complex widgets that you can then embed in other apps.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://unsuckjs.com/"&gt;Here&lt;/a&gt; you can find a list that compares multiple no-build libraries.&lt;/p&gt;

&lt;h2&gt;
  
  
  HTM, Preact &amp;amp; JSX
&lt;/h2&gt;

&lt;p&gt;I feel very productive writing front-end UI components in React using JSX, so I wanted to have something similar for this app. After some googling I stumbled upon HTM, which promises JSX-like syntax without bundling, so I decided to give that a try. HTM plays nicely with &lt;a href="https://preactjs.com/"&gt;Preact&lt;/a&gt; (a leaner version of React with only slight differences).&lt;/p&gt;

&lt;p&gt;When using Preact without HTM, you would need create components similar to how you would call &lt;code&gt;React.createElement()&lt;/code&gt;, namely:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://esm.sh/preact&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;myH1Element&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;h1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello World!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;h1&amp;gt;Hello World!&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;HTM uses JavaScript's own &lt;a href="https://preactjs.com/guide/v10/getting-started#alternatives-to-jsx"&gt;tagged template syntax&lt;/a&gt;, so you can write JSX-like syntax:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;render&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://esm.sh/preact&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;htm&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;https://esm.sh/htm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Initialize htm with Preact&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;htm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;h&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;MyComponent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;state&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;html&lt;/span&gt;&lt;span class="s2"&gt;`&amp;lt;div ...&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; class=bar&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/div&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;htm&lt;/span&gt;&lt;span class="s2"&gt;`&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;MyComponent&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; /&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For convenience, HTM provides a module that does this binding for you. You only need to import from the &lt;code&gt;preact/standalone&lt;/code&gt; module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;render&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://esm.sh/htm/preact/standalone&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="s2"&gt;`&amp;lt;&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;span class="s2"&gt; name="World" /&amp;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;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  State management using Valtio
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://valtio.pmnd.rs/"&gt;Valtio&lt;/a&gt; uses JavaScript proxies to wrap your state objects and track changes automagically. ✨&lt;/p&gt;

&lt;p&gt;The state can be manipulated outside the React/Preact lifecycle too using vanilla JS. &lt;a href="https://github.com/pmndrs/valtio/wiki/How-to-persist-states"&gt;Persisting the app state&lt;/a&gt; to &lt;code&gt;localStorage&lt;/code&gt; is also trivial.&lt;/p&gt;

&lt;p&gt;The library is light-weight and easy to work with. Valtio is certainly not required for the no-build app, but it felt like a good match for this setup.&lt;/p&gt;

&lt;h1&gt;
  
  
  Implementing the TODO app
&lt;/h1&gt;

&lt;p&gt;I would like to use a component based development approach without writing everything from scratch, so I decided to use &lt;a href="https://github.com/developit/htm"&gt;HTM&lt;/a&gt; with &lt;a href="https://preactjs.com/"&gt;Preact&lt;/a&gt;. This allows me to write JSX-like syntax without a transpiler.&lt;/p&gt;

&lt;p&gt;I won't dive too deep into the implementation itself, but you can find the source on &lt;a href="https://github.com/ekeijl/todomvc-htm-preact"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;

&lt;p&gt;Create an &lt;code&gt;index.html&lt;/code&gt; file and add a &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag and point it to &lt;code&gt;js/index.mjs&lt;/code&gt; - the starting point of the app:&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="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;No-build ToDo app&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"module"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"js/index.mjs"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can import the CSS for our TODO app directly from a CDN like so:&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;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://unpkg.com/todomvc-common@1.0.5/base.css"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the &lt;code&gt;index.mjs&lt;/code&gt; file we can &lt;code&gt;import()&lt;/code&gt; any other modules that we need. From here we can start writing modular components as we would do when using React!&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="c1"&gt;// js/index.mjs&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;render&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;preact&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Header&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./Header/index.mjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Footer&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./Footer/index.mjs&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;App&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="s2"&gt;`
        &amp;lt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; /&amp;gt;
        &amp;lt;section class="todoapp"&amp;gt;
            &amp;lt;!-- etc --&amp;gt;
        &amp;lt;/section&amp;gt;
        &amp;lt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;Footer&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="s2"&gt;` &amp;lt;&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;span class="s2"&gt; /&amp;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;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Beware that we need to write the full path, including extension, when importing a module - this is how ESM works.&lt;/p&gt;

&lt;h2&gt;
  
  
  Importing third party modules
&lt;/h2&gt;

&lt;p&gt;You might be wondering how we are importing &lt;code&gt;preact&lt;/code&gt;, as we are doing on the first line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;render&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;preact&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;How does our browser know how to resolve &lt;code&gt;preact&lt;/code&gt;? We are using a so called &lt;em&gt;import map&lt;/em&gt; to define a mapping of a dependency to a specific URL. This URL can point directly to a CDN where this library is hosted.&lt;/p&gt;

&lt;p&gt;In our &lt;code&gt;index.html&lt;/code&gt;, we define a script of type &lt;code&gt;importmap&lt;/code&gt; where we map all our dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;script type="importmap"&amp;gt;
{
  "imports": {
    "uuid": "https://esm.sh/uuid",
    "preact": "https://esm.sh/preact",
    "preact/compat": "https://esm.sh/preact/compat",
    "preact/hooks": "https://esm.sh/preact/hooks",
    "htm": "https://esm.sh/htm",
    "valtio": "https://esm.sh/*valtio?alias=react:preact/compat",
    "proxy-compare": "https://esm.sh/proxy-compare",
    "use-sync-external-store/shim/index.js": "https://esm.sh/use-sync-external-store?alias=react:preact/compat"
  }
}
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case we are using &lt;a href="https://esm.sh/"&gt;esm.sh&lt;/a&gt;, which is a CDN for NPM packages. It bundles the library in the URL with all of its dependencies, so they are ready to use in the browser.&lt;/p&gt;

&lt;p&gt;See &lt;a href="https://blog.logrocket.com/es-modules-in-browsers-with-import-maps/"&gt;this article&lt;/a&gt; for more information about import maps.&lt;/p&gt;

&lt;h3&gt;
  
  
  ESM.sh aliasing
&lt;/h3&gt;

&lt;p&gt;We need to tell ESM.sh that it needs to replace all imports of &lt;code&gt;react&lt;/code&gt; with &lt;code&gt;preact/compat&lt;/code&gt;, which is Preact's &lt;a href="https://preactjs.com/guide/v10/switching-to-preact/"&gt;compatability library&lt;/a&gt;. We can do this by using the &lt;code&gt;alias&lt;/code&gt; URL parameter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://esm.sh/valtio?alias=react:preact/compat
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Issue with &lt;code&gt;use-sync-external-hook&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;There is an &lt;a href="https://github.com/facebook/react/issues/24590"&gt;issue&lt;/a&gt; with the &lt;code&gt;use-sync-external-store&lt;/code&gt; package (a React hook that Valtio uses to synchronize state changes with React) when using ES Modules.&lt;/p&gt;

&lt;p&gt;There are a few things that need fixing:&lt;/p&gt;

&lt;p&gt;Tell ESM.sh that it should not rewrite module imports for &lt;code&gt;use-sync-external-store&lt;/code&gt;. ESM offers a parameter &lt;code&gt;external&lt;/code&gt; to mark an import as external, so it won't be bundled. This means we need to define it ourselves:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"valtio": "https://esm.sh/valtio?external=use-sync-external-store/shim/index.js"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The annoying thing is that we need to point to the complete path of the imported module (including &lt;code&gt;shim/index.js&lt;/code&gt;). Instead, we can mark all dependencies as external by prefixing the package with an asterisk:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"valtio": "https://esm.sh/*valtio
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means we need to define import mappings for all of valtio's dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"proxy-compare": "https://esm.sh/proxy-compare",
"use-sync-external-store/shim/index.js": "https://esm.sh/use-sync-external-store?alias=react:preact/compat"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Issue with Preact standalone
&lt;/h3&gt;

&lt;p&gt;For some reason the Preact standalone module breaks things due to a missing module. The solution was to import Preact and htm separately and create a module that initializes htm with Preact:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;preact&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;htm&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;htm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;htm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then use this to render components:&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;html&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;../render.mjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Footer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="s2"&gt;`&amp;lt;h1&amp;gt;Footer&amp;lt;/h1&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Run the app
&lt;/h2&gt;

&lt;p&gt;Now we only need to some kind of static file server so we can preview the app in our browser. You can use the &lt;a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode.live-server"&gt;VSCode Live Preview extension&lt;/a&gt; or use a &lt;a href="https://www.npmjs.com/package/serve"&gt;simple static server&lt;/a&gt; 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;npx serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Creating an app without a bundler was a fun and overall a pretty smooth experience. ES6 has all the language features needed to create apps with a great developer experience. We've seen how dependencies can be imported from a CDN to add third party code to our app without the need for extra tools.&lt;/p&gt;

&lt;p&gt;Still, I probably wouldn't go without a bundler for production apps in 2022. Choosing which tools to use is a trade-off between complexity of the build process and productivity + optimizations that you get by using these tools. Using third party libraries in a buildless app might prove hard if there is no ESM version available.&lt;/p&gt;

&lt;p&gt;Pika is a great initiative that moves the complexity of build tools away from the app. It is a step towards a simpler development process. It's nice to see that the JS ecosystem is moving towards ES modules, which makes a lot of sense to me.&lt;/p&gt;

&lt;h1&gt;
  
  
  Sources
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://world.hey.com/dhh/modern-web-apps-without-javascript-bundling-or-transpiling-a20f2755"&gt;https://world.hey.com/dhh/modern-web-apps-without-javascript-bundling-or-transpiling-a20f2755&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.logrocket.com/building-without-bundling/"&gt;https://blog.logrocket.com/building-without-bundling/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/pika/a-future-without-webpack-ago"&gt;https://dev.to/pika/a-future-without-webpack-ago&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/open-wc/developing-without-a-build-1-introduction-26ao"&gt;https://dev.to/open-wc/developing-without-a-build-1-introduction-26ao&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/open-wc/on-the-bleeding-edge-3cb8"&gt;https://dev.to/open-wc/on-the-bleeding-edge-3cb8&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>react</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Internet Explorer retires on June 15 - what can developers do?</title>
      <dc:creator>Edwin</dc:creator>
      <pubDate>Tue, 24 May 2022 16:12:15 +0000</pubDate>
      <link>https://dev.to/ekeijl/internet-explorer-retires-on-june-15-what-can-a-developer-do-11d3</link>
      <guid>https://dev.to/ekeijl/internet-explorer-retires-on-june-15-what-can-a-developer-do-11d3</guid>
      <description>&lt;p&gt;Microsoft will finally stop supporting Internet Explorer 11 on June 15, 2022. That is &lt;del&gt;3 weeks from now&lt;/del&gt; &lt;strong&gt;RIGHT NOW&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;If a Windows 10 or Windows 11 user tries to open IE after this date, the Edge browser will launch instead (or the user will be taken to the download page for Edge). &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%2Foedpxscxsqwbni2ej266.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foedpxscxsqwbni2ej266.png" alt="Edge notification"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is an IE11 compatibility mode to support legacy apps, but it may not work exactly the same. This legacy mode is supported until 2029, so we won't be completely rid of Internet Explorer for some time. However, if you are a developer that is building modern web apps, I think it's better to drop support for Internet Explorer completely.&lt;/p&gt;

&lt;p&gt;In this article, I will explain what the problems of supporting Internet Explorer are and how you can move to a better alternative.&lt;/p&gt;

&lt;h1&gt;
  
  
  What's the problem with IE11?
&lt;/h1&gt;

&lt;p&gt;According to Microsoft itself, &lt;a href="https://www.zdnet.com/article/microsoft-security-chief-ie-is-not-a-browser-so-stop-using-it-as-your-default/" rel="noopener noreferrer"&gt;IE is not a browser but a compatibility solution&lt;/a&gt; for legacy apps. New browser features are not supported by it, which means new apps do not work out of the box in IE. By using IE, you are effectively going back in time to experience the web as if it were &lt;a href="https://en.wikipedia.org/wiki/Internet_Explorer_11#History" rel="noopener noreferrer"&gt;2013&lt;/a&gt; (the most recent updates being only security patches).&lt;/p&gt;

&lt;p&gt;The global market share (desktop - April '22) of IE is as low as &lt;a href="https://gs.statcounter.com/browser-market-share/desktop/worldwide" rel="noopener noreferrer"&gt;0.4%&lt;/a&gt;, compared to 64% for Chrome, 10% for Edge, 10% for Safari and 7.9% for Firefox. So who is still using it? Mostly &lt;a href="https://qz.com/2011408/some-companies-will-still-use-internet-explorer-after-its-retired/" rel="noopener noreferrer"&gt;users of legacy applications&lt;/a&gt;,  people who work at companies with strict browser policies and... &lt;a href="https://www.ft.com/content/a71ace1b-37b8-49f4-b8a5-baddbdb67b8b" rel="noopener noreferrer"&gt;Japan&lt;/a&gt;, apparently.&lt;/p&gt;

&lt;p&gt;From a technical perspective, developers that need to support IE cannot make full use of new browser features in their apps that work out of the box in evergreen browsers such as Chrome, Firefox and Microsoft's newest Edge browser. &lt;/p&gt;

&lt;p&gt;IE11 only works with an older version of JavaScript, namely ECMAScript 5 (ES5). Developers can use tools like Babel so they can still write modern JavaScript (ES6 and beyond), which gets translated back to ES5 so it works in every browser. This "transpilation" inevitably creates overhead in the resulting code bundle. Furthermore, some third party JavaScript libraries may only be available in ES6, so an app developer has to make additional effort to ensure those libraries are transpiled as well.&lt;/p&gt;

&lt;p&gt;Other frameworks and tools are also abandoning IE11: &lt;a href="https://webpack.js.org/migrate/5/#need-to-support-an-older-browser-like-ie-11" rel="noopener noreferrer"&gt;Webpack 5 bundles for ES6 by default&lt;/a&gt;, &lt;a href="https://reactjs.org/docs/javascript-environment-requirements.html" rel="noopener noreferrer"&gt;React 18&lt;/a&gt; and &lt;a href="https://create-react-app.dev/docs/supported-browsers-features/" rel="noopener noreferrer"&gt;CRA&lt;/a&gt; dropped support, to name a few. If you want to stay up to date, that means you will need to make the decision at some point.&lt;/p&gt;

&lt;p&gt;Dropping support for a browser means that you potentially lose revenue of its users. A bigger problem, however, is that supporting IE11 causes a degraded performance for &lt;em&gt;all other browsers&lt;/em&gt;. It will also lock your app out of innovative new features such as &lt;a href="https://web.dev/progressive-web-apps/" rel="noopener noreferrer"&gt;Progressive Web App features&lt;/a&gt; (unless you apply &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/Graceful_degradation" rel="noopener noreferrer"&gt;graceful degradation&lt;/a&gt;).&lt;/p&gt;

&lt;h1&gt;
  
  
  Unsupported features in IE11
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://caniuse.com/?compare=ie+11,edge+101&amp;amp;compareCats=all" rel="noopener noreferrer"&gt;This page&lt;/a&gt; compares supported browser features between IE11 and Edge version 100. The length of this list is staggering, to be honest. Some notable features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://caniuse.com/?search=dom%20manipulation" rel="noopener noreferrer"&gt;DOM manipulation methods&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Full &lt;a href="https://caniuse.com/?search=css%20flex" rel="noopener noreferrer"&gt;CSS Flex&lt;/a&gt; and &lt;a href="https://caniuse.com/?search=css%20grid" rel="noopener noreferrer"&gt;CSS Grid&lt;/a&gt; support&lt;/li&gt;
&lt;li&gt;&lt;a href="https://caniuse.com/css-sticky" rel="noopener noreferrer"&gt;CSS sticky&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://caniuse.com/css-filters" rel="noopener noreferrer"&gt;CSS filters&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Input types for &lt;a href="https://caniuse.com/input-datetime" rel="noopener noreferrer"&gt;date and time&lt;/a&gt; and &lt;a href="https://caniuse.com/?search=color%20input" rel="noopener noreferrer"&gt;color&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://caniuse.com/serviceworkers" rel="noopener noreferrer"&gt;Service Workers&lt;/a&gt; and most other PWA related features&lt;/li&gt;
&lt;li&gt;&lt;a href="https://caniuse.com/fetch" rel="noopener noreferrer"&gt;Fetch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://caniuse.com/custom-elementsv1" rel="noopener noreferrer"&gt;Custom elements&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://caniuse.com/resizeobserver" rel="noopener noreferrer"&gt;Resize observer&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Missing JS language features I personally use almost every day:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://kangax.github.io/compat-table/es6/" rel="noopener noreferrer"&gt;ES6 language features in general&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://caniuse.com/?search=arrow%20function" rel="noopener noreferrer"&gt;Arrow functions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://caniuse.com/template-literals" rel="noopener noreferrer"&gt;Template literals&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://caniuse.com/promises" rel="noopener noreferrer"&gt;Promises&lt;/a&gt; and &lt;a href="https://caniuse.com/async-functions" rel="noopener noreferrer"&gt;Async functions&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://caniuse.com/object-entries" rel="noopener noreferrer"&gt;Object.entries&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://caniuse.com/array-find" rel="noopener noreferrer"&gt;Array.find&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://caniuse.com/es6-class" rel="noopener noreferrer"&gt;Classes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Transpilation example
&lt;/h2&gt;

&lt;p&gt;Consider the following piece of code where I showcase some ES6 features:&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;doStuff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &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;foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bar&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;qux&lt;/span&gt; &lt;span class="o"&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;foo&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; with extra &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&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;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;qux&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;This code doesn't do anything useful, but if you press F12 in a modern browser to open your developer tools and paste it in the console, it will run just fine.&lt;/p&gt;

&lt;p&gt;Now &lt;a href="https://babeljs.io/repl#?browsers=ie%2011&amp;amp;build=&amp;amp;builtIns=false&amp;amp;corejs=3.6&amp;amp;spec=false&amp;amp;loose=false&amp;amp;code_lz=MYewdgzgLgBAJiAylArgMzTAvDAhhATzGBgAoBKbAPhgG8AoGGUSWNEEbGAIgCNcATtwDcjGOxAA6AJbEANijgBTCKW6Du5UWLlLYARxQAPLgAMAJLQkBfGAHdpUABYwlRqANwxLN06KZiAnooAmB4driOMGBKdjAACgIgALbSEEqkpEEQIHIAbkqUWDQMTEzZuQVq_ABemv4w1lr01sJAA&amp;amp;debug=false&amp;amp;forceAllTransforms=false&amp;amp;shippedProposals=false&amp;amp;circleciRepo=&amp;amp;evaluate=false&amp;amp;fileSize=false&amp;amp;timeTravel=false&amp;amp;sourceType=module&amp;amp;lineWrap=true&amp;amp;presets=env&amp;amp;prettier=false&amp;amp;targets=&amp;amp;version=7.18.1&amp;amp;externalPlugins=&amp;amp;assumptions=%7B%7D" rel="noopener noreferrer"&gt;watch the result&lt;/a&gt; if we transpile this code using Babel to something that IE11 understands. If you scroll down to line 11 in the frame on the right you will see something that looks like our original function. All the cruft above it is required to make it run in old browsers.&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;var&lt;/span&gt; &lt;span class="nx"&gt;doStuff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="cm"&gt;/*#__PURE__*/&lt;/span&gt;&lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;_ref&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;_asyncToGenerator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="cm"&gt;/*#__PURE__*/&lt;/span&gt;&lt;span class="nf"&gt;_regeneratorRuntime&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;mark&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;_callee&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;qux&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;_regeneratorRuntime&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;wrap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;_callee$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prev&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;case&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;foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bar&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nx"&gt;qux&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;concat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; with extra &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;concat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nx"&gt;_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;baz&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt;

          &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;5&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;_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abrupt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;return&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

          &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;end&lt;/span&gt;&lt;span class="dl"&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;_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stop&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;_callee&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;doStuff&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;_ref&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;apply&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;arguments&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;This example showcases how building for IE11 negatively impacts your bundle size, build time, total byte size sent over the network, parse time and page load time! More about transpilation bloat in &lt;a href="https://webreflection.medium.com/avoiding-babels-production-bloat-d53eea2e1cbf" rel="noopener noreferrer"&gt;this article&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Benefits of dropping IE11 in your web app
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Use modern browser features (such as the ones listed above) out of the box. &lt;/li&gt;
&lt;li&gt;No need to support IE11 quirks. Ugly hacks to make edge cases work on IE11 can be removed, which leads to cleaner code that is easier to maintain.&lt;/li&gt;
&lt;li&gt;The JavaScript code that is served by your app in production can be optimized for modern standards, which means transpilers such as Babel have less features to polyfill. This can potentially reduce the size of your code bundle by as much as 30%!&lt;/li&gt;
&lt;li&gt;Simplify your testing and quality assurance process.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  How to phase out IE11
&lt;/h1&gt;

&lt;p&gt;As a developer, you can lead the discussion about whether or not to support IE11 with technical arguments mentioned above. Make dropping IE11 support a company goal! Put it on a roadmap! This is not just a development issue, other departments should also contribute and inform customers about the plan. As with most things in life: communication is key! &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Communicate the deadline for dropping IE support to your customers, so they can prepare if needed. Switching to different browser may not be an issue for them at all. The not so tech-savvy users may not be aware they are still using an outdated browser. Companies may have an outdated browser policy which only allows IE11 for legacy reasons (or no policy at all).&lt;/li&gt;
&lt;li&gt;Show a banner at the top of your app to warn the user if it detects IE11 usage.&lt;/li&gt;
&lt;li&gt;If possible, gather browser usage statistics for your app.&lt;/li&gt;
&lt;li&gt;Document any features of your app that require IE11 support on your Wiki/Confluence/README. Are these features still relevant or can they be refactored to modern JavaScript?&lt;/li&gt;
&lt;li&gt;Write a knowledge base article about the browsers your app supports and how users can switch to a safe alternative.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  How to drop IE11 support from your web app
&lt;/h1&gt;

&lt;p&gt;The following steps explain how to optimize your project to drop IE11 support. Sometimes we need to tell our build tools that they don't need to transpile code for older browsers. The production bundle will no longer include patches for missing features, which will save you some precious bytes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use browserslist
&lt;/h2&gt;

&lt;p&gt;If you are developing a web application, you are most likely using technologies such as &lt;a href="https://babeljs.io/" rel="noopener noreferrer"&gt;Babel&lt;/a&gt; with the &lt;a href="https://babeljs.io/docs/en/babel-preset-env" rel="noopener noreferrer"&gt;preset-env plugin&lt;/a&gt; and Webpack to bundle your code. You can set a &lt;code&gt;browserslist&lt;/code&gt; (&lt;a href="https://github.com/browserslist/browserslist" rel="noopener noreferrer"&gt;docs&lt;/a&gt;) property in &lt;code&gt;package.json&lt;/code&gt; (or &lt;code&gt;.browserslistrc&lt;/code&gt; file) to set the browsers you want to target:&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="nl"&gt;"browserslist"&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;span class="s2"&gt;"&amp;gt;0.2%"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"not dead"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"not IE 11"&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;You can customize the query using &lt;a href="https://browserslist.dev/?q=PjAuMiUgIGFuZCBub3QgZGVhZCBhbmQgbm90IGllIDEx" rel="noopener noreferrer"&gt;this tool&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Remove redundant Babel plugins
&lt;/h2&gt;

&lt;p&gt;Furthermore, some Babel plugins for ES6 language features can be removed from your project and from the babel configuration, if you were still using them. Check &lt;a href="https://babeljs.io/docs/en/plugins-list#es2015" rel="noopener noreferrer"&gt;this list&lt;/a&gt; and remove them from your project. &lt;/p&gt;

&lt;p&gt;Note: These plugins are all included in &lt;a href="https://babeljs.io/docs/en/babel-preset-env" rel="noopener noreferrer"&gt;preset-env&lt;/a&gt;, so chances are small you were using them. Still worth checking though.&lt;/p&gt;

&lt;h2&gt;
  
  
  Specific tool examples
&lt;/h2&gt;

&lt;p&gt;Most frameworks and tools will automatically use the &lt;code&gt;browserslist&lt;/code&gt; configuration. Just because your framework says it does not support IE11 doesn't mean the code it produces is optimized for ES6. Sometimes you still need to set the &lt;code&gt;browserslist&lt;/code&gt; yourself.&lt;/p&gt;

&lt;p&gt;Here is the behaviour of some of those tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://create-react-app.dev/docs/supported-browsers-features/#configuring-supported-browsers" rel="noopener noreferrer"&gt;Create React App&lt;/a&gt;: generates a &lt;code&gt;browserslist&lt;/code&gt; config for you, but you need adjust it to exclude IE11 (as above).&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://nextjs.org/docs/basic-features/supported-browsers-features" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt; supports IE11, &lt;em&gt;does not respect browserslist for JS (only CSS)!&lt;/em&gt; It seems you need a custom &lt;a href="https://nextjs.org/docs/advanced-features/customizing-babel-config" rel="noopener noreferrer"&gt;babel config&lt;/a&gt; and configure the &lt;a href="https://babeljs.io/docs/en/babel-preset-env#targets" rel="noopener noreferrer"&gt;preset-env targets option&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://remix.run/docs/en/v1/guides/browser-support" rel="noopener noreferrer"&gt;Remix&lt;/a&gt;: no IE11 support, promises progressive enhancement&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://angular.io/guide/browser-support" rel="noopener noreferrer"&gt;Angular&lt;/a&gt;: no IE11 support out of the box(?) 🎉&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/sveltejs/svelte/issues/558" rel="noopener noreferrer"&gt;Svelte&lt;/a&gt;: ???&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://cli.vuejs.org/guide/browser-compatibility.html#browser-compatibility" rel="noopener noreferrer"&gt;Vue 3&lt;/a&gt;: Configure browserslist as above. Also offers &lt;a href="https://cli.vuejs.org/guide/browser-compatibility.html#modern-mode" rel="noopener noreferrer"&gt;modern mode&lt;/a&gt; for differential serving.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://nuxtjs.org/docs/configuration-glossary/configuration-modern/" rel="noopener noreferrer"&gt;Nuxt&lt;/a&gt;: need to build with the &lt;code&gt;--modern&lt;/code&gt; option.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://emberjs.com/browser-support/" rel="noopener noreferrer"&gt;Ember 4&lt;/a&gt;: no IE11 support, set targets in &lt;code&gt;./config/targets.js&lt;/code&gt; using &lt;a href="https://github.com/babel/ember-cli-babel" rel="noopener noreferrer"&gt;ember-cli&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://webpack.js.org/configuration/target/" rel="noopener noreferrer"&gt;Webpack 5&lt;/a&gt;: You need to configure the &lt;code&gt;browserslist&lt;/code&gt; as above.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://vitejs.dev/guide/build.html#browser-compatibility" rel="noopener noreferrer"&gt;Vite&lt;/a&gt;: targets ES6 by default and uses a &lt;code&gt;browserslist&lt;/code&gt; query that does not target IE11 (so it works out of the box 🎉).&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Progressive enhancement for modern browsers, fallback for IE
&lt;/h1&gt;

&lt;p&gt;If you want to show at least &lt;em&gt;something&lt;/em&gt; to IE users, instead of an empty, broken page, you could use the &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag with a &lt;code&gt;nomodule&lt;/code&gt; attribute. This is only used by browsers that don't support &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules" rel="noopener noreferrer"&gt;JS modules&lt;/a&gt;, which happens to be IE!&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="c"&gt;&amp;lt;!-- Your ES6+ bundle for modern browsers --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"module"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"index.mjs"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- Fallback for IE --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;nomodule&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;No module support, could not load app&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;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;has-old-js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You could go down the path of creating one ES5 bundle for IE users and an ES6+ bundle for other browsers. This technique is called &lt;a href="https://johnstew.github.io/differential-serving/" rel="noopener noreferrer"&gt;differential serving&lt;/a&gt;. But now that IE11 is no longer supported, you should really ask yourself if this is worth the effort.&lt;/p&gt;

&lt;h1&gt;
  
  
  Detect IE11 using JavaScript
&lt;/h1&gt;

&lt;p&gt;Checking supported browser features:&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="o"&gt;!!&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MSInputMethodContext&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&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;documentMode&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Checking the user agent:&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="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userAgent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;MSIE&lt;/span&gt;&lt;span class="dl"&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;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appVersion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Trident/&lt;/span&gt;&lt;span class="dl"&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="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;So should your app still support IE? Unless you are working on a legacy app for a customer with specific needs, the answer is &lt;strong&gt;probably not&lt;/strong&gt;. Microsoft does not even support IE in its own &lt;a href="https://docs.microsoft.com/en-us/microsoftteams/limits-specifications-teams#browsers" rel="noopener noreferrer"&gt;Teams&lt;/a&gt; app. You should at least have a plan to move away from supporting IE and inform your customers about it.&lt;/p&gt;

&lt;p&gt;Ask yourself: What is the cost of supporting IE11, in terms of time spent on maintenance, fixing obscure bugs, code complexity, technical debt, negative performance impact, exceptions in the build process and even testing?&lt;/p&gt;

&lt;p&gt;It's time to move forward and start writing code for the modern web.&lt;/p&gt;

&lt;h1&gt;
  
  
  Sources
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://css-tricks.com/a-business-case-for-dropping-internet-explorer/" rel="noopener noreferrer"&gt;https://css-tricks.com/a-business-case-for-dropping-internet-explorer/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/doctolib/supporting-internet-explorer-11-in-2019-12dc1f1ebc3c" rel="noopener noreferrer"&gt;https://medium.com/doctolib/supporting-internet-explorer-11-in-2019-12dc1f1ebc3c&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://codeandchaos.medium.com/the-end-of-life-of-internet-explorer-11-12736f9ff75f" rel="noopener noreferrer"&gt;https://codeandchaos.medium.com/the-end-of-life-of-internet-explorer-11-12736f9ff75f&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/samthor/what-to-expect-when-you-re-expecting-to-drop-ie11-ifg"&gt;https://dev.to/samthor/what-to-expect-when-you-re-expecting-to-drop-ie11-ifg&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>browser</category>
      <category>javascript</category>
      <category>ie11</category>
    </item>
    <item>
      <title>Format numbers in the user's locale using vanilla JS</title>
      <dc:creator>Edwin</dc:creator>
      <pubDate>Thu, 07 Apr 2022 14:56:34 +0000</pubDate>
      <link>https://dev.to/ekeijl/intlnumberformat-playground-23gh</link>
      <guid>https://dev.to/ekeijl/intlnumberformat-playground-23gh</guid>
      <description>&lt;p&gt;JavaScript has a built-in language-sensitive number formatting feature, called &lt;code&gt;Intl.NumberFormat&lt;/code&gt;. This is useful if you want to format large numbers, use thousand separators, format currency values. It is &lt;a href="https://caniuse.com/?search=NumberFormat"&gt;supported&lt;/a&gt; by all evergreen browsers, except Internet Explorer.&lt;/p&gt;

&lt;h1&gt;
  
  
  Example
&lt;/h1&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;formatter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Intl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;NumberFormat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;de-DE&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="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;currency&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
     &lt;span class="na"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;EUR&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; 
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;formatter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;123456.789&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="c1"&gt;// expected output: "123.456,79 €"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Playground
&lt;/h1&gt;

&lt;p&gt;The &lt;code&gt;NumberFormat&lt;/code&gt; documentation on MDN is not very clear about the available options, so I decided to build a playground that allows you to customize the formatting options and immediately see the result for a list of locales and input numbers.&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/7or3uo"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;In the sidebar you'll find some presets that showcase interesting use cases. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: The features marked as V3 will be implemented in a future version of ECMAScript. Therefore, they may not yet be available in your browser yet. I will add a polyfill as soon as it is available.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Open the &lt;a href="https://7or3uo.csb.app/"&gt;Codesandbox&lt;/a&gt; fullscreen for easy editing. The playground is built in React using the &lt;a href="https://mantine.dev/"&gt;Mantine&lt;/a&gt; component library.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>intl</category>
      <category>frontend</category>
      <category>showdev</category>
    </item>
    <item>
      <title>React: automatic date formatting in translations (i18next + date-fns)</title>
      <dc:creator>Edwin</dc:creator>
      <pubDate>Sat, 19 Dec 2020 17:16:46 +0000</pubDate>
      <link>https://dev.to/ekeijl/react-automatic-date-formatting-in-translations-i18next-date-fns-8df</link>
      <guid>https://dev.to/ekeijl/react-automatic-date-formatting-in-translations-i18next-date-fns-8df</guid>
      <description>&lt;h1&gt;
  
  
  Update April 2022
&lt;/h1&gt;

&lt;p&gt;Since October '21, i18next has &lt;a href="https://www.i18next.com/translation-function/formatting" rel="noopener noreferrer"&gt;added support for formatting using native Intl API&lt;/a&gt; in &lt;a href="https://github.com/i18next/i18next/releases/tag/v21.3.0" rel="noopener noreferrer"&gt;version 21.3&lt;/a&gt;. You should probably check that out first, but I think this article can still be useful to you as it explains how to work with date-fns.&lt;/p&gt;

&lt;h1&gt;
  
  
  Intro
&lt;/h1&gt;

&lt;p&gt;In this article I will show you how to translate your React app in multiple languages and how to automatically format dates in the user's locale. &lt;/p&gt;

&lt;p&gt;Rendering a date using a localized format is important: for example, the US uses &lt;code&gt;MM/DD/YYYY&lt;/code&gt;, while some other countries use &lt;code&gt;DD/MM/YYYY&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We will be using React together with &lt;a href="https://www.i18next.com/" rel="noopener noreferrer"&gt;i18next&lt;/a&gt; and &lt;a href="https://date-fns.org/" rel="noopener noreferrer"&gt;date-fns&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/v27h6"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;The naive solution would be to handle both concepts of translation and date formatting separately:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;

&lt;span class="nf"&gt;render&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;&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="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;article.postedOn&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;MM/DD/YYYY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
   &lt;span class="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;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;The end result of this article is that we can pass a &lt;code&gt;Date&lt;/code&gt; object to our translation function and easily declare which date format we want to use:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="c1"&gt;// In our React component:&lt;/span&gt;
&lt;span class="nf"&gt;render&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;span&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;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;article.postedOn&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="na"&gt;date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&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;lt;&lt;/span&gt;&lt;span class="sr"&gt;/span&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// In our translation bundle:&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;article&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;postedOn&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;This article was posted on {{ date, short }}&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;And the user will see a message like: &lt;code&gt;This article was posted on 12/19/2020&lt;/code&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  React i18next
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://www.i18next.com/" rel="noopener noreferrer"&gt;i18next&lt;/a&gt; is a popular solution to manage translations in your app. I won't go into detail how to configure it for usage with React, I used &lt;a href="https://react.i18next.com/guides/quick-start" rel="noopener noreferrer"&gt;this guide&lt;/a&gt; as a setup for this article.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using i18next in your app
&lt;/h2&gt;

&lt;p&gt;The basic idea is that you can translate strings using a &lt;code&gt;t()&lt;/code&gt; function. You pass in a translation key and i18next will look up the translation in its bundle for the currently active locale:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useTranslation&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-i18next&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;MyComponent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useTranslation&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;span&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;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;messages.welcome&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/span&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  Translation bundles
&lt;/h2&gt;

&lt;p&gt;For each language that you support, you create a translation bundle as JSON and pass it to i18next:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&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;"en"&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;span class="nl"&gt;"translation"&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;span class="nl"&gt;"messages"&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;span class="nl"&gt;"welcome"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Welcome!"&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;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"nl"&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;span class="nl"&gt;"translation"&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;span class="nl"&gt;"messages"&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;span class="nl"&gt;"welcome"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Welkom!"&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;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;h2&gt;
  
  
  Setup i18next
&lt;/h2&gt;

&lt;p&gt;Install the library:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

npm install react-i18next i18next


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

&lt;/div&gt;

&lt;p&gt;Create a new module &lt;code&gt;i18next.js&lt;/code&gt; where you configure the library:&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;i18n&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;i18next&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;initReactI18next&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-i18next&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Here we import the bundle file as defined above&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;resources&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./translation.json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;i18n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;initReactI18next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// passes i18n down to react-i18next&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="nx"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;en&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

        &lt;span class="na"&gt;interpolation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// react already saves from xss&lt;/span&gt;
            &lt;span class="na"&gt;escapeValue&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="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;i18n&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;And simply import this file in your app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Interpolation
&lt;/h2&gt;

&lt;p&gt;It is common that you need to use a name or a date in your translated text. The position of the dynamic value in the translated string can vary between languages, so we use a template string with curly braces and pass the variable to the &lt;code&gt;t()&lt;/code&gt; function:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="nf"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;welcome&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="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;John&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// translation bundle:&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;welcome&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Welcome {{ name }}&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;This is called &lt;a href="https://www.i18next.com/translation-function/interpolation" rel="noopener noreferrer"&gt;interpolation&lt;/a&gt; in i18next.&lt;/p&gt;

&lt;h1&gt;
  
  
  Adding date-fns
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://date-fns.org" rel="noopener noreferrer"&gt;Date-fns&lt;/a&gt; is a modular library for working with dates in JS and a popular alternative to the monolithic &lt;a href="https://momentjs.com/" rel="noopener noreferrer"&gt;MomentJS&lt;/a&gt;. To install:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

npm install date-fns


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

&lt;/div&gt;
&lt;h1&gt;
  
  
  Automatically format dates with i18next
&lt;/h1&gt;

&lt;p&gt;In the &lt;code&gt;i18next.js&lt;/code&gt; file, we need to import some stuff from date-fns:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;format&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;formatDate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isDate&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;date-fns&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;en&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;nl&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;date-fns/locale&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// import all locales we need&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;locales&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;en&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;nl&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt; &lt;span class="c1"&gt;// used to look up the required locale&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Then add the following configuration to i18next:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="nx"&gt;interpolation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;format&lt;/span&gt;&lt;span class="p"&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="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lng&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="nf"&gt;isDate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;locale&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;locales&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;formatDate&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="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;locale&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;We simply check if the dynamic value is a date and then let date-fns format it. As the third &lt;code&gt;options&lt;/code&gt; parameter of &lt;code&gt;format()&lt;/code&gt;, we tell date-fns which locale object to use.&lt;/p&gt;

&lt;p&gt;If we now pass a &lt;code&gt;Date&lt;/code&gt; object in the options of our &lt;code&gt;t()&lt;/code&gt; function, it will automatically be formatted. We can set the format inside the curly braces in the translation bundle:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&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;"postedOn"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Posted on {{ date, MM/DD/YYYY }}"&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;As I explained above, not every language uses the same date format. Luckily date-fns provides locale aware date formats:&lt;/p&gt;

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

&lt;p&gt;So instead of &lt;code&gt;MM/DD/YYYY&lt;/code&gt; we should use &lt;code&gt;P&lt;/code&gt;. Don't forget to pass in the &lt;code&gt;locale&lt;/code&gt; option to the &lt;a href="https://date-fns.org/v2.16.1/docs/format#arguments" rel="noopener noreferrer"&gt;format function&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To make our date formats easy to work with, we can predefine some formatters that we would like to use in our app:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&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="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lng&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="nf"&gt;isDate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;locale&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;locales&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;lng&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;format&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;short&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;formatDate&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;P&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;locale&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;format&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;long&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;formatDate&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;PPPP&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;locale&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;format&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;relative&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;formatRelative&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;locale&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;format&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ago&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;formatDistance&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;addSuffix&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="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;formatDate&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="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;locale&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;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Here we use powerful date-fns functions such as &lt;a href="https://date-fns.org/v2.16.1/docs/formatDistance" rel="noopener noreferrer"&gt;formatDistance&lt;/a&gt; and &lt;a href="https://date-fns.org/v2.16.1/docs/formatRelative" rel="noopener noreferrer"&gt;formatRelative&lt;/a&gt; to create a human readable representation of a date in the past.&lt;/p&gt;

&lt;p&gt;And now we can simply choose from a set of formatters in our translation bundle:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&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;"postedOn"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Posted on {{ date, short }}"&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useTranslation&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-i18next&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;t&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useTranslation&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// 'Posted on 11/10/2021'&lt;/span&gt;
&lt;span class="nf"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;postedOn&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="na"&gt;date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&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;

</description>
      <category>react</category>
      <category>i18next</category>
      <category>datefns</category>
      <category>translation</category>
    </item>
    <item>
      <title>Simple React fade animation hook</title>
      <dc:creator>Edwin</dc:creator>
      <pubDate>Sat, 28 Nov 2020 14:06:00 +0000</pubDate>
      <link>https://dev.to/ekeijl/simple-react-fade-animation-hook-5dp8</link>
      <guid>https://dev.to/ekeijl/simple-react-fade-animation-hook-5dp8</guid>
      <description>&lt;p&gt;Hey you! Yea you! Do you need a quick and simple fade animation on your React component? Don't feel like installing a library? &lt;br&gt;
Then let's go! 💨💨💨&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/3gjkn"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h1&gt;
  
  
  Show me the code already! 👀
&lt;/h1&gt;

&lt;p&gt;The hook returns &lt;code&gt;[isVisible, setVisible, fadeProps]&lt;/code&gt;, just like &lt;code&gt;useState()&lt;/code&gt; hook, but you also need to set the &lt;code&gt;fadeProps&lt;/code&gt; on the element you want to fade.&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MyFadingComponent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Just like useState() hook, the fadeProps go on the fading DOM element&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;setVisible&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fadeProps&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useFade&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// You can use isVisible to mount/unmount the component!&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&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="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;setVisible&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isVisible&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Toggle visibility&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="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;isVisible&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;fadeProps&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Now you see me...&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  And the hook! 🎣
&lt;/h1&gt;

&lt;p&gt;It uses &lt;code&gt;onAnimationEnd&lt;/code&gt; to delay setting the &lt;code&gt;isVisible&lt;/code&gt; state to &lt;code&gt;false&lt;/code&gt;, which allows the animation to finish before the component unmounts!&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;useFade&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;initial&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;show&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setShow&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;initial&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;setVisible&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;show&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Update visibility when show changes&lt;/span&gt;
    &lt;span class="nx"&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="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;show&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;setVisible&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;show&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="c1"&gt;// When the animation finishes, set visibility to false&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;onAnimationEnd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;show&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;setVisible&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;animation&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;show&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fadeIn&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fadeOut&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; .3s`&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="c1"&gt;// These props go on the fading DOM element&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fadeProps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;onAnimationEnd&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;isVisible&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setShow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fadeProps&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;
  
  
  Styles 💅
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@keyframes&lt;/span&gt; &lt;span class="n"&gt;fadeIn&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="err"&gt;100&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;@keyframes&lt;/span&gt; &lt;span class="n"&gt;fadeOut&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="err"&gt;100&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  What's the point? 🙄
&lt;/h1&gt;

&lt;p&gt;If we would use the &lt;code&gt;useState()&lt;/code&gt; hook and then apply the state with an expression like &lt;code&gt;isVisible &amp;amp;&amp;amp; &amp;lt;Component /&amp;gt;&lt;/code&gt;, our component will unmount before the CSS animation has finished, which is not what we want! The &lt;code&gt;useFade()&lt;/code&gt; hook delays unmounting until the animation is finished.&lt;/p&gt;

&lt;h1&gt;
  
  
  What's cool about this? 😎
&lt;/h1&gt;

&lt;p&gt;The syntax is just like &lt;code&gt;useState()&lt;/code&gt;, you can simply use an &lt;code&gt;isVisible &amp;amp;&amp;amp; &amp;lt;Component /&amp;gt;&lt;/code&gt; expression to mount/unmount the component. &lt;/p&gt;

&lt;p&gt;Here's how you do it with &lt;a href="https://codesandbox.io/s/thirsty-pasteur-m77l2vp00x?from-embed"&gt;React Transition Group&lt;/a&gt;, you need a wrapper component and wire the enter/exit animation to the state yourself, yuck! &lt;a href="https://codesandbox.io/s/fade-between-pages-2d7y7"&gt;Framer Motion&lt;/a&gt; and &lt;a href="https://www.react-spring.io/docs/hooks/use-transition"&gt;React Spring&lt;/a&gt; are similar.&lt;/p&gt;

&lt;h1&gt;
  
  
  Room for improvement (please help!)
&lt;/h1&gt;

&lt;p&gt;Toggling between two elements does not really work at the moment:&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="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;setVisible&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fromProps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;toProps&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useFade&lt;/span&gt;&lt;span class="p"&gt;();&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ComponentA&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;fromProps&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;:&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ComponentB&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;toProps&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'm trying to pass the opposite fade animation to &lt;code&gt;ComponentB&lt;/code&gt;, but can't figure out how to get it right. If you have an idea, let me know!&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/3gjkn?module=/src/Example2.js&amp;amp;initialpath=/example2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>react</category>
      <category>animation</category>
      <category>hook</category>
    </item>
    <item>
      <title>Why I prefer WebStorm over VSCode for web dev</title>
      <dc:creator>Edwin</dc:creator>
      <pubDate>Sat, 10 Oct 2020 16:34:31 +0000</pubDate>
      <link>https://dev.to/ekeijl/why-i-prefer-webstorm-over-vscode-for-web-dev-3kjm</link>
      <guid>https://dev.to/ekeijl/why-i-prefer-webstorm-over-vscode-for-web-dev-3kjm</guid>
      <description>&lt;p&gt;The reason is short and simple: WebStorm fits my workflow better. It shows me information that I need when I need it, which improves my productivity.&lt;/p&gt;

&lt;p&gt;I'm not sponsored by JetBrains to write this, I just got the feeling that people regard VSCode as the 'go-to' IDE for web development these days. I hope this article can give you some insight into the stuff you are missing out on if you have never used Webstorm before.&lt;/p&gt;

&lt;p&gt;Recently I tried switching from WebStorm to VSCode for work when we started working from home, but I felt something was missing, even with plugins.&lt;/p&gt;

&lt;h1&gt;
  
  
  In control of version control
&lt;/h1&gt;

&lt;p&gt;WebStorm makes me feel in control of the changes that I'm making to the project that I'm working on. The differences between both IDEs are small, but they do add up. Both have great features for finding, editing and refactoring code. The main area where the developer experience of VSCode is lacking is version control.&lt;/p&gt;

&lt;p&gt;Both editors show you which files were changed/added, but WebStorm offers some very useful additional features and a better overall developer experience:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fos1ask088ef7ctc2qikp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fos1ask088ef7ctc2qikp.png" alt="Change Log panel in Webstorm"&gt;&lt;/a&gt;&lt;/p&gt;
Change Log of the React repository in WebStorm



&lt;ul&gt;
&lt;li&gt;I can organize my local changes in &lt;em&gt;multiple changesets&lt;/em&gt;, which makes it easy to split my changes per feature or between client/server or isolate some experimental code that I want to revert later on.&lt;/li&gt;
&lt;li&gt;Both IDEs have great tools for comparing versions of a file (diff tools).&lt;/li&gt;
&lt;li&gt;WebStorm comes with a fantastic &lt;em&gt;change log panel&lt;/em&gt;. It allows me to easily see the changed files of each commit and to get an overview of the state of all branches in the project. In VSCode, you need a plugin such as &lt;a href="https://marketplace.visualstudio.com/items?itemName=donjayamanne.githistory" rel="noopener noreferrer"&gt;Git History&lt;/a&gt; to get the same overview, but it's just not as nice.&lt;/li&gt;
&lt;li&gt;Fixing mistakes in my commits (that have not yet been pushed) is as simple as right clicking the actual commit and either undoing or rewording the commit message. It's the tiny things like these that speed up my work.&lt;/li&gt;
&lt;li&gt;WebStorm has a dedicated panel for "&lt;em&gt;shelves&lt;/em&gt;", which allows you to temporarily store uncommited code (useful for working with temporary changes). It's easy to manage multiple shelves and compare changes to the current version. VSCode only offers a basic implementation of Git stash.&lt;/li&gt;
&lt;li&gt;You can bundle up your changes into a &lt;em&gt;patch file&lt;/em&gt; and send it to someone else or import it in another project, without checking in to version control. This should not be your standard process, but every now and then this can save a bit of time.&lt;/li&gt;
&lt;li&gt;Finally, when you are fixing a merge conflict, the 'magic wand' tool can automatically resolve all changes that do not cause any conflicts, so you can focus on the actual issues.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  VSCode
&lt;/h1&gt;

&lt;p&gt;One can argue that there are tons of plugins available for VSCode that can fill up these gaps or you could use some external app like &lt;a href="https://www.sourcetreeapp.com/" rel="noopener noreferrer"&gt;Sourcetree&lt;/a&gt; or &lt;a href="https://www.gitkraken.com/" rel="noopener noreferrer"&gt;Git Kraken&lt;/a&gt;, but that's the whole point: WebStorm feels like a complete package right out of the box, I never have to leave my IDE during the whole development process (OK, just for debugging in my browser).&lt;/p&gt;

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

&lt;p&gt;I prefer working with WebStorm over VSCode, because it speeds up my work and has great features that show me useful information when I need it most. VSCode feels lacking in this aspect.&lt;/p&gt;

&lt;p&gt;It does come at a price for individual developers, but you should ask yourself if such a small investment is worth the time you save in everyday tasks such as version control. However, WebStorm is free for students of most universities.&lt;/p&gt;

</description>
      <category>ide</category>
      <category>productivity</category>
      <category>webstorm</category>
      <category>vscode</category>
    </item>
    <item>
      <title>Using HTML Frames in React</title>
      <dc:creator>Edwin</dc:creator>
      <pubDate>Tue, 01 Sep 2020 00:12:46 +0000</pubDate>
      <link>https://dev.to/ekeijl/rendering-react-components-inside-a-frame-5gn1</link>
      <guid>https://dev.to/ekeijl/rendering-react-components-inside-a-frame-5gn1</guid>
      <description>&lt;p&gt;Yes, you read that correctly: &lt;code&gt;&amp;lt;frame&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You know, that funny HTML tag that disappeared from the face of the internet at about the same time as &lt;code&gt;&amp;lt;marquee&amp;gt;&lt;/code&gt;, GeoCities, guest book pages, animated "UNDER CONSTRUCTION" gifs and the rest of Web 1.0. &lt;/p&gt;

&lt;p&gt;I wanted to see if it is possible to render a React component to a HTML &lt;code&gt;&amp;lt;frame&amp;gt;&lt;/code&gt;. Here is the end result:&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/ehuq3"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h1&gt;
  
  
  Frames 101
&lt;/h1&gt;

&lt;p&gt;Documentation about frames is incredibly hard to find, so here's a quick refresher on how they work:&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="cp"&gt;&amp;lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN"
   "http://www.w3.org/TR/html4/frameset.dtd"&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;HTML&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;HEAD&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;TITLE&amp;gt;&lt;/span&gt;A simple frameset document&lt;span class="nt"&gt;&amp;lt;/TITLE&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/HEAD&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;FRAMESET&lt;/span&gt; &lt;span class="na"&gt;cols=&lt;/span&gt;&lt;span class="s"&gt;"20%, 80%"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;FRAME&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"menu"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"menu.html"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;FRAME&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"content"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"content.html"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/FRAMESET&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/HTML&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Frames need to be inside a &lt;code&gt;&amp;lt;frameset&amp;gt;&lt;/code&gt;, which replaces the &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; tag and defines how the frames are divided into rows and/or columns. The &lt;code&gt;rows&lt;/code&gt; and &lt;code&gt;cols&lt;/code&gt; attributes can take percentage values, absolute numbers (as pixels) or an asterisk to tell it to take the remainder of the space. Framesets can be nested inside each other, in order to create a layout with multiple dividers.&lt;/p&gt;

&lt;p&gt;Using frames and framesets, you can create layouts where parts of the page remain visible (such as the header and navigation menu), while the main content of the website is replaced.&lt;/p&gt;

&lt;p&gt;A frame has a &lt;code&gt;name&lt;/code&gt; attribute, that can be used in hyperlinks to replace the content of that frame:&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;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"page2.html"&lt;/span&gt; &lt;span class="na"&gt;target=&lt;/span&gt;&lt;span class="s"&gt;"content"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Go to page 3&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Where did they go?
&lt;/h1&gt;

&lt;p&gt;I remember building websites using frames years ago and I was wondering what happened to them. The short answer is that the &lt;code&gt;&amp;lt;frame&amp;gt;&lt;/code&gt; element was introduced in HTML version 4.0 (1999) and deprecated in HTML5 (2014).&lt;/p&gt;

&lt;p&gt;Frames &lt;a href="https://www.nngroup.com/articles/why-frames-suck-most-of-the-time/"&gt;break a fundamental principle of the web&lt;/a&gt;, namely that a page is a single unit of information that can be viewed and clicked/navigated to using a URL. The information that is shown inside frames, however, depends on a sequence of navigation actions by the user.&lt;/p&gt;

&lt;p&gt;Perhaps the same thing can be said about modern Single Page Applications with their dynamic content in modal screens, sidebars and infinite scrolling lists, but that's a discussion for a different time.&lt;/p&gt;

&lt;p&gt;It's the year 2020, which means you should use CSS to lay out the information on your webpage. Now that &lt;a href="https://caniuse.com/#feat=flexbox"&gt;flexbox&lt;/a&gt; is supported by all major browsers, it is super easy to create these type of layouts with &lt;a href="https://flexbox.ninja/demos/holy-grail-layout/"&gt;a bit of CSS and a few semantic HTML5 elements&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;What &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/frame"&gt;Mozilla&lt;/a&gt; says about frames:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Deprecated&lt;br&gt;
This feature is no longer recommended. Though some browsers might still support it, it may have already been removed from the relevant web standards, may be in the process of being dropped, or may only be kept for compatibility purposes. Avoid using it, and update existing code if possible; see the compatibility table at the bottom of this page to guide your decision. Be aware that this feature may cease to work at any time.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Just because the internet says we should not use frames, does not mean it is forbidden to do so, right?&lt;/p&gt;

&lt;h1&gt;
  
  
  Using frames with React
&lt;/h1&gt;

&lt;p&gt;I got inspired by the &lt;a href="https://github.com/ryanseddon/react-frame-component"&gt;react-frame-component&lt;/a&gt; library that allows you to render a React app inside an &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; and I was curious if I could do the same thing for &lt;code&gt;&amp;lt;frame&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I wanted to create something similar to the react-frame-component library, where you can pass in any React component to a &lt;code&gt;&amp;lt;Frame&amp;gt;&lt;/code&gt; component, which renders it inside its document. These components are abstractions that resemble the original frame and frameset 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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Frameset&lt;/span&gt; &lt;span class="na"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"20%, 80%"&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="nc"&gt;Frame&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"nav"&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="nc"&gt;Menu&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="nc"&gt;Frame&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="nc"&gt;Frame&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"main"&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="nc"&gt;Home&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="nc"&gt;Frame&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="nc"&gt;Frameset&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of a &lt;code&gt;src&lt;/code&gt; attribute of the frame that points to a page, you pass the child component that you want to render. It should also accept the &lt;a href="https://www.w3.org/TR/html401/present/frames.html#h-16.2.2"&gt;original frame attributes&lt;/a&gt;, such as options to disable the frame border and (dis)allow resizing. The &lt;code&gt;name&lt;/code&gt; attribute is used to target the frame for navigation purposes (see below).&lt;/p&gt;

&lt;h2&gt;
  
  
  Frameset
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;&amp;lt;frameset&amp;gt;&lt;/code&gt; replaces the &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; tag, which is kind of an issue in React, since rendering a React app requires a DOM element container to render the app into. In the end, I decided to let the &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt; be the root of the React app, which means the app won't have a &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; anymore (no styling, no title). &lt;/p&gt;

&lt;p&gt;I could not pass the &lt;code&gt;cols&lt;/code&gt; and &lt;code&gt;rows&lt;/code&gt; to the &lt;code&gt;&amp;lt;frameset&amp;gt;&lt;/code&gt; element as props using React, so I have to use a ref and set the attributes using JS. I still don't know why.&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="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;Frameset&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Component&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&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;ref&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createRef&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;componentDidMount&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cols&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;ref&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&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;cols&lt;/span&gt;&lt;span class="dl"&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;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cols&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rows&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;ref&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&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;rows&lt;/span&gt;&lt;span class="dl"&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;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rows&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;render&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;frameset&lt;/span&gt; &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&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;ref&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&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;frameset&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;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;React will also show us a nasty warning that we cannot nest frameset elements, which I believe is incorrect:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Warning: validateDOMNesting(...): &amp;lt;frameset&amp;gt; cannot appear as a child of &amp;lt;frameset&amp;gt;.
    in frameset (at Frameset.js:45)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Frame
&lt;/h2&gt;

&lt;p&gt;Implementing the frame itself is a bit more complex. We need to access the frame's document and render the React component to it.&lt;/p&gt;

&lt;p&gt;The document can easily be accessed using the &lt;a href="https://www.w3schools.com/jsref/prop_frame_contentdocument.asp"&gt;contentDocument&lt;/a&gt;  property.&lt;/p&gt;

&lt;p&gt;The react-frame-component library used &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/write"&gt;document.write()&lt;/a&gt; to overwrite the content of the document. This works, but it is &lt;a href="https://web.dev/no-document-write/"&gt;not recommended&lt;/a&gt; by modern browsers:&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;doc&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;frameRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contentDocument&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text/html&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;replace&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;!DOCTYPE html&amp;gt;&amp;lt;html&amp;gt;&amp;lt;head&amp;gt;&amp;lt;/head&amp;gt;&amp;lt;body style="margin:0"&amp;gt;&amp;lt;div id="root"&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I tried simply rendering a new React component tree to the body of the contentDocument, but then the page briefly displayed the content and disappeared again, resulting in an empty page. Waiting for the onLoad event of the frame is not possible using React, but a delayed render using &lt;code&gt;setTimeout()&lt;/code&gt; seemed to do the trick. &lt;/p&gt;

&lt;p&gt;React again complained, this time that we should not render the app directly to the document body, so I manually inserted a container element. Then, we can render our component to this container on every &lt;code&gt;componentDidUpdate()&lt;/code&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="nx"&gt;ReactDOM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;render&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;props&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rootRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Replacing a frame's content
&lt;/h2&gt;

&lt;p&gt;I ended up with a component that renders a component to a target frame, similar to the &lt;code&gt;&amp;lt;a target="content"&amp;gt;&lt;/code&gt; in HTML:&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt; &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"main"&lt;/span&gt; &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Home&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Go Home&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To make this work across component trees, I used a React Context inside each &lt;code&gt;&amp;lt;Frameset&amp;gt;&lt;/code&gt; component, that keeps track of which component is rendered inside each frame by it's name and a function to replace the component of a target frame.&lt;/p&gt;

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

&lt;p&gt;The fact that every frame has its own document completely breaks importing styles in components that are rendered inside a frame. I came up with a custom &lt;code&gt;useStyle()&lt;/code&gt; hook that adds a stylesheet from the public assets directory to the frame's document. Not pretty, but good enough for this project.&lt;/p&gt;

&lt;h2&gt;
  
  
  End result
&lt;/h2&gt;

&lt;p&gt;The basic functionality of HTML frames was successfully implemented. It is possible to nest framesets and create a layout with multiple dividers. The implementation required some hacks and I really doubt sharing state across components works. I have not even tested it in production mode, but it was never meant to be used in production anyway.&lt;/p&gt;

&lt;p&gt;The full app is here:&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/ehuq3?view=editor"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>react</category>
      <category>html</category>
      <category>frames</category>
    </item>
    <item>
      <title>Fallout hacking minigame in JS</title>
      <dc:creator>Edwin</dc:creator>
      <pubDate>Fri, 26 Jun 2020 17:26:33 +0000</pubDate>
      <link>https://dev.to/ekeijl/fallout-hacking-minigame-in-js-2dbk</link>
      <guid>https://dev.to/ekeijl/fallout-hacking-minigame-in-js-2dbk</guid>
      <description>&lt;p&gt;Here is a follow-up of my completely pointless but incredibly fun side-project to &lt;a href="https://dev.to/ekeijl/retro-crt-terminal-screen-in-css-js-4afh"&gt;create a retro CRT screen in JS and CSS&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This time I decided to build the &lt;a href="https://www.youtube.com/watch?v=jDJHtLCHuAg"&gt;hacking minigame from the Fallout game series&lt;/a&gt;, which was also the inspiration to start this project in the first place.&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/tlijm?module=%2Fcommands%2Ffallout.js&amp;amp;initialpath=%2F%3Fcommand%3Dfallout"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Check the &lt;a href="https://codesandbox.io/embed/crt-terminal-in-css-js-tlijm?fontsize=14&amp;amp;hidenavigation=1&amp;amp;initialpath=%3Fcommand%3Dfallout&amp;amp;module=%2Fcommands%2Ffallout.js&amp;amp;theme=dark"&gt;full Sandbox&lt;/a&gt; for a bigger screen.&lt;/p&gt;

&lt;p&gt;The goal of the game is to find the password that is hidden in this garbled mess of characters. You can click a word or move with the arrow keys and use the enter key to confirm. Incorrect guesses will result in a hint indicating how many letters the password has in common with your guess.&lt;/p&gt;

&lt;h1&gt;
  
  
  Some of the stuff I've learned
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Hexadecimal numbers (&lt;code&gt;0xffff&lt;/code&gt;) can be used as regular numbers in JS, so you can easily add and subtract them. Seems obvious, just never thought about it.&lt;/li&gt;
&lt;li&gt;The CSS &lt;code&gt;ch&lt;/code&gt; unit (&lt;a href="https://meyerweb.com/eric/thoughts/2018/06/28/what-is-the-css-ch-unit/"&gt;width of one character&lt;/a&gt;) combined with a &lt;a href="https://fonts.google.com/specimen/VT323?selection.family=VT323&amp;amp;sidebar.open"&gt;monospace font&lt;/a&gt; is super useful for building these kind of text based interfaces, where I want to have a maximum of exactly 80 characters on every line.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://css-tricks.com/almanac/properties/c/columns/"&gt;CSS columns&lt;/a&gt; is something I rarely use, but works perfectly here. The &lt;code&gt;column-fill: auto&lt;/code&gt; property was crucial for filling columns one by one, instead of in a balanced manner.&lt;/li&gt;
&lt;li&gt;To word-break the passwords and highlight them over multiple lines, I first tried putting a password in a single span with the  &lt;code&gt;word-break: break-all&lt;/code&gt; property, but later switched to one &lt;code&gt;&amp;lt;span&amp;gt;&lt;/code&gt; per character, since that makes the whole thing easier to control using JS and also easier to position. I then assign some &lt;code&gt;data&lt;/code&gt; attributes to the span to keep track of which word is being clicked and highlighted.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Any cool ideas?
&lt;/h1&gt;

&lt;p&gt;What should I build next? Know any other games with terminal screens like these? Let me know! 👇👇👇&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>javascript</category>
      <category>css</category>
      <category>sideprojects</category>
    </item>
    <item>
      <title>Retro CRT terminal screen in CSS + JS</title>
      <dc:creator>Edwin</dc:creator>
      <pubDate>Fri, 21 Feb 2020 00:38:17 +0000</pubDate>
      <link>https://dev.to/ekeijl/retro-crt-terminal-screen-in-css-js-4afh</link>
      <guid>https://dev.to/ekeijl/retro-crt-terminal-screen-in-css-js-4afh</guid>
      <description>&lt;p&gt;As a fun project, I decided to create a terminal interface that has the look and feel of an old CRT monitor. &lt;/p&gt;

&lt;p&gt;The terminal is responsive, but it might be easier to work with on a larger screen (and it will provide a hint for the login).&lt;/p&gt;

&lt;p&gt;Now, it's time to boot that terminal! &lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/tlijm"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h1&gt;
  
  
  Goals
&lt;/h1&gt;

&lt;p&gt;My goal is to build the whole thing using modern &lt;a href="https://caniuse.com/#feat=es6" rel="noopener noreferrer"&gt;ECMAScript 6&lt;/a&gt; features (without any transpiler such as &lt;a href="https://babeljs.io/" rel="noopener noreferrer"&gt;Babel&lt;/a&gt;). Sorry Internet Explorer, it's time for you to retire.&lt;/p&gt;

&lt;p&gt;During this project, I learned about a lot of topics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ES6 features such as modules, dynamic import and async/await&lt;/li&gt;
&lt;li&gt;CSS border-image, background gradients, animation and variables&lt;/li&gt;
&lt;li&gt;JavaScript Audio and SpeechSynthesis APIs&lt;/li&gt;
&lt;li&gt;Handling DOM elements using pure JS&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is too much going on to do a full tutorial, but in this article I'll explain the most important parts. Later on, I might do a follow up article about the details, such as individual components and how I organized the project. In the examples I often leave out some of the final code for clarity, but you can always view the source on Codesandbox.&lt;/p&gt;

&lt;h1&gt;
  
  
  Inspiration
&lt;/h1&gt;

&lt;p&gt;I drew most of my inspiration for this project from the Fallout 3 game, where you can "hack" your way into computers by playing a mini game on one of these terminals:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/jDJHtLCHuAg"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h1&gt;
  
  
  Skeuomorphisms
&lt;/h1&gt;

&lt;p&gt;Mimicing attributes of real life objects (such as the material or shape) in a design is called a &lt;a href="https://en.wikipedia.org/wiki/Skeuomorph" rel="noopener noreferrer"&gt;skeuomorphism&lt;/a&gt;. The reasoning is that by making a design look like an object that the user is familiar with, it might become easier to understand. Apple used it a lot in their apps, such as the book store apps that showed your books on a 'real' shelf or a compass app that showed a spinning compass pointing to the direction you are facing.&lt;/p&gt;

&lt;p&gt;This style has fallen in to disuse mostly due to the popularity of flat design, where extreme minimalism seems to be the norm. Most software still contains some skeuomorphisms though. For example, the simple &lt;em&gt;unstyled&lt;/em&gt; HTML &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; on a webpage will resemble a hardware button, which should give the user a clue that this element can be pressed. Navigational tabs look like a physical tabbed folder. &lt;/p&gt;

&lt;p&gt;Another great example that I recently encountered is this polaroid camera:&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/fossheim" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&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%2Fuser%2Fprofile_image%2F162343%2F44361076-7f51-4b84-8c99-86c7e8824249.jpeg" alt="fossheim"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/fossheim/polaroid-camera-in-css-44lc" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;📸✨ Polaroid Camera In CSS&lt;/h2&gt;
      &lt;h3&gt;Sarah ・ Jan 31 '20&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#codepen&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#css&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#html&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;h1&gt;
  
  
  Coding a CRT
&lt;/h1&gt;

&lt;p&gt;So how do we make our CRT resemble the real deal? We're gonna need a few parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://youtu.be/3BJU2drrtCM?t=60" rel="noopener noreferrer"&gt;Scanlines&lt;/a&gt;, the visual pattern of alternating horizontal lines that this type of monitor used to have.&lt;/li&gt;
&lt;li&gt;A huge rounded bezel, to make it look like one of those tiny portable TV sets.&lt;/li&gt;
&lt;li&gt;Some buttons, such as a power switch. I feel that manually switching on the device and actually seeing the device boot increases the immersion of the whole experience.&lt;/li&gt;
&lt;li&gt;A text based interface where the user can type in commands.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Building the screen 📺
&lt;/h2&gt;

&lt;p&gt;The basic HTML is pretty simple, it's just a &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; for each part:&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="c"&gt;&amp;lt;!-- the actual device --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"monitor"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- the rounded edge near the glass --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"bezel"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="c"&gt;&amp;lt;!-- the overlay and horizontal pattern --&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"crt"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"off"&lt;/span&gt; &lt;span class="na"&gt;onClick=&lt;/span&gt;&lt;span class="s"&gt;"handleClick(event)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; 
            &lt;span class="c"&gt;&amp;lt;!-- slowly moving scanline --&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;"scanline"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
            &lt;span class="c"&gt;&amp;lt;!-- the input and output --&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;"terminal"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I might cover the button controls in a future article.&lt;/p&gt;

&lt;h3&gt;
  
  
  The scanlines
&lt;/h3&gt;

&lt;p&gt;The horizontal black and semi-transparent lines from this &lt;a href="https://codepen.io/meduzen/pen/zxbwRV" rel="noopener noreferrer"&gt;Codepen&lt;/a&gt; seemed to do the trick:&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="nf"&gt;#crt&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="s1"&gt;" "&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;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="m"&gt;0&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="m"&gt;0&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="m"&gt;0&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="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;linear-gradient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="nb"&gt;bottom&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;16&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="m"&gt;50%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;rgba&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="m"&gt;0&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="m"&gt;0.25&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="m"&gt;50%&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nl"&gt;background-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt; &lt;span class="m"&gt;8px&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;2&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="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;:before&lt;/code&gt; pseudo class, combined with &lt;code&gt;position: absolute&lt;/code&gt;, allows us to overlay the line pattern on top of the element. The linear-gradient fills the background for the top half with an opaque dark line and the bottom half with a semi-transparent black. The &lt;code&gt;background-size&lt;/code&gt; property makes it full width and 8px high, so each individual line becomes 4px. This background is repeated vertically to create the alternating line pattern.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://aleclownes.com/2017/02/01/crt-display.html" rel="noopener noreferrer"&gt;This article&lt;/a&gt; describes a way to create a really realistic scanline pattern, which even includes a &lt;em&gt;screen door effect&lt;/em&gt;: a mesh-like appearance where you can see the gaps between pixels on the screen. This causes the screen to really flicker, which was very straining on my eyes, so I decided not to use that. I did use the color separation effect for the text, which adds an animated text-shadow to the terminal text which makes the text appear to move around a bit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@keyframes&lt;/span&gt; &lt;span class="n"&gt;textShadow&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;text-shadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.4389924193300864px&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="n"&gt;rgba&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="m"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="m"&gt;-0.4389924193300864px&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;255&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="m"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;3px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="err"&gt;5&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;text-shadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2.7928974010788217px&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="n"&gt;rgba&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="m"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="m"&gt;-2.7928974010788217px&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;255&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="m"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;3px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c"&gt;/** etc */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then there is also a scanline moving over the screen from top to bottom every ten seconds. It uses a similar, but larger linear-gradient and an animation to make it move from top to bottom.&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;.scanline&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;100%&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;100px&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;8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;linear-gradient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="m"&gt;0deg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;rgba&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="m"&gt;0&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="m"&gt;0&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="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="m"&gt;10%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;rgba&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="m"&gt;0&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="m"&gt;0.1&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="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.1&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;bottom&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="nl"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;scanline&lt;/span&gt; &lt;span class="m"&gt;10s&lt;/span&gt; &lt;span class="n"&gt;linear&lt;/span&gt; &lt;span class="n"&gt;infinite&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 animation is out of view for 80% of the time and moves from top to bottom in the remaining 20%:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@keyframes&lt;/span&gt; &lt;span class="n"&gt;scanline&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="o"&gt;%&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="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="err"&gt;80&lt;/span&gt;&lt;span class="o"&gt;%&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="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="err"&gt;100&lt;/span&gt;&lt;span class="o"&gt;%&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="m"&gt;0%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The bezel 🖵
&lt;/h3&gt;

&lt;p&gt;To create the rounded edge, I use a &lt;a href="https://css-tricks.com/almanac/properties/b/border-image/" rel="noopener noreferrer"&gt;&lt;em&gt;border&lt;/em&gt;-image&lt;/a&gt;, a CSS property that I have never even heard of before! The idea is that you create a background image that is sliced up automatically into several regions, one for each edge and corner.&lt;/p&gt;

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

&lt;p&gt;You can indicate how much of the image is actually used by the &lt;em&gt;unitless&lt;/em&gt; &lt;code&gt;border-image-slice&lt;/code&gt; property. It uses the value as pixels for raster images and as percentage for SVG. In our case, we want 30px from the edge. Defining the &lt;code&gt;border: 30px solid transparent&lt;/code&gt; property seemed necessary to make it look OK in Android Chrome.&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="nf"&gt;#screen&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="nl"&gt;width&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="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;67.5vmin&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="m"&gt;30px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="nb"&gt;transparent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;border-image-source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sx"&gt;url(./bezel.png)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;border-image-slice&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt; &lt;span class="n"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;border-image-outset&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;overflow&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;p&gt;Your browser will then automagically use the border image and scale the middle sections for varying width and height of the element. ✨&lt;/p&gt;

&lt;h2&gt;
  
  
  Screens
&lt;/h2&gt;

&lt;p&gt;To create an experience where the user can interact with the terminal and have some screens where all the output is automatic and others where there is alternating input/output, I created one function for each of the screens:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;boot - the start-up sequence&lt;/li&gt;
&lt;li&gt;login - a &lt;em&gt;very secure&lt;/em&gt; authentication mechanism&lt;/li&gt;
&lt;li&gt;main - where the user can type commands&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Boot
&lt;/h3&gt;

&lt;p&gt;The boot screen just outputs a lot of text on the screen. To achieve this, I created a &lt;code&gt;type()&lt;/code&gt; function, which returns a &lt;a href="https://developer.mozilla.org/nl/docs/Web/JavaScript/Reference/Global_Objects/Promise" rel="noopener noreferrer"&gt;promise&lt;/a&gt; that resolves when the typing animation is finished. It is crucial to make it an asynchronous function, because we want to wait for the typing animation to complete before we let the user type his input. How the function works is explained further below.&lt;/p&gt;

&lt;p&gt;In all of my functions, I use a simple async/await pattern that is shown &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function" rel="noopener noreferrer"&gt;here&lt;/a&gt;, so I can build my screens in a synchronous flow, which keeps the code very readable.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;boot()&lt;/code&gt; function, I can then just await the &lt;code&gt;typer()&lt;/code&gt; function to finish and move to the next screen:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;boot&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;clear&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;typer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello world&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;clear()&lt;/code&gt; function just empties the terminal div by resetting the &lt;code&gt;innerHTML&lt;/code&gt;. I will skip the login screen for now and explain the main loop.&lt;/p&gt;

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

&lt;p&gt;The &lt;code&gt;main()&lt;/code&gt; function shows the input and waits for the user to type a command. The command is then parsed and based on a lot of if/else statements, we can call a function and/or show some output to the user. When the command has finished, we start over by recursively calling the &lt;code&gt;main()&lt;/code&gt; 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="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;command&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;input&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nf"&gt;main&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 just love how concise and readable this code is, despite the fact that we are using an &lt;a href="https://codeburst.io/declarative-vs-imperative-programming-a8a7c93d9ad2" rel="noopener noreferrer"&gt;imperative&lt;/a&gt; style of programming. Creating and updating DOM elements manually is a bit of a chore, but quite manageable in our case.&lt;/p&gt;

&lt;h2&gt;
  
  
  Input/output ⌨️
&lt;/h2&gt;

&lt;p&gt;The CSS for the input and output text is pretty simple, the only interesting thing to mention is the pixely &lt;a href="https://fonts.google.com/specimen/VT323?selection.family=VT323" rel="noopener noreferrer"&gt;VT323&lt;/a&gt; font and all text is transformed to uppercase:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="sx"&gt;url("https://fonts.googleapis.com/css?family=VT323&amp;amp;display=swap")&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nc"&gt;.terminal&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;"VT323"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;monospace&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;text-transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;uppercase&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;
  
  
  Animated typing for the output
&lt;/h3&gt;

&lt;p&gt;This is the part where most of the JavaScript stuff comes in. I started out using a library called &lt;a href="https://typeitjs.com/" rel="noopener noreferrer"&gt;TypeIt&lt;/a&gt; to create an animated typing effect for the command line output. It's quite versatile - you can just pass it a container element and an array of strings and off it goes!&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;new&lt;/span&gt; &lt;span class="nc"&gt;TypeIt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#container&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="na"&gt;strings&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="s2"&gt;Hello&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;world&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;speed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;lifeLike&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="na"&gt;startDelay&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;cursorChar&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;■&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;go&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After a while I decided to roll my own typing function, because I wanted to add a fancy animation when characters appeared on the screen (try clicking the red button). The core of this functionality is a while loop that adds one character to the screen and then pauses for a short while:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;pause&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;queue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&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="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;char&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shift&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;char&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;pause&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;pause&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;active&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;while&lt;/code&gt; loop keeps running as long as the queue string has &lt;code&gt;length &amp;gt; 0&lt;/code&gt; and the &lt;code&gt;String.shift()&lt;/code&gt; function removes the first character and returns it.&lt;/p&gt;

&lt;p&gt;The pause function is a glorified wrapper for &lt;code&gt;setTimeout()&lt;/code&gt;, returning a Promise so we can wait for it using &lt;code&gt;async/await&lt;/code&gt; - nifty! Usually you want to postpone executing a callback function using &lt;code&gt;setTimeout&lt;/code&gt;, but here we just want to pause the code execution, to simulate the terminal processing your command. Thanks &lt;a href="https://stackoverflow.com/questions/39538473/using-settimeout-on-promise-chain" rel="noopener noreferrer"&gt;Stackoverflow&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;function&lt;/span&gt; &lt;span class="nf"&gt;pause&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&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;One second is the default argument, because that is how I wanted to use it most of the time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Handling input commands
&lt;/h3&gt;

&lt;p&gt;In a very similar fashion, I let the user type a command by creating an input element that returns a resolved promise when the user presses the enter key.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pw&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&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;onKeyDown&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&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;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keyCode&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&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;let&lt;/span&gt; &lt;span class="nx"&gt;terminal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.terminal&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;div&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;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;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;input&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;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;contenteditable&lt;/span&gt;&lt;span class="dl"&gt;"&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="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;keydown&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;onKeyDown&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;terminal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;focus&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The input is actually a &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; with the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/contentEditable" rel="noopener noreferrer"&gt;contenteditable&lt;/a&gt; attribute property, which allows the user to type inside the element. This may come in handy if we want to do fancy HTML stuff inside the div, which is mostly not allowed inside a regular &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; element.&lt;/p&gt;

&lt;h3&gt;
  
  
  The blinking caret 🟩
&lt;/h3&gt;

&lt;p&gt;The blinking square at the end of a line really adds to the whole typing animation (credits to TypeIt for the inspiration). It is nothing more than a character placed in the &lt;code&gt;:after&lt;/code&gt; pseudo class!&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="nf"&gt;#input&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="py"&gt;caret-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;transparent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c"&gt;/* Puts a blinking square after the content as replacement for caret */&lt;/span&gt;
&lt;span class="nf"&gt;#input&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;contenteditable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"true"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="nd"&gt;:after&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;"■"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="m"&gt;1s&lt;/span&gt; &lt;span class="n"&gt;infinite&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;animation-timing-function&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;step-end&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;margin-left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c"&gt;/* Inserts the &amp;gt; before terminal input */&lt;/span&gt;
&lt;span class="nf"&gt;#input&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="s1"&gt;"&amp;gt;"&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;padding-left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.5rem&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="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;animation-timing-function: step-end&lt;/code&gt; makes the cursor change its transparency discretely to make it blink, rather than as a linear fade.&lt;/p&gt;

&lt;p&gt;Then I also place a &lt;code&gt;&amp;gt;&lt;/code&gt; character before the input to indicate that he user can type there. A neat little trick is settings &lt;code&gt;caret-color: transparent;&lt;/code&gt; on the actual element itself, to hide the default caret. This will break moving the cursor if the user clicks in the middle of the text, but it does not bother me all too much.&lt;/p&gt;

&lt;h3&gt;
  
  
  Executing commands
&lt;/h3&gt;

&lt;p&gt;I started off with a large if/else block to handle all the different commands, but that got out of hand quickly, so I needed something more &lt;em&gt;modular&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;This is where I decided to use &lt;a href="https://flaviocopes.com/javascript-dynamic-imports/" rel="noopener noreferrer"&gt;dynamic imports&lt;/a&gt;. Another ES6 feature that has great browser support, now that Chromium version of Edge is released!&lt;/p&gt;

&lt;p&gt;You probably know static imports, where you import your dependencies at the top of your own module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import moment from 'moment'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A dynamic import can be used anywhere, even conditionally, with variable paths  and will require the specified resource &lt;em&gt;on demand&lt;/em&gt;! Just what we need! The import will return a Promise with your module. If you use async/await, you can access any of its exports directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const { format } = await import('date-fns');
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So here is how I used imports in for parsing commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async function parse(command) {

    let module;

    // Try to import the command function
    try {
        module = await import(`../commands/${command}.js`);
    } catch (e) {
        console.error(e);
        return await type("Unknown command");
    }

    // Type the output if the command exports any
    if (module &amp;amp;&amp;amp; module.output) {
        await type(module.output);
    }

    await pause();

    // Execute and wait for the command (default export) to finish
    if (module.default) {
        await module.default();
    }
    return;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Doing this kind of stuff directly in the browser without any transpiler such as Babel and a code bundler like Webpack is &lt;em&gt;very&lt;/em&gt; cutting-edge. It gives the developer a lot of freedom to only load resources whenever they are needed, preventing your main app from getting bloated. This is one of the main features that will make it easy to write modular, lightweight apps in native JavaScript.&lt;/p&gt;

&lt;h3&gt;
  
  
  Commands 👨‍💻
&lt;/h3&gt;

&lt;p&gt;Every command is simply a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules" rel="noopener noreferrer"&gt;JavaScript module&lt;/a&gt; with a default export function that is executed when it is loaded. It can also directly output some text when the user presses enter by adding an &lt;code&gt;output&lt;/code&gt; named export, as explained above. If we return a Promise here, the &lt;code&gt;main()&lt;/code&gt; function will wait for the command to be finished.&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;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello world.&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;helloWorld&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="c1"&gt;// do whatever...&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="p"&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="nx"&gt;helloWorld&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we can add commands in a modular way, we can go completely crazy and write any cool stuff we can think of. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I'm trying to free your mind, Neo. But I can only show you the door. You're the one that has to walk through it. &lt;/p&gt;

&lt;p&gt;-- Morpheus&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;h1&gt;
  
  
  Next time...
&lt;/h1&gt;

&lt;p&gt;In the next part of this article, I will explain more about how I added sound, control buttons and theming! For now, have fun hacking!&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%2F7f1otodkw3fkpbw3itz9.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%2F7f1otodkw3fkpbw3itz9.gif" alt="hackerman" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>css</category>
      <category>javascript</category>
      <category>crt</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Creating dice using CSS grid 🎲</title>
      <dc:creator>Edwin</dc:creator>
      <pubDate>Sun, 02 Feb 2020 20:59:49 +0000</pubDate>
      <link>https://dev.to/ekeijl/creating-dice-using-css-grid-j4</link>
      <guid>https://dev.to/ekeijl/creating-dice-using-css-grid-j4</guid>
      <description>&lt;p&gt;Recently, I felt like creating a game as a nice little side project. The game involves dice, so I needed some way to visualise them. This article will explain how you can create dice using only HTML and CSS.&lt;/p&gt;

&lt;h1&gt;
  
  
  Flexing with CSS 💪
&lt;/h1&gt;

&lt;p&gt;Why reinvent the wheel if other people have already solved the problem for us, right? A quick search around the web led me to &lt;a href="https://davidwalsh.name/flexbox-dice" rel="noopener noreferrer"&gt;this article by Landon Schropp&lt;/a&gt;, which describes how to make great looking dice using CSS flexbox.&lt;/p&gt;

&lt;p&gt;He implemented the face of a die by simply positioning a &lt;code&gt;span&lt;/code&gt; element for each pip of the die inside of a &lt;code&gt;div&lt;/code&gt;. The &lt;code&gt;div.column&lt;/code&gt; contains the vertically aligned pips.&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;"fourth-face"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"column"&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;"pip"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/span&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;"pip"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/span&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;"column"&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;"pip"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/span&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;"pip"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/span&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;These pips are positioned using flexbox and pushed to opposite sides of the die using the &lt;code&gt;justify-content: space-between&lt;/code&gt; property. His final solution requires quite a bit of CSS to correctly style every possible face value.&lt;/p&gt;

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

&lt;h1&gt;
  
  
  We can do better with CSS grid 🔥
&lt;/h1&gt;

&lt;p&gt;While I was playing around with the code, I realised that the pips of a traditional die are aligned in three rows and three columns, which is a great opportunity to use CSS grid.&lt;/p&gt;

&lt;p&gt;Imagine the face of a die as a 3x3 grid, where each cell represents the position of a pip:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

+---+---+---+
| a | b | c |
+---+---+---+
| d | e | f |
+---+---+---+
| g | h | i |
+---+---+---+


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

&lt;/div&gt;

&lt;p&gt;CSS grid is supported by &lt;a href="https://caniuse.com/#feat=css-grid" rel="noopener noreferrer"&gt;all evergreen browsers&lt;/a&gt;, but as you may expect, Internet Explorer offers only very basic support. The final result will therefore not work in IE11.&lt;br&gt;
For a full guide on CSS grid, please refer to the amazing &lt;a href="https://gridbyexample.com/" rel="noopener noreferrer"&gt;Grid by Example&lt;/a&gt;, by Rachel Andrews.&lt;/p&gt;
&lt;h2&gt;
  
  
  Creating the grid layout
&lt;/h2&gt;

&lt;p&gt;To create a simple 3 by 3 grid using CSS, the only thing we need to do is set a container element to &lt;code&gt;display: grid&lt;/code&gt; and tell it that we want three equally sized rows and columns:&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;.face&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-rows&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="m"&gt;1&lt;/span&gt;&lt;span class="n"&gt;fr&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="py"&gt;grid-template-columns&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="m"&gt;1&lt;/span&gt;&lt;span class="n"&gt;fr&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="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;fr&lt;/code&gt; unit allows you to set the size of a row or column as a &lt;em&gt;fraction&lt;/em&gt; of the free space of the grid container; in our case we want one third of the available space, so we use &lt;code&gt;1fr&lt;/code&gt; three times.&lt;/p&gt;

&lt;p&gt;Instead of writing &lt;code&gt;1fr 1fr 1fr&lt;/code&gt; we can use &lt;code&gt;repeat(3, 1fr)&lt;/code&gt; to repeat the &lt;code&gt;1fr&lt;/code&gt; unit three times. We also use the &lt;code&gt;grid-template&lt;/code&gt; shorthand property that defines &lt;code&gt;rows / columns&lt;/code&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;.face&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="nl"&gt;grid-template&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;3&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="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;3&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="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;
&lt;p&gt;The only HTML that we need is a &lt;code&gt;div.face&lt;/code&gt; container with the above CSS and a &lt;code&gt;span.pip&lt;/code&gt; for each pip:&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;"face"&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;"pip"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/span&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;"pip"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/span&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;"pip"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/span&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;"pip"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;
&lt;p&gt;The pips will be automatically placed in each of the cells, from left to right:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fhokhomruu4h7zpltsfex.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fhokhomruu4h7zpltsfex.PNG" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  Positioning the pips
&lt;/h1&gt;

&lt;p&gt;Now we get to the point where we need to position the pips for each of the dice values. It would be nice if the &lt;code&gt;span&lt;/code&gt;s automatically flowed to the correct positions in the grid for each value. Sadly, we will need to set the position of each of the pips individually.&lt;/p&gt;

&lt;p&gt;Recall the ASCII table at the beginning of the article? We are going to create something very similar using CSS. Instead of labeling the cells in the row order, we use this specific order so we only need a minimal amount of CSS to fix the edge cases:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

+---+---+---+
| a |   | c |
+---+---+---+
| e | g | f |
+---+---+---+
| d |   | b |
+---+---+---+


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

&lt;/div&gt;
&lt;p&gt;Two of the cells are left empty, because they are never used on our dice.&lt;/p&gt;
&lt;h3&gt;
  
  
  Grid template areas
&lt;/h3&gt;

&lt;p&gt;We can translate this layout to CSS using the magical &lt;code&gt;grid-template-areas&lt;/code&gt; property (which replaces the &lt;code&gt;grid-template&lt;/code&gt; used above):&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;.face&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-areas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="s1"&gt;"a . c"&lt;/span&gt;
        &lt;span class="s1"&gt;"e g f"&lt;/span&gt;
        &lt;span class="s1"&gt;"d . b"&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;So instead of using traditional units to size our rows and columns, we can just refer to each cell with a name. The syntax itself provides a visualization of the structure of the grid, just like our ASCII table. The names are defined by the &lt;code&gt;grid-area&lt;/code&gt; property of the grid item. The period in the middle column signifies an empty cell.&lt;/p&gt;
&lt;h3&gt;
  
  
  Placing pips in an area
&lt;/h3&gt;

&lt;p&gt;We use the &lt;code&gt;grid-area&lt;/code&gt; property to give a name to this grid item. The grid template (above) can then reference the item by its name to place it in a specific area in the grid. The &lt;code&gt;:nth-child()&lt;/code&gt; pseudo selector allows us to target each pip individually:&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;.pip&lt;/span&gt;&lt;span class="nd"&gt;:nth-child&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;grid-area&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.pip&lt;/span&gt;&lt;span class="nd"&gt;:nth-child&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;3&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;grid-area&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.pip&lt;/span&gt;&lt;span class="nd"&gt;:nth-child&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;4&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;grid-area&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.pip&lt;/span&gt;&lt;span class="nd"&gt;:nth-child&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;5&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;grid-area&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.pip&lt;/span&gt;&lt;span class="nd"&gt;:nth-child&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;6&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;grid-area&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;f&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 are getting pretty close!&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fu9unq2dtyihqjsbtm29d.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fu9unq2dtyihqjsbtm29d.PNG" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, the values 1, 3 and 5 are still incorrect. Because of the order of the &lt;code&gt;grid-template-areas&lt;/code&gt; that we chose earlier, we only need to reposition the last pip of each of these dice. To get the result that we want, we combine the &lt;code&gt;:nth-child(odd)&lt;/code&gt; and &lt;code&gt;:last-child&lt;/code&gt; pseudo selectors:&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;.pip&lt;/span&gt;&lt;span class="nd"&gt;:nth-child&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;odd&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nd"&gt;:last-child&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;grid-area&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;g&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 we have our final result!&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fz01znj7zx8c043zux1wy.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fz01znj7zx8c043zux1wy.PNG" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Setting the position of each element individually does not scale well. But for our goal, the number of cases is very limited, it works for all dice values and it allows us to keep our HTML simple. It feels like a cleaner solution than the flexbox version above. It is also easier to translate into components using a JavaScript framework such as React as you will see below.&lt;/p&gt;
&lt;h1&gt;
  
  
  Final result 🎲
&lt;/h1&gt;

&lt;p&gt;The implementation above only uses HTML and CSS, but I also created a very basic React app to show how you can use the dice as components.&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/cly4v"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>css</category>
      <category>react</category>
      <category>dice</category>
      <category>grid</category>
    </item>
  </channel>
</rss>
