bitsofcodeArticles on frontend development and more.2023-03-21T00:00:00Zhttps://bitsofco.de/Ire Aderinokunire@ireaderinokun.comWhen is :focus-visible visible?2023-03-21T00:00:00Zhttps://bitsofco.de/when-is-focus-visible-visible/<p>Focus outlines (and their styling) have frequently been a point of contention between aesthetics and usability. On the one hand, focus outlines are incredibly important for navigation, particularly by users navigating via keyboard or other non-pointer devices. On the other hand, for certain mouse or touchscreen users they can be unnecessary and detract from the design.</p>
<p>Before the <code>:focus-visible</code> CSS pseudo-class was introduced, there was no middle ground to this debate - you either sided with design or accessibility (hopefully the latter). But now, with <code>:focus-visible</code>, we can have focus styles that apply only when the browser / user agent determines that focus should be indicated on the element.</p>
<p>So, when <em>do</em> user agents determine that focus should be indicated? According to the <a href="https://www.w3.org/TR/selectors-4/#the-focus-visible-pseudo">:focus-visible specification</a>, user agents are free to choose their own heuristics for indicating focus. However, the spec includes some suggestions, which most browsers have adopted. These suggestions can be broken down into four questions -</p>
<ol>
<li>Has the user expressed a preference for always visible focus?</li>
<li>Does the focused element support keyboard input?</li>
<li>Is the user interacting with the focused element with a non-pointing device?</li>
<li>Did a script cause focus to move from a previously visibly-focused element?</li>
</ol>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/z3uJeVgO1Y-814.avif 814w"><source type="image/webp" srcset="https://bitsofco.de/img/z3uJeVgO1Y-814.webp 814w"><img alt="Flowchart showing when :focus-visible applies" loading="lazy" decoding="async" src="https://bitsofco.de/img/z3uJeVgO1Y-814.png" width="814" height="1019"></picture></p>
<p>Let’s break each of these questions down.</p>
<h2 id="1-has-the-user-expressed-a-preference-for-always-visible-focus" tabindex="-1">1. Has the user expressed a preference for always visible focus? <a class="header-anchor" href="https://bitsofco.de/when-is-focus-visible-visible/">#</a></h2>
<p>This is a simple one. If a user has expressed a preference for focus to always be visible, then the <code>:focus-visible</code> pseudo-class should apply to the focused element. Fin.</p>
<h2 id="2-does-the-focused-element-support-keyboard-input" tabindex="-1">2. Does the focused element support keyboard input? <a class="header-anchor" href="https://bitsofco.de/when-is-focus-visible-visible/">#</a></h2>
<p>If the focused element supports keyboard input, the <code>:focus-visible</code> pseudo-class should apply. Elements that support keyboard input are typically form elements, such as <code><input></code> or <code><textarea></code>, basically any element that would trigger a virtual keyboard to be shown in the absence of a physical one.</p>
<p>In the example below, we have both a <code><button></code> and an <code><input></code>. For the <code><button></code>, whether the <code>:focus</code> or <code>:focus-visible</code> style is applied depends on how you interact with the element (more on that in the next section). But regardless of how you interact with the <code><input></code> element, it always displays both the <code>:focus</code> style and the <code>:focus-visible</code> style of the red border.</p>
<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="YzOvBMP" data-user="ire" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/ire/pen/YzOvBMP">
Untitled</a> by Ire Aderinokun (<a href="https://codepen.io/ire">@ire</a>)
on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<h2 id="3-is-the-user-interacting-with-the-focused-element-with-a-non-pointing-device" tabindex="-1">3. Is the user interacting with the focused element with a non-pointing device? <a class="header-anchor" href="https://bitsofco.de/when-is-focus-visible-visible/">#</a></h2>
<p>Next, if the user is interacting with the focused element using a keyboard or another non-pointing device, the <code>:focus-visible</code> pseudo-class should apply. So regardless of the type of element it is, focus should be visible.</p>
<p>In the example below, the <code><button></code> elements display the <code>:focus</code> and/or <code>:focus-visible</code> styles depending on what device you use to interact with it.</p>
<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="qBMKgGG" data-user="ire" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/ire/pen/qBMKgGG">
:focus-visible - Is the user interacting with a non-pointing device?</a> by Ire Aderinokun (<a href="https://codepen.io/ire">@ire</a>)
on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<h2 id="4-did-a-script-cause-focus-to-move-from-a-previously-visibly-focused-element" tabindex="-1">4. Did a script cause focus to move from a previously visibly-focused element? <a class="header-anchor" href="https://bitsofco.de/when-is-focus-visible-visible/">#</a></h2>
<p>Finally, this question is to do with what happens if focus is moved via a script instead of user input. According to this guideline, the <code>:focus-visible</code> pseudo-class should apply based on the state of the previously focused element. If <code>:focus-visible</code> applied to the previously focused element, then it should also apply to the currently focused element.</p>
<p>We can see this play out in the example below. Depending on how you interact with the “reset” button, the <code>:focus</code> and/or <code>:focus-visible</code> styles will apply to the test button when focus is programatically moved.</p>
<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="mdGKobP" data-user="ire" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/ire/pen/mdGKobP">
:focus-visible - script</a> by Ire Aderinokun (<a href="https://codepen.io/ire">@ire</a>)
on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<h2 id="bonus-user-agent-wildcard" tabindex="-1">Bonus - User agent wildcard <a class="header-anchor" href="https://bitsofco.de/when-is-focus-visible-visible/">#</a></h2>
<p>As a reminder, these are mostly guidelines/suggestions and user agents are still able to make decisions about when they want <code>:focus-visible</code> to apply. From my tests, I haven’t seen any of the major browsers respond in a different way, but it’s something to remember.</p>
<p>Because of this, best practice is always to only remove <code>:focus</code> styles if <code>:focus-visible</code> is <em>not</em> also applied to the element. Here’s what that looks like:</p>
<pre class="language-css" tabindex="0"><code class="language-css"><span class="token comment">/* Apply focus styles */</span><br><span class="token selector">:focus-visible, :focus</span> <span class="token punctuation">{</span><br> <span class="token property">outline</span><span class="token punctuation">:</span> 1px solid red<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token comment">/* Remove the focus outline */</span><br><span class="token selector">:focus:not(:focus-visible)</span> <span class="token punctuation">{</span><br> <span class="token property">outline</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre>
From Ghost to 11ty2023-02-22T00:00:00Zhttps://bitsofco.de/from-ghost-to-11ty/<p>Over 5 years ago, I migrated this blog <a href="https://bitsofco.de/from-jekyll-to-ghost/">From Jekyll to Ghost</a>. Back then, my reasons for moving to <a href="https://ghost.org">Ghost</a> were because -</p>
<blockquote>
<ul>
<li>I wanted a CMS</li>
<li>Managing all the posts’ raw markdown files in one folder was getting clunky</li>
<li>The Jekyll build process was starting to significantly lag</li>
<li>I no longer wanted to have to manually upload new posts via FTP</li>
<li>I wanted to be able to schedule posts (although Ghost doesn’t support this yet), or at least push one button to publish them</li>
</ul>
</blockquote>
<p>Today, I want the exact opposite 😅</p>
<ul>
<li><strong>I no longer need a CMS</strong>. Since I now post a lot less frequently than before, having a full-blown CMS isn’t necessary.</li>
<li><strong>While raw markdown files can be a bit clunky, I prefer them to Ghost’s CMS</strong>. One of the reasons I chose Ghost in the first place was because, at the time, the editor allowed you to write in pure markdown. Over the years, they’ve made this more difficult and basically force you to use their WYSIWYG editor.</li>
<li><strong>The build process of 11ty is fast</strong>, even with 160+ articles.</li>
<li>Luckily, the world has progressed from FTP 🥳 . Now, <strong>I can publish on Netlify</strong> just by pushing to GitHub.</li>
<li>While I haven’t yet set this up, <strong>scheduling blog posts is possible</strong> with 11ty and Netlify.</li>
</ul>
<p>So, last week, I migrated this blog to <a href="https://www.11ty.dev/">11ty</a>. I managed to do the bulk of the migration in one day, but continued fixing some bugs over the next few days. Here’s how the migration process went.</p>
<h2 id="step-1-convert-the-ghost-export-to-markdown" tabindex="-1">Step 1 - Convert the Ghost export to markdown <a class="header-anchor" href="https://bitsofco.de/from-ghost-to-11ty/">#</a></h2>
<p>One of the great things about Ghost is it allows you to export all your site data into one big JSON file. So, the first thing to do in this migration was to convert that JSON export to individual markdown files for each post.</p>
<p>Luckily, there’s an NPM package for that - <a href="https://www.npmjs.com/package/ghost-to-md">ghost-to-md</a>. I did run into a slight issue with that package due to the library it was using to convert HTML content to Markdown. So, I created a fork of the package (<a href="https://github.com/ireade/ghost-to-md">ireade/ghost-to-md</a>), using a different HTML-to-Markdown converter, which worked better for me.</p>
<p>Using this package, I was able to generate a folder filled with individual markdown files for each blog post.</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/EYxZWFa0H4-2020.avif 2020w"><source type="image/webp" srcset="https://bitsofco.de/img/EYxZWFa0H4-2020.webp 2020w"><img alt="Folder structure" loading="lazy" decoding="async" src="https://bitsofco.de/img/EYxZWFa0H4-2020.png" width="2020" height="1420"></picture></p>
<h2 id="step-2-organise-each-post-into-sub-folders" tabindex="-1">Step 2 - Organise each post into sub-folders <a class="header-anchor" href="https://bitsofco.de/from-ghost-to-11ty/">#</a></h2>
<p>With 11ty, you have a lot of freedom with how and where you want to store all post files. For me, I wanted a structure where, within the directory for all posts, each post will have its own folder, and the markdown file will be within that folder. So, instead of a folder structure like this -</p>
<pre><code>- content
- blog
- article.md
</code></pre>
<p>I'd have a structure like this -</p>
<pre><code>- content
- blog
- article
- article.md
- [other files related to the article]
</code></pre>
<p>The reason I wanted it this way was so that within each article folder, I could also store all the media related to that article. This makes it easy to use the 11ty shortcode for responsive images (more on that in Step 4).</p>
<p>To achieve this, I wrote a script to read all the exported markdown files, and create new subdirectories for each of them.</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> inputDir <span class="token operator">=</span> <span class="token string">'./ghost-to-md-output'</span><span class="token punctuation">;</span><br><span class="token keyword">const</span> outputDir <span class="token operator">=</span> <span class="token string">'./output'</span><span class="token punctuation">;</span><br><br><span class="token comment">// Read all files in inputDir</span><br>fs<span class="token punctuation">.</span><span class="token function">readdir</span><span class="token punctuation">(</span>inputDir<span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">err<span class="token punctuation">,</span> files</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token keyword">return</span><span class="token punctuation">;</span><br><br> <span class="token comment">// Loop through each file</span><br> files<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">file</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br><br> <span class="token keyword">const</span> fileName <span class="token operator">=</span> path<span class="token punctuation">.</span><span class="token function">parse</span><span class="token punctuation">(</span>file<span class="token punctuation">)</span><span class="token punctuation">.</span>name<span class="token punctuation">;</span><br> <span class="token keyword">const</span> inputFilePath <span class="token operator">=</span> path<span class="token punctuation">.</span><span class="token function">join</span><span class="token punctuation">(</span>inputDir<span class="token punctuation">,</span> file<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">const</span> outputFolderPath <span class="token operator">=</span> path<span class="token punctuation">.</span><span class="token function">join</span><span class="token punctuation">(</span>outputDir<span class="token punctuation">,</span> fileName<span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token comment">// Create folder in outputDir</span><br> fs<span class="token punctuation">.</span><span class="token function">mkdir</span><span class="token punctuation">(</span>outputFolderPath<span class="token punctuation">,</span> <span class="token parameter">err</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span>err <span class="token operator">&&</span> err<span class="token punctuation">.</span>code <span class="token operator">!==</span> <span class="token string">'EEXIST'</span><span class="token punctuation">)</span> <span class="token keyword">return</span><span class="token punctuation">;</span><br><br> <span class="token keyword">const</span> outputFilePath <span class="token operator">=</span> path<span class="token punctuation">.</span><span class="token function">join</span><span class="token punctuation">(</span>outputFolderPath<span class="token punctuation">,</span> file<span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token comment">// Copy markdown file to new folder</span><br> fs<span class="token punctuation">.</span><span class="token function">copyFile</span><span class="token punctuation">(</span>inputFilePath<span class="token punctuation">,</span> outputFilePath<span class="token punctuation">,</span> <span class="token parameter">err</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token keyword">return</span><span class="token punctuation">;</span><br><br> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">Successfully copied </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>file<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> to </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>outputFolderPath<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// end fs. fs.mkdir</span><br> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// end files.forEach</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>The result was something like this -</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/4HdpoFFhqZ-2258.avif 2258w"><source type="image/webp" srcset="https://bitsofco.de/img/4HdpoFFhqZ-2258.webp 2258w"><img alt="Folder structure" loading="lazy" decoding="async" src="https://bitsofco.de/img/4HdpoFFhqZ-2258.png" width="2258" height="1366"></picture></p>
<h2 id="step-3-download-media-hosted-on-ghost" tabindex="-1">Step 3 - Download media hosted on Ghost <a class="header-anchor" href="https://bitsofco.de/from-ghost-to-11ty/">#</a></h2>
<p>The next step was to download media content hosted on Ghost. If you look through the markdown files, you’ll notice that some of the URLs looks like this -</p>
<pre><code>__GHOST_URL__/content/images/2019/01/image.png
</code></pre>
<p>This refers to images/media uploaded via the CMS and hosted on Ghost. The JSON export doesn’t include any media, so what I needed to do was manually download every image and place them in the correct folder.</p>
<p>Again, I used a script for this. I created a function that:</p>
<ol>
<li>Takes the folder path to an article, plus the article file name;</li>
<li>Downloads all images starting with <code>__GHOST_URL__</code>; *</li>
<li>Places them in the <code>articleFolderPath</code>;</li>
<li>Replaces the URL in the markdown file with a local relative URL, (i.e. starting with <code>./</code>)</li>
</ol>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token comment">// Takes the folder path to an article, plus the article file name</span><br><span class="token keyword">function</span> <span class="token function">downloadImages</span><span class="token punctuation">(</span><span class="token parameter">articleFolderPath<span class="token punctuation">,</span> fileName</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br><br> <span class="token keyword">const</span> markdownFilePath <span class="token operator">=</span> articleFolderPath <span class="token operator">+</span> <span class="token string">"/"</span> <span class="token operator">+</span> fileName<span class="token punctuation">;</span><br><br> fs<span class="token punctuation">.</span><span class="token function">readFile</span><span class="token punctuation">(</span>markdownFilePath<span class="token punctuation">,</span> <span class="token string">'utf8'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">err<span class="token punctuation">,</span> data</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token keyword">return</span><span class="token punctuation">;</span><br><br> <span class="token comment">// 🚨Warning - Very buggy! (see note below)</span><br> <span class="token comment">// Find all images starting with __GHOST_URL__</span><br> <span class="token keyword">const</span> regex <span class="token operator">=</span> <span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">\(__GHOST_URL__([\s\S]*?)\)</span><span class="token regex-delimiter">/</span><span class="token regex-flags">g</span></span><span class="token punctuation">;</span><br> <span class="token keyword">let</span> match<span class="token punctuation">;</span><br><br> <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>match <span class="token operator">=</span> regex<span class="token punctuation">.</span><span class="token function">exec</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">!==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br><br> <span class="token keyword">const</span> ghostHostedUrl <span class="token operator">=</span> <span class="token string">'https://bitsofco.de'</span> <span class="token operator">+</span> match<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">;</span><br> <span class="token keyword">const</span> filename <span class="token operator">=</span> path<span class="token punctuation">.</span><span class="token function">basename</span><span class="token punctuation">(</span>ghostHostedUrl<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">const</span> filePath <span class="token operator">=</span> path<span class="token punctuation">.</span><span class="token function">join</span><span class="token punctuation">(</span>articleFolderPath<span class="token punctuation">,</span> filename<span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">Downloading image from </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>ghostHostedUrl<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> to </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>filePath<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token keyword">const</span> file <span class="token operator">=</span> fs<span class="token punctuation">.</span><span class="token function">createWriteStream</span><span class="token punctuation">(</span>filePath<span class="token punctuation">)</span><span class="token punctuation">;</span><br> https<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span>ghostHostedUrl<span class="token punctuation">,</span> <span class="token parameter">response</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> response<span class="token punctuation">.</span><span class="token function">pipe</span><span class="token punctuation">(</span>file<span class="token punctuation">)</span><span class="token punctuation">;</span><br> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">Successfully downloaded image to </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>filePath<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">on</span><span class="token punctuation">(</span><span class="token string">'error'</span><span class="token punctuation">,</span> <span class="token parameter">err</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">Error downloading image: </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>err<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> fs<span class="token punctuation">.</span><span class="token function">unlink</span><span class="token punctuation">(</span>filePath<span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token comment">// Replaces the URL in the markdown file with a local relative URL</span><br> data <span class="token operator">=</span> data<span class="token punctuation">.</span><span class="token function">replace</span><span class="token punctuation">(</span>match<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">(./</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>filename<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">)</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br><br> fs<span class="token punctuation">.</span><span class="token function">writeFile</span><span class="token punctuation">(</span>markdownFilePath<span class="token punctuation">,</span> data<span class="token punctuation">,</span> <span class="token parameter">err</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token keyword">return</span><span class="token punctuation">;</span><br> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">Successfully updated file: </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>markdownFilePath<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br><span class="token punctuation">}</span></code></pre>
<p>* <mark>Note - This regex was buggy</mark>. I later realised that there were more URLs that started with this same <code>__GHOST_URL__</code> string. For example, links to other pages within the Ghost site started with that string, so the script replaced those instances as well. If you want to use this, I'd suggest you update the regex to only look for images.</p>
<h2 id="step-4-use-the-11ty-image-plugin" tabindex="-1">Step 4 - Use the 11ty Image Plugin <a class="header-anchor" href="https://bitsofco.de/from-ghost-to-11ty/">#</a></h2>
<p>Since each blog post is written in markdown, I could link to images using the regular markdown syntax -</p>
<pre class="language-markdown" tabindex="0"><code class="language-markdown"><span class="token url"><span class="token operator">!</span>[<span class="token content">Image description</span>](<span class="token url">image.png</span>)</span></code></pre>
<p>However, 11ty has an <a href="https://www.11ty.dev/docs/plugins/image/">Image Plugin</a>, which performs build-time image transformations for locally-stored images. By linking to one single image file, the plugin will automatically generate images in different sizes and formats. To take advantage of this, instead of using the markdown syntax above, we need to use this image shortcode -</p>
<pre><code>{% image "image.png", "Image description" %}
</code></pre>
<p>So, I wrote a script to make this change. This script was also pretty buggy and resulted in the shortcode being inserted into places is shouldn’t have, so I wouldn’t recommend you use as-is. That said, it did work well enough for me, I just had to go through and fix some buggy places.</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">function</span> <span class="token function">convertMarkdownImagesTo11ty</span><span class="token punctuation">(</span><span class="token parameter">articleFolderPath<span class="token punctuation">,</span> fileName</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br><br> <span class="token keyword">const</span> markdownFilePath <span class="token operator">=</span> articleFolderPath <span class="token operator">+</span> <span class="token string">"/"</span> <span class="token operator">+</span> fileName<span class="token punctuation">;</span><br><br> fs<span class="token punctuation">.</span><span class="token function">readFile</span><span class="token punctuation">(</span>markdownFilePath<span class="token punctuation">,</span> <span class="token string">'utf8'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">err<span class="token punctuation">,</span> data</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token keyword">return</span><span class="token punctuation">;</span><br><br> <span class="token comment">// 🚨Warning - Very buggy!</span><br> <span class="token comment">// Find all images starting with "./"</span><br> <span class="token keyword">const</span> regex <span class="token operator">=</span> <span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">!\[(.*?)\]\(\.\/(.*?)\)</span><span class="token regex-delimiter">/</span><span class="token regex-flags">g</span></span><span class="token punctuation">;</span><br> <span class="token keyword">let</span> match<span class="token punctuation">;</span><br><br> <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>match <span class="token operator">=</span> regex<span class="token punctuation">.</span><span class="token function">exec</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">!==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">const</span> altText <span class="token operator">=</span> match<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">;</span><br> <span class="token keyword">const</span> imagePath <span class="token operator">=</span> match<span class="token punctuation">[</span><span class="token number">2</span><span class="token punctuation">]</span><span class="token punctuation">;</span><br> <span class="token keyword">const</span> eleventyImage <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">{% image "</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>imagePath<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">", "</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>altText<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">" %}</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span><br> data <span class="token operator">=</span> data<span class="token punctuation">.</span><span class="token function">replace</span><span class="token punctuation">(</span>match<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">,</span> eleventyImage<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br><br> fs<span class="token punctuation">.</span><span class="token function">writeFile</span><span class="token punctuation">(</span>markdownFilePath<span class="token punctuation">,</span> data<span class="token punctuation">,</span> <span class="token parameter">err</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token keyword">return</span><span class="token punctuation">;</span><br> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">Successfully updated file: </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>markdownFilePath<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre>
<h2 id="step-5-setup-11ty-with-eleventy-base-blog" tabindex="-1">Step 5 - Setup 11ty with eleventy-base-blog <a class="header-anchor" href="https://bitsofco.de/from-ghost-to-11ty/">#</a></h2>
<p>Now that the content was all set up, I could finally move on to 11ty! This part was pretty straightforward. To setup the new project, I used the <a href="https://github.com/11ty/eleventy-base-blog">11ty/eleventy-base-blog</a> starter repository. This already had setup all the relevant pages and templates. All I had to do was tweak the page structure and styling to the theme of my blog.</p>
<p>To add all my post content, I just pasted all the newly-created directories into the <code>content/blog</code> directory.</p>
<p>A few implementation details to mention -</p>
<h3 id="permalinks" tabindex="-1">Permalinks <a class="header-anchor" href="https://bitsofco.de/from-ghost-to-11ty/">#</a></h3>
<p>The URL structure for the articles on Ghost was pretty simple -</p>
<pre><code>https://bitsofco.de/[article-slug]
</code></pre>
<p>Luckily, the ghost-to-md script does include the post slug in each file’s front matter. So, all I had to do was make it such that all posts used that slug for their permalink. To do that, I added the following to the <code>content/blog/blog.11tydata.js</code> file, which has global configuration for all posts.</p>
<pre class="language-js" tabindex="0"><code class="language-js">module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span><br> <span class="token comment">/* other config */</span><br> <span class="token literal-property property">eleventyComputed</span><span class="token operator">:</span> <span class="token punctuation">{</span><br> <span class="token function-variable function">permalink</span><span class="token operator">:</span> <span class="token parameter">data</span> <span class="token operator">=></span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">/</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>data<span class="token punctuation">.</span>page<span class="token punctuation">.</span>fileSlug<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">/</span><span class="token template-punctuation string">`</span></span><br> <span class="token punctuation">}</span><br><span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<h3 id="tags" tabindex="-1">Tags <a class="header-anchor" href="https://bitsofco.de/from-ghost-to-11ty/">#</a></h3>
<p>One issue with the conversion from Ghost to Markdown is how tags are handled. In the export, multiple tags on a post are comma-separated, like this -</p>
<pre class="language-markdown" tabindex="0"><code class="language-markdown"><span class="token front-matter-block"><span class="token punctuation">---</span><br><span class="token front-matter yaml language-yaml"><span class="token key atrule">title</span><span class="token punctuation">:</span> Example Article<br><span class="token key atrule">slug</span><span class="token punctuation">:</span> example<span class="token punctuation">-</span>article<br><span class="token key atrule">tags</span><span class="token punctuation">:</span> one<span class="token punctuation">,</span> two</span><br><span class="token punctuation">---</span></span></code></pre>
<p>However, 11ty needs them to be formatted like this -</p>
<pre class="language-markdown" tabindex="0"><code class="language-markdown"><span class="token front-matter-block"><span class="token punctuation">---</span><br><span class="token front-matter yaml language-yaml"><span class="token key atrule">title</span><span class="token punctuation">:</span> Example Article<br><span class="token key atrule">slug</span><span class="token punctuation">:</span> example<span class="token punctuation">-</span>article<br><span class="token key atrule">tags</span><span class="token punctuation">:</span><br> <span class="token punctuation">-</span> one<br> <span class="token punctuation">-</span> two</span><br><span class="token punctuation">---</span></span></code></pre>
<p>This was an annoying bug to find out at the 11th hour, and I ended up having to fix it manually. If I were to do this again, I'd probably write a script to automate the process.</p>
<p><em>Update</em> - An issue has been filed to support tags as a comma-separated list! (<a href="https://github.com/11ty/eleventy/issues/2820">#2820</a>)</p>
<h3 id="post-excerpts" tabindex="-1">Post excerpts <a class="header-anchor" href="https://bitsofco.de/from-ghost-to-11ty/">#</a></h3>
<p>As far as I know, 11ty doesn’t have a native way to show excerpts from posts, so I had to add a custom shortcode to do that. I got the code for that from this article - <a href="https://www.jonathanyeong.com/garden/excerpts-with-eleventy/">Excerpts with Eleventy</a>, by Jonathan Yeong.</p>
<h3 id="caniuse-embed" tabindex="-1">CanIUse Embed <a class="header-anchor" href="https://bitsofco.de/from-ghost-to-11ty/">#</a></h3>
<p>Since the <a href="https://bitsofco.de/caniuse-embed">embed I created to display caniuse data</a> is an integral part of my posts, I wanted to create a custom plugin to make embedding them easier (similar to how the image plugin works). Luckily for me, someone already beat me to it 😅 . I integrated the plugin, <a href="https://github.com/KevinGimbel/eleventy-plugin-caniuse">KevinGimbel/eleventy-plugin-caniuse</a>, and now I can add the embed by using the following shortcode -</p>
<pre><code>{% caniuse "css-grid" %}
</code></pre>
<p>And get this result -</p>
<p class="ciu_embed" data-feature="css-grid" data-periods="future_1,current,past_1,past_2" data-accessible-colours="false">
<picture>
<source type="image/webp" srcset="https://caniuse.bitsofco.de/image/css-grid.webp">
<source type="image/png" srcset="https://caniuse.bitsofco.de/image/css-grid.png">
<img src="https://caniuse.bitsofco.de/image/css-grid.jpg" alt="Data on support for the css-grid feature across the major browsers from caniuse.com">
</picture>
</p>
<h2 id="set-6-run-npm-start-and-fix-bugs" tabindex="-1">Set 6 - Run <code>npm start</code> and fix bugs <a class="header-anchor" href="https://bitsofco.de/from-ghost-to-11ty/">#</a></h2>
<p>After building the 11ty site, a lot of random bugs came up that I had to fix manually. I was able to debug them pretty easily due to the descriptive error messages, but here are some of the main ones in case you run into similar issues -</p>
<ul>
<li><strong>Markdown errors, especially with tables</strong> - This wasn't really an error but rather the markdown tables were sometimes strange. If you're going through this migration process, I would recommend that you go through all the places you have tables to make sure they look right.</li>
<li><strong>Using nunjucks within the code examples</strong> - One weird bug I ran into was if I happened to be demonstrating a code example within my markdown file that was in Nunjucks. For example, the code above demonstrating how I use the caniuse shortcode. In these cases, I had to wrap the code block with a <code>{% raw %}{% endraw %}</code> to make sure it's not trying to execute the block itself.</li>
<li><strong>Double-quotes in post titles</strong> - Another thing to look out for is if quotes are being used in the front-matter. In some cases, you may need to escape the quotes by adding a backwards slash <code>\"</code> in front of them</li>
<li><strong>Broken links</strong> - I also did a check for broken links using this <a href="https://www.brokenlinkcheck.com/">free broken link checker</a>. This helped me track down and fix those random side-cases, a lot of which were closed by my own code above 😂</li>
</ul>
<p> </p>
<p>That was it! It’s only been a few days, but so far I’m loving 11ty. I'm also in the middle of doing a redesign, so more on that soon!</p>
Web Performance Metrics Cheatsheet2021-10-25T00:00:00Zhttps://bitsofco.de/web-performance-metrics-cheatsheet/<p>I recently gave a talk on <a href="https://noti.st/ire/EfLNUX/fmp-tti-wtf-making-sense-of-performance-metrics">“Making Sense of Performance Metrics”</a> at the Web Unleashed Conference, and it was suggested that I turn the breakdown of all the performance metrics into a cheatsheet; so here it is!</p>
<p>The full cheatsheet, including a downloadable PDF version, are at the bottom of the page so feel free to skip to that.</p>
<h2 id="four-questions" tabindex="-1">Four Questions <a class="header-anchor" href="https://bitsofco.de/web-performance-metrics-cheatsheet/">#</a></h2>
<p>In a web.dev article on <a href="https://web.dev/user-centric-performance-metrics/">"User-centric Performance Metrics"</a>, they frame performance metrics around four questions:</p>
<ol>
<li>Is it happening?</li>
<li>Is it useful?</li>
<li>Is it usable?</li>
<li>Is it delightful?</li>
</ol>
<p>I really like this framework as a way to better understand what aspect of the user experience each metric is tracking.</p>
<h3 id="1-is-it-happening" tabindex="-1">1. Is it happening? <a class="header-anchor" href="https://bitsofco.de/web-performance-metrics-cheatsheet/">#</a></h3>
<p>This is about measuring if there is any response after a user starts the navigation to the page. There are three metrics that fall under this category.</p>
<h4 id="time-to-first-byte-ttfb" tabindex="-1">Time to First Byte (TTFB) <a class="header-anchor" href="https://bitsofco.de/web-performance-metrics-cheatsheet/">#</a></h4>
<p>Time to First Byte measures the time from when the browser requests a page to the first byte of the page being received. This is really useful for measuring the roundtrip between the user’s device and the server. A good number to aim for is 600 milliseconds or less.</p>
<h4 id="first-paint-fp" tabindex="-1">First Paint (FP) <a class="header-anchor" href="https://bitsofco.de/web-performance-metrics-cheatsheet/">#</a></h4>
<p>First Paint measures the time when the first pixel is painted on the screen. This isn't the most user-centric metric, since at first paint there still isn't any content on the screen, but it's useful to know. Because it's not so user-centric, I wasn't able to find any reliable guidelines for what a good number for FP should be, but it should of course fall somewhere between TTFB and and FCP.</p>
<h4 id="first-contentful-paint-fcp" tabindex="-1">First Contentful Paint (FCP) <a class="header-anchor" href="https://bitsofco.de/web-performance-metrics-cheatsheet/">#</a></h4>
<p>First Contentful Paint measures the time when the first piece of content from the DOM is rendered. This is a useful metric to track because this is the point at which a user actually gets recognisable feedback. A good number for this is 1.8 seconds or less.</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/bXSS9PP--j-800.avif 800w"><source type="image/webp" srcset="https://bitsofco.de/img/bXSS9PP--j-800.webp 800w"><img alt="A timeline chart showing TTFB, FP, and FCP" loading="lazy" decoding="async" src="https://bitsofco.de/img/bXSS9PP--j-800.png" width="800" height="206"></picture></p>
<h3 id="2-is-it-useful" tabindex="-1">2. Is it useful? <a class="header-anchor" href="https://bitsofco.de/web-performance-metrics-cheatsheet/">#</a></h3>
<p>While the previous metrics measure if there is <em>any</em> content on the page, these metrics measure if the content rendered is <em>actually useful</em> or meaningful to a user.</p>
<h4 id="largest-contentful-paint-lcp" tabindex="-1">Largest Contentful Paint (LCP) ⭐️ <a class="header-anchor" href="https://bitsofco.de/web-performance-metrics-cheatsheet/">#</a></h4>
<p>Largest Contentful Paint, one of the three <a href="https://web.dev/vitals/">Core Web Vitals</a>, measures the time when the largest piece of content within the viewport is rendered. It can also be configured to measure when a specific piece of content is loaded, instead of just the largest by size. LCP is a successor to the First Meaningful Paint metric, which is no longer recommended as it is not standardised. A good number for LCP is 2.5 seconds or less.</p>
<h4 id="visually-complete-vc" tabindex="-1">Visually Complete (VC) <a class="header-anchor" href="https://bitsofco.de/web-performance-metrics-cheatsheet/">#</a></h4>
<p>Visually Complete measures the time taken for the content within the viewport to be fully rendered. It's based on an algorithm that sets "visual completion" as the capture of the frame that is the least different to the final frame. There are different Visual Complete metrics you can use based on what percentage to completeness you are trying to track, e.g. Visually Complete 85% etc. A good number for VC is 3.4 seconds or less.</p>
<h4 id="speed-index-si" tabindex="-1">Speed Index (SI) <a class="header-anchor" href="https://bitsofco.de/web-performance-metrics-cheatsheet/">#</a></h4>
<p>Speed Index is a similar score to Visually Complete in that is also measures how quickly content is visually displayed during the page load. However, SI also takes into account how your site performs in comparison to other sites on the web. Like VC, a good number to aim for is 3.4 seconds or less.</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/9EKROU1tHO-800.avif 800w"><source type="image/webp" srcset="https://bitsofco.de/img/9EKROU1tHO-800.webp 800w"><img alt="A timeline chart showing LCP, VC, and SI" loading="lazy" decoding="async" src="https://bitsofco.de/img/9EKROU1tHO-800.png" width="800" height="220"></picture></p>
<h3 id="3-is-it-usable" tabindex="-1">3. Is it usable? <a class="header-anchor" href="https://bitsofco.de/web-performance-metrics-cheatsheet/">#</a></h3>
<p>These metrics are about measuring if a user can actually interact with the content that has been rendered.</p>
<h4 id="first-input-delay-fid" tabindex="-1">First Input Delay (FID) ⭐️ <a class="header-anchor" href="https://bitsofco.de/web-performance-metrics-cheatsheet/">#</a></h4>
<p>First Input Delay, another Core Web Vital, measures the delay between the time a user can attempt to interact with a part of the site, and the time that the interface is able to respond to that interaction. This is an important metric to track because, generally, if users see an interactive element such as a button, they assume that it can be interacted with reliably. If there is a delay between when a user presses the button and when they get feedback, this can become an issue. Ideally, FID should not surpass 100 milliseconds.</p>
<h4 id="max-potential-first-input-delay-mfid" tabindex="-1">Max Potential First Input Delay (mFID) <a class="header-anchor" href="https://bitsofco.de/web-performance-metrics-cheatsheet/">#</a></h4>
<p>Max Potential First Input Delay measures the maximum possible First Input Delay that could occur based on the duration of the longest task. This is a useful metric to have because FID is a field metric, which means it's based on real user data and cannot be measured by page simulations (i.e., in the lab). So, if you want an approximation of the worst case scenario of FID while you're still in a lab setting, mFID is a good option. Ideally, mFID should be 130 milliseconds or less.</p>
<h4 id="total-blocking-time-tbt" tabindex="-1">Total Blocking Time (TBT) <a class="header-anchor" href="https://bitsofco.de/web-performance-metrics-cheatsheet/">#</a></h4>
<p>Total Blocking Time measures the total duration of Javascript tasks between the First Contentful Paint and Time to Interactive. This metric gives us a sense of how long a user could potentially be waiting if there are several blocking tasks. Because while mFID tells us the duration of the longest blocking task, a user may end up waiting even longer than that if there are several blocking tasks in a chain. TBT is expected to be 200 milliseconds or less.</p>
<h4 id="time-to-interactive-tti" tabindex="-1">Time to Interactive (TTI) <a class="header-anchor" href="https://bitsofco.de/web-performance-metrics-cheatsheet/">#</a></h4>
<p>Time to Interactive measures the time when the main thread has had up to 5 seconds with no network activity or JavaScript tasks. Having up to 5 seconds clear is important because that’s when the main thread is clear enough to reliably respond to user input. TTI should be at most 3.8 seconds.</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/6D6V-0h-19-800.avif 800w"><source type="image/webp" srcset="https://bitsofco.de/img/6D6V-0h-19-800.webp 800w"><img alt="A timeline chart showing FID, mFID, TBT, and TTI" loading="lazy" decoding="async" src="https://bitsofco.de/img/6D6V-0h-19-800.png" width="800" height="230"></picture></p>
<h3 id="4-is-it-delightful" tabindex="-1">4. Is it delightful? <a class="header-anchor" href="https://bitsofco.de/web-performance-metrics-cheatsheet/">#</a></h3>
<p>Finally, this category of metrics measures the <em>experience</em> of the interactions and how enjoyable and pleasant the website is to use.</p>
<h4 id="cumulative-layout-shift-cls" tabindex="-1">Cumulative Layout Shift (CLS) ⭐️ <a class="header-anchor" href="https://bitsofco.de/web-performance-metrics-cheatsheet/">#</a></h4>
<p>Cumulative Layout Shift, the third Core Web Vital, measures the shifts in layout while a page is loading. This metric is calculated based on how much elements shift relative to the viewport (impact fraction) and what distance they travel (distance fraction). A good score for CLS is 0.1 or less.</p>
<h4 id="frame-rate" tabindex="-1">Frame Rate <a class="header-anchor" href="https://bitsofco.de/web-performance-metrics-cheatsheet/">#</a></h4>
<p>Frame rate is the rate at which the browser can produce new frames in response to interactions and/or animations. The standard for a smooth frame rate is 60 frames per second.</p>
<h2 id="the-cheatsheet" tabindex="-1">The Cheatsheet <a class="header-anchor" href="https://bitsofco.de/web-performance-metrics-cheatsheet/">#</a></h2>
<p>Here are all the metrics together, including what a good score is to aim for, and whether they can be measure in the lab or field.</p>
<table>
<thead>
<tr>
<th>Metric</th>
<th>Description</th>
<th>✅</th>
<th>Lab?</th>
<th>Field?</th>
</tr>
</thead>
<tbody>
<tr>
<td>Time to First Byte (TTFB)</td>
<td>Measures the time from when the browser requests a page to the first byte of the page being received</td>
<td><600ms</td>
<td>✅</td>
<td>✅</td>
</tr>
<tr>
<td>First Paint (FP)</td>
<td>Measures the time when the first pixel is painted on the screen</td>
<td><1.8s *</td>
<td>✅</td>
<td>✅</td>
</tr>
<tr>
<td>First Contentful Paint (FCP)</td>
<td>Measures the time when the first piece of content from the DOM is rendered</td>
<td><1.8s</td>
<td>✅</td>
<td>✅</td>
</tr>
<tr>
<td>Largest Contentful Paint (LCP) ⭐️</td>
<td>Measures the time when the largest piece of content within the viewport is rendered</td>
<td><2.5s</td>
<td>✅</td>
<td>✅</td>
</tr>
<tr>
<td>Visually Complete (VC)</td>
<td>Measures the time taken for the content within the viewport to be fully rendered</td>
<td><3.4s</td>
<td>✅</td>
<td>❌</td>
</tr>
<tr>
<td>Speed Index (SI)</td>
<td>Measures how quickly visual content is rendered within the viewport</td>
<td><3.4s</td>
<td>✅</td>
<td>❌</td>
</tr>
<tr>
<td>First Input Delay (FID) ⭐️</td>
<td>Measures the delay between the time a user can attempt to interact with a part of the site, and the time that the interface is able to respond to that interaction</td>
<td><100ms</td>
<td>❌</td>
<td>✅</td>
</tr>
<tr>
<td>Max Potential First Input Delay (mFID)</td>
<td>Measures the maximum possible First Input Delay based on the duration of the longest task</td>
<td><130ms</td>
<td>✅</td>
<td>❌</td>
</tr>
<tr>
<td>Total Blocking Time (TBT)</td>
<td>Measures the total duration of Javascript tasks between the FCP and TTI</td>
<td><200ms</td>
<td>✅</td>
<td>❌</td>
</tr>
<tr>
<td>Time to Interactive (TTI)</td>
<td>Measures the time when the main thread has had up to 5 seconds with no network activity or JavaScript tasks</td>
<td><3.8s</td>
<td>✅</td>
<td>❌</td>
</tr>
<tr>
<td>Cumulative Layout Shift (CLS) ⭐️</td>
<td>Measures the shifts in layout while a page is loading</td>
<td><0.1</td>
<td>✅</td>
<td>✅</td>
</tr>
<tr>
<td>Frame Rate (FR)</td>
<td>Measures the rate at which the browser can produce new frames in response to interactions and/or animations</td>
<td>60 FPS</td>
<td>✅</td>
<td>✅</td>
</tr>
</tbody>
</table>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/R_LkW2-iRZ-800.avif 800w"><source type="image/webp" srcset="https://bitsofco.de/img/R_LkW2-iRZ-800.webp 800w"><img alt="A timeline chart of all the performance metrics" loading="lazy" decoding="async" src="https://bitsofco.de/img/R_LkW2-iRZ-800.png" width="800" height="349"></picture></p>
<p><strong>👉🏾</strong><a href="https://ireaderinokun.com/web-performance-metrics-cheatsheet.pdf"><strong>Download the PDF !</strong></a><strong>👈🏾</strong></p>
Setting up a Decentralised Website (ENS + IPFS = dWeb)2021-06-18T00:00:00Zhttps://bitsofco.de/setting-up-a-decentralised-website/<p>One thing that I’ve been really into lately has been ENS names. As someone who’s already obsessed with buying domain names, ENS names have been an even greater obsession (mostly because of emoji names, like <code>✊🏾✊🏾✊🏾.eth</code>)!</p>
<p>So what are ENS names, exactly? And what is a “decentralised website”?</p>
<h2 id="a-short-intro-to-ens-ipfs-and-the-dweb" tabindex="-1">A short intro to ENS, IPFS, and the dWeb <a class="header-anchor" href="https://bitsofco.de/setting-up-a-decentralised-website/">#</a></h2>
<p>Let’s start with ENS. The <strong>Ethereum Name Service</strong> is a naming system based on the Ethereum blockchain. It has a lot of parallels with the Internet’s Domain Name Service (DNS) in that both of these services are used to map human-readable names to less human-readable characters. Where DNS names map to IP addresses, ENS names can map to cryptocurrency addresses, content hashes, and other metadata.</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/yGqXBbryHh-593.avif 593w"><source type="image/webp" srcset="https://bitsofco.de/img/yGqXBbryHh-593.webp 593w"><img alt="As bitsofco.de is to 104.18.131.189, bitsofcode.eth is to 0x3ca2FC4f1F207178Fee25a9Aa8676ec00292dcfc" loading="lazy" decoding="async" src="https://bitsofco.de/img/yGqXBbryHh-593.jpeg" width="593" height="217"></picture></p>
<p>For example, I have the ENS name <code>bitsofcode.eth</code>, which maps to the ETH address <code>0x3ca2FC4f1F207178Fee25a9Aa8676ec00292dcfc</code>. If someone (👀) wanted to send me Ether, they could simply put in the ENS name instead of copying and pasting the longer, less user friendly, address.</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/djhbf6J2nv-1170.avif 1170w"><source type="image/webp" srcset="https://bitsofco.de/img/djhbf6J2nv-1170.webp 1170w"><img alt="Screenshot of sending to the bitsofcode.eth ENS name" loading="lazy" decoding="async" src="https://bitsofco.de/img/djhbf6J2nv-1170.png" width="1170" height="2532"></picture></p>
<p>As I mentioned, ENS names can map on to more than just cryptocurrency addresses, and this is where the really fun part comes in. In addition to metadata like a website or twitter username, we can also map an ENS name to what’s called a <strong>content hash</strong>. A content hash, similar to an IP address, is a unique identifier for hosted content, i.e. files. However, unlike IP addresses that are based on a <em>location</em> where content is stored, content hashes are based on the <em>content</em> itself. This may not make a lot of sense now, but we’ll see how this distinction matters when we setup our dWebsite below.</p>
<p>So how and where do we host these files? That’s where IPFS comes in! The <strong>InterPlanetary File System</strong> is a “distributed system for storing and accessing files, websites, applications, and data”. Unlike other hosting options which have a centralised server, IPFS is ✨decentralised✨. It’s powered by a peer-to-peer network that stores and relays content as needed. If you’ve ever used BitTorrent, it’s a very similar concept.</p>
<p>Both ENS and IPFS together form the <strong>Decentralised Web</strong> - websites that are hosted via a decentralised system such as IPFS, and accessed via a decentralised name service such as ENS.</p>
<h2 id="how-to-publish-a-dwebsite" tabindex="-1">How to publish a dWebsite <a class="header-anchor" href="https://bitsofco.de/setting-up-a-decentralised-website/">#</a></h2>
<p>So how does one setup and publish a dWebsite? Well, there are four steps:</p>
<h3 id="1-build-your-website" tabindex="-1">1. Build your website <a class="header-anchor" href="https://bitsofco.de/setting-up-a-decentralised-website/">#</a></h3>
<p>You can build a dWebsite in pretty much the same way you’d build any other website. As long as, at the end, you have a directory with all your files contained in them, you’re good.</p>
<p>For my <a href="https://bitsofcode.eth.link">bitsofcode.eth</a> example, I decided to create a simple page that loads an iframe of the original blog.</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/USfXFmvvB6-1558.avif 1558w"><source type="image/webp" srcset="https://bitsofco.de/img/USfXFmvvB6-1558.webp 1558w"><img alt="Screenshot of the website in the Opera browser" loading="lazy" decoding="async" src="https://bitsofco.de/img/USfXFmvvB6-1558.png" width="1558" height="1194"></picture></p>
<p>The entire website is just 3 files - 1 HTML and 2 CSS (<a href="https://github.com/ireade/bitsofcode.eth">view on GitHub</a>).</p>
<h3 id="2-register-an-ens-name" tabindex="-1">2. Register an ENS name <a class="header-anchor" href="https://bitsofco.de/setting-up-a-decentralised-website/">#</a></h3>
<p>Depending on if you already have a cryptocurrency wallet (and some ETH stored in it), this may be an easy or difficult step. For an in-depth guide for how to set everything up from scratch, I suggest you read this great article on <a href="https://tejuadeyinka.medium.com/how-to-register-an-ens-name-for-your-wallet-address-190767641dae">How to register an ENS name for your wallet address</a> by Teju Adeyinka (<code>teju.eth</code>).</p>
<p>If you already have a wallet + ETH, go to <a href="https://ens.domains/">ens.domains</a> to register your .eth name.</p>
<h3 id="3-upload-your-files-on-ipfs" tabindex="-1">3. Upload your files on IPFS <a class="header-anchor" href="https://bitsofco.de/setting-up-a-decentralised-website/">#</a></h3>
<p>Next, you’ll need to upload your website files on IPFS. I recommend using a service like <a href="https://pinata.cloud/">Pinata</a>, which makes this process really simple. Once you’ve uploaded your files, you’ll get an IPFS CID (Content Identifier).</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/qbCQwbwktG-1594.avif 1594w"><source type="image/webp" srcset="https://bitsofco.de/img/qbCQwbwktG-1594.webp 1594w"><img alt="IPFS CID for bitsofcode" loading="lazy" decoding="async" src="https://bitsofco.de/img/qbCQwbwktG-1594.png" width="1594" height="238"></picture></p>
<p>The CID is a unique hash for your specific content. This is where the fact that a content hash (i.e., the CID) is based on content, not location, comes in. It means that, once you’ve uploaded your files on IPFS and get a CID for those specific files, <strong>you won’t be able to change the content of the files and retain the same CID</strong>. If the content changes, the CID changes.</p>
<h3 id="4-link-your-ens-name-to-your-website" tabindex="-1">4. Link your ENS name to your website <a class="header-anchor" href="https://bitsofco.de/setting-up-a-decentralised-website/">#</a></h3>
<p>Finally, we can connect the pieces. Going back to ENS, we now need to edit the records associated with the ENS name and add the content hash. Since we’re using IPFS to store our content, we need to add a prefix of <code>ipfs://</code> before the CID from Pinata.</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/8hNRFTr4fn-1672.avif 1672w"><source type="image/webp" srcset="https://bitsofco.de/img/8hNRFTr4fn-1672.webp 1672w"><img alt="Editing the content field on ENS" loading="lazy" decoding="async" src="https://bitsofco.de/img/8hNRFTr4fn-1672.png" width="1672" height="262"></picture></p>
<p>At this point, the site is now accessible via the raw ENS name in browsers like Opera or via browser extensions like Metamask. For other/all browsers, the site can be accessed by the ENS name with a <code>.link</code> appended, e.g. <a href="https://bitsofcode.eth.link">bitsofcode.eth.link</a>.</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/qsQXEeNP7e-2000.avif 2000w"><source type="image/webp" srcset="https://bitsofco.de/img/qsQXEeNP7e-2000.webp 2000w"><img alt="bitsofcode.eth.link in browser url bar" loading="lazy" decoding="async" src="https://bitsofco.de/img/qsQXEeNP7e-2000.png" width="2000" height="583"></picture></p>
<h2 id="redirecting-an-ens-name-to-an-existing-website" tabindex="-1">Redirecting an ENS name to an existing website <a class="header-anchor" href="https://bitsofco.de/setting-up-a-decentralised-website/">#</a></h2>
<p>Given the limitations of not being able to update the content of a site hosted on IPFS without also changing the content hash, you may want to just redirect your ENS name to an existing website hosted elsewhere. This is the approach I took for my personal website, ireaderinokun.com, and my personal ENS name, <code>ire.eth</code>.</p>
<p>To do this, we need to replace step 1 of “building our dWebsite”, with a simple webpage that redirects to the existing website of our choice. Here’s an example of what that page could look like:</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token doctype"><span class="token punctuation"><!</span><span class="token doctype-tag">DOCTYPE</span> <span class="token name">html</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>html</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>en<span class="token punctuation">"</span></span> <span class="token attr-name">dir</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>ltr<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>head</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">charset</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>utf-8<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>title</span><span class="token punctuation">></span></span>ire.eth<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>title</span><span class="token punctuation">></span></span><br><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><br> window<span class="token punctuation">.</span>location<span class="token punctuation">.</span>href <span class="token operator">=</span> <span class="token string">"https://ireaderinokun.com"</span><br> </span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>head</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>html</span><span class="token punctuation">></span></span></code></pre>
<p>All the other steps remain the same.</p>
<p>Now, if you visit <a href="https://ire.eth.link">ire.eth</a>, you’ll be automatically redirected to ireaderinokun.com! 🚀</p>
Highlights from Chrome Dev Summit 20202020-12-14T00:00:00Zhttps://bitsofco.de/chrome-dev-summit-2020/<p>CDS in 2020 was a bit different, like everything else. It was online, and the talks were more bite-sized at roughly 10 minutes each. I really enjoyed this new style of the summit and, while I missed the in-person aspects of the conference, this was a great way to end the year. So, without further ado, here’s what I’m most excited about from the conference!</p>
<h2 id="simplified-performance-metrics-with-the-core-web-vitals" tabindex="-1">Simplified performance metrics with the "Core Web Vitals" <a class="header-anchor" href="https://bitsofco.de/chrome-dev-summit-2020/">#</a></h2>
<p>As we all know, there are a tonne of performance metrics out there we can use to track seemingly everything about how our websites perform. Although this granularity is great because it allows us to dig deep into specific areas we may need to track, it does make getting into performance metrics a little bit daunting.</p>
<p>The solution to this is what the Chrome team are calling the “Core Web Vitals”. These are 3 performance metrics that they present as the critical vitals to keep track of:</p>
<ol>
<li><strong>First Contentful Paint</strong> - this measures the loading experience</li>
<li><strong><a href="https://bitsofco.de/what-is-first-input-delay">First Input Delay</a> (or Total Blocking Time)</strong> - this measures the interactivity experience</li>
<li><strong>Cumulative Layout Shift</strong> - this measures visual stability</li>
</ol>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/-30TJ4p2xq-1620.avif 1620w"><source type="image/webp" srcset="https://bitsofco.de/img/-30TJ4p2xq-1620.webp 1620w"><img alt="Largest Contentful Paint, First Input Delay, and Cumulative Layout Shift" loading="lazy" decoding="async" src="https://bitsofco.de/img/-30TJ4p2xq-1620.png" width="1620" height="580"></picture></p>
<p>Image from <a href="https://web.dev/vitals/">web.dev</a></p>
<p>Going forward, the Lighthouse performance score will be more heavily weighted to these metrics, so they are definitely the ones to start with.</p>
<p>Most of the first day of CDS was spent around what these metrics are, how to track them, and how to fix common issues with them. If you want to know more, I’d recommend you watch the following sessions:</p>
<ul>
<li><a href="https://developer.chrome.com/devsummit/sessions/state-of-speed-tooling/">State of speed tooling</a> by Elizabeth Sweeny and Paul Irish</li>
<li><a href="https://developer.chrome.com/devsummit/sessions/fixing-common-web-vitals-issues/">Fixing common Web Vitals issues</a> by Katie Hempenius</li>
</ul>
<h2 id="new-css-properties-for-performance" tabindex="-1">New CSS properties for performance <a class="header-anchor" href="https://bitsofco.de/chrome-dev-summit-2020/">#</a></h2>
<p>Continuing with the theme of performance, there are some new CSS features geared towards better performing sites.</p>
<p>The <code>content-visibility</code> property allows us to tell the browser when to render an element. The “magic” value here is <code>auto</code>, which tells the browser not to render an element until it’s visible in the viewport. This means that page loads will be significantly faster, if the browser only needs to render above-the-fold content initially. In Jake Archibald’s talk <a href="https://developer.chrome.com/devsummit/sessions/beyond-fast/">Beyond fast</a>, he mentioned how the HTML Standard layout time went from 50 seconds to 400 milliseconds, just by using this property!</p>
<p>The <code>content-visibility</code> property works hand in hand with the <code>contain-intrinsic-size</code> property to prevent potential issues with Cumulative Layout Shift. Because the browser isn’t rendering the element fully on load, there may be some shift when it does need to render the element. In order to minimise shifts happening on the page, we can use the <code>contain-intrinsic-size</code> property to specify dimensions for the element even without the content being rendered.</p>
<pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.below-fold-element</span> <span class="token punctuation">{</span><br> <span class="token property">content-visibility</span><span class="token punctuation">:</span> auto<span class="token punctuation">;</span><br> <span class="token property">contain-intrinsic-size</span><span class="token punctuation">:</span> 0 500px<span class="token punctuation">;</span> <span class="token comment">/* set height to 500px */</span><br><span class="token punctuation">}</span></code></pre>
<p>I like to think of this property like adding a <code>width</code> or <code>height</code> to media elements to reserve that space so the page doesn’t shift when the media is eventually loaded.</p>
<p>These properties are already shipped in Chrome (and by extension Edge). But the great thing about CSS is they can be used today and will just be a nice enhancement for browsers that do support it.</p>
<p class="ciu_embed" data-feature="css-content-visibility" data-periods="future_1,current,past_1,past_2" data-accessible-colours="false">
<picture>
<source type="image/webp" srcset="https://caniuse.bitsofco.de/image/css-content-visibility.webp">
<source type="image/png" srcset="https://caniuse.bitsofco.de/image/css-content-visibility.png">
<img src="https://caniuse.bitsofco.de/image/css-content-visibility.jpg" alt="Data on support for the css-content-visibility feature across the major browsers from caniuse.com">
</picture>
</p>
<h2 id="shipping-modern-javascript" tabindex="-1">Shipping modern Javascript <a class="header-anchor" href="https://bitsofco.de/chrome-dev-summit-2020/">#</a></h2>
<p>According to Houssein Djirdeh and Jason Miller in their talk on <a href="https://developer.chrome.com/devsummit/sessions/transitioning-to-modern-javascript/">Transitioning to modern JavaScript</a>, over 90% of web traffic comes from browsers that support ES2017. This means that we could technically be shipping features like classes, arrow functions, and async/await directly to browsers with zero transpilation needed!</p>
<p>That said, we still need to figure out a way to work with both the 90% and the 10%. And, given that the majority of browsers support modern javascript, the solution isn’t to just ship the transpiled code to all browsers. Instead, we should have both modern and legacy versions of our app files, and serve them based on the support of</p>
<h3 id="type-module" tabindex="-1"><code>type=module</code> <a class="header-anchor" href="https://bitsofco.de/chrome-dev-summit-2020/">#</a></h3>
<p>This isn’t exactly new, but one way to serve different files based on browser support is to use <code>type="module"</code> on script tags for modern files, and to specify the <code>nomodule</code> attribute for the legacy files.</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>module<span class="token punctuation">"</span></span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>modern.js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">nomodule</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>legacy.js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></code></pre>
<p>Browsers that support <code>type="module"</code> will load that file, and it'll be ignored by the legacy browsers.</p>
<h3 id="package-exports" tabindex="-1">Package <code>exports</code> <a class="header-anchor" href="https://bitsofco.de/chrome-dev-summit-2020/">#</a></h3>
<p>In our package.json file, we can now define another “main” script file using the <code>exports</code> key.</p>
<pre class="language-json" tabindex="0"><code class="language-json"><span class="token punctuation">{</span><br> <span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"my-package"</span><span class="token punctuation">,</span><br> <span class="token property">"exports"</span><span class="token operator">:</span> <span class="token string">"./modern.js"</span><span class="token punctuation">,</span><br> <span class="token property">"main"</span><span class="token operator">:</span> <span class="token string">"./legacy.js"</span><br><span class="token punctuation">}</span></code></pre>
<p>This <code>exports</code> field is only supported in Node 12.8 and above, which implies support for ES2019+ syntax.</p>
<h2 id="tabbed-desktop-pwas-and-more" tabindex="-1">Tabbed desktop PWAs! (& more) <a class="header-anchor" href="https://bitsofco.de/chrome-dev-summit-2020/">#</a></h2>
<p>PWAs installed to desktops are receiving a lot of awesome new features. The most exciting for me is the fact that these apps can now be tabbed!</p>
<p>This is implemented via the <code>display_override</code> field in the web manifest, which allows us to specify a list of display modes we want to use, in order of priority.</p>
<pre class="language-json" tabindex="0"><code class="language-json"><span class="token punctuation">{</span><br> <span class="token property">"display"</span><span class="token operator">:</span> <span class="token string">"standalone"</span><span class="token punctuation">,</span><br> <span class="token property">"display_override"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">"tabbed"</span><span class="token punctuation">,</span> <span class="token string">"minimal-ui"</span><span class="token punctuation">]</span><span class="token punctuation">,</span><br><span class="token punctuation">}</span></code></pre>
<p>This will allow us to create tabs in our PWAs! This will be a critical feature for so many desktop PWAs.</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/9DxqnPnFv4-2000.avif 2000w"><source type="image/webp" srcset="https://bitsofco.de/img/9DxqnPnFv4-2000.webp 2000w"><img alt="Screenshot of a Desktop PWA for 2 tabs" loading="lazy" decoding="async" src="https://bitsofco.de/img/9DxqnPnFv4-2000.png" width="2000" height="1126"></picture></p>
<p>Screenshot from <a href="https://developer.chrome.com/devsummit/sessions/next-level-web-apps-on-desktop/">Next-level web apps on desktop</a> talk at CDS 2020</p>
<p>Another interesting new API will allow our desktop PWAs to be launched on login to the device.</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">if</span> <span class="token punctuation">(</span>navigator<span class="token punctuation">.</span>runOnOsLogin<span class="token punctuation">)</span> <span class="token punctuation">{</span><br><br> navigator<span class="token punctuation">.</span>runOnOsLogin<span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token literal-property property">mode</span><span class="token operator">:</span> <span class="token string">"windowed"</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><br> <span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> <span class="token comment">// Permission approved</span><br> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br><span class="token punctuation">}</span></code></pre>
<p>Other interesting new APIS coming soon include:</p>
<ul>
<li>File Handling API</li>
<li>Notification Triggers API</li>
<li>Digital Goods API</li>
<li>Local Fonts API</li>
</ul>
<p>PJ Mclachlan’s talk on <a href="https://developer.chrome.com/devsummit/sessions/next-level-web-apps-on-desktop/">Next-level web apps on desktop</a> dives in further into all these features.</p>
<hr>
<p>Read my summaries of previous Chrome Dev Summits:</p>
<ul>
<li><a href="https://bitsofco.de/chrome-dev-summit-2019">2019</a></li>
<li><a href="https://bitsofco.de/chrome-dev-summit-2018">2018</a></li>
<li><a href="https://bitsofco.de/chrome-dev-summit-2016">2016</a></li>
</ul>
<p>What was your favourite talk from the conference?</p>
What I wish I knew about React2020-04-28T00:00:00Zhttps://bitsofco.de/what-i-wish-i-knew-about-react/<p>A couple weeks ago I started working on my first React application. Not only was it my first React application, but it was also my first React Native application, so a lot was new to me all in one go.</p>
<p>Coming from the Angular (and Ionic) world, it felt like all my preconceived notions and ideas were being turned upside down. I managed to put together (what I think is) a pretty decent application, but I had a lot of roadblocks along the way. This is partly because I prefer to learn by just diving in (the only tutorial I did was this <a href="https://www.youtube.com/watch?v=Hf4MJH0jDb4">React Native Crash Course</a>). So, a lot of these roadblocks could have been solved by me just reading the documentation.</p>
<p>That said, in case there’s anyone else there like me, here are a few things I wish I knew before I got started.</p>
<h2 id="react-is-a-library-not-a-framework" tabindex="-1">React is a library, not a framework <a class="header-anchor" href="https://bitsofco.de/what-i-wish-i-knew-about-react/">#</a></h2>
<p>This is something I technically knew as I had heard it several times before, but I didn’t really internalise it. I sort of assumed this was more of a technicality, rather than a key piece of information around which I should redefine my expectations.</p>
<h3 id="what-s-the-difference-between-a-library-and-a-framework" tabindex="-1">What’s the difference between a library and a framework? <a class="header-anchor" href="https://bitsofco.de/what-i-wish-i-knew-about-react/">#</a></h3>
<p>The difference between a library and a framework is, it turns out, very key. Let’s use an analogy of trying to build a house. With a framework, you’re essentially given all the materials to build that house, and it’s up to you in what arrangement you put them in. You will very rarely need to step outside the materials you have been given to use something third-party and, in some cases, you may not be allowed to step outside the materials.</p>
<p>With a library, on the other hand, you start with essentially nothing. The library’s materials are a limited set in comparison to a framework’s materials, and you can pick/choose when you want to use them and when you want to step outside that and use third-party materials.</p>
<h3 id="what-does-that-really-mean" tabindex="-1">What does that really mean? <a class="header-anchor" href="https://bitsofco.de/what-i-wish-i-knew-about-react/">#</a></h3>
<p>For me, this understanding allowed me to step away from thinking that a React-specific solution was what I needed for everything.</p>
<p>For example, I spent hours trying to figure out how to do simple things like making a request to an API in React. In Angular, I would use their Angular-specific <code>HttpClient</code> to make such requests, but that doesn’t exist in React. It wasn’t until a <a href="https://twitter.com/bravelyjake">kind twitter user</a> made the (now obvious) point that I could just use a simple Javascript <code>fetch()</code> request, that I had this moment of realisation.</p>
<p>So now, I’ve redefined my expectations of what React will do for me. When I have a problem to solve, I know to think Javascript-first instead of React-first.</p>
<h2 id="react-is-a-ui-library" tabindex="-1">React is a <em>UI</em> library <a class="header-anchor" href="https://bitsofco.de/what-i-wish-i-knew-about-react/">#</a></h2>
<p>Not only is React a library, it’s specifically for User Interfaces. Again, this should be obvious considering it says so on their website, but I didn’t internalise it enough. The reason this is so important to understand is similar to why it should have been obvious to me that I could just use the browser’s native <code>fetch()</code> API.</p>
<blockquote>
<p>If what you’re trying to do isn’t directly related to something visual on screen, React does not care</p>
</blockquote>
<p>The way I like to think of it in comparison to Angular is this: Angular is a framework for architecting and building applications. It, for the most part, doesn’t change the way you write your HTML or CSS, but it heavily controls the way you write Javascript.</p>
<p>React, on the other hand, is a library for building user interfaces. It, for the most part, doesn’t change the way you write Javascript that isn’t directly related to your UI, but it heavily controls the way you write HTML and CSS (or the equivalent of it). And this brings me to the next point…</p>
<h2 id="react-component-libraries-are-always-for-ui" tabindex="-1">React component libraries are always for UI <a class="header-anchor" href="https://bitsofco.de/what-i-wish-i-knew-about-react/">#</a></h2>
<p>This is something that really made no sense to me the first time I realised it. Take, for example, the <code>apollo-react</code> library. In that library, you can use a <code><Query></code> element, which will perform a GraphQL query and return the result, right in your component markup.</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">import</span> React from ‘react’<span class="token punctuation">;</span><br><span class="token keyword">import</span> <span class="token punctuation">{</span>Text<span class="token punctuation">}</span> from ‘react<span class="token operator">-</span>native’<span class="token punctuation">;</span><br><span class="token keyword">import</span> gql from ‘graphql<span class="token operator">-</span>tag’<span class="token punctuation">;</span><br><span class="token keyword">import</span> <span class="token punctuation">{</span>Query<span class="token punctuation">}</span> from ‘react<span class="token operator">-</span>apollo’<span class="token punctuation">;</span><br><br><span class="token keyword">const</span> <span class="token constant">MY_QUERY</span> <span class="token operator">=</span> gql<span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">query …{} </span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span><br><br><span class="token keyword">const</span> <span class="token function-variable function">ExamplePage</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">props</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> <span class="token keyword">return</span> <span class="token punctuation">(</span><br> <span class="token operator"><</span>View<span class="token operator">></span><br> <span class="token operator"><</span>Query query<span class="token operator">=</span><span class="token punctuation">{</span><span class="token constant">MY_QUERY</span><span class="token punctuation">}</span><span class="token operator">></span><br> <span class="token punctuation">{</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> loading<span class="token punctuation">,</span> error<span class="token punctuation">,</span> data <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span>loading<span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token operator"><</span>Text<span class="token operator">></span>Loading<span class="token operator"><</span><span class="token operator">/</span>Text<span class="token operator">></span><span class="token punctuation">;</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span>error<span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token operator"><</span>Text<span class="token operator">></span>Error<span class="token operator"><</span><span class="token operator">/</span>Text<span class="token operator">></span><span class="token punctuation">;</span><br> <span class="token keyword">return</span> <span class="token operator"><</span>Text<span class="token operator">></span><span class="token punctuation">{</span> <span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token operator"><</span><span class="token operator">/</span>Text<span class="token operator">></span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><span class="token punctuation">}</span><br> <span class="token operator"><</span><span class="token operator">/</span>Query<span class="token operator">></span><br> <span class="token operator"><</span><span class="token operator">/</span>View<span class="token operator">></span><br> <span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<p>Being so used to the model-view-controller way of architecting applications, this seemed (and, to be honest, still seems) like such an unnatural way to interface with an API. Granted, you can also use the <code>useQuery()</code> Javascript function from the <code>react-apollo</code> library, but it has its own pretty significant limitations too.</p>
<p>I also came across this issue when trying to use ActionCable with React. The <code>react-actioncable-provider</code> package is for subscribing to ActionCable channels <em>in your UI component</em>. If you just want to subscribe to a channel and not immediately display data on the page, the package isn’t what you need.</p>
<h2 id="components-will-update-whether-you-want-them-to-or-not" tabindex="-1">Components will update, whether you want them to or not <a class="header-anchor" href="https://bitsofco.de/what-i-wish-i-knew-about-react/">#</a></h2>
<p>Before I understood why and when components updated, it seemed like my entire application was stuck in an infinite loop. I would do something like make an API call right at the root of the component, which itself would update the state, causing the component to re-render, and the API call would be made again.</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> <span class="token function-variable function">BadExamplePage</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">props</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br><br> <span class="token keyword">const</span> <span class="token punctuation">[</span>user<span class="token punctuation">,</span> setUser<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">useState</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token function">getUser</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">u</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token function">setUser</span><span class="token punctuation">(</span>u<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token keyword">return</span> <span class="token punctuation">(</span><br> …<br> <span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<div class="warning code-addendum">Don't do this</div>
<p>If you use React, you’ll understand why this is a terrible idea. The <code>getUser()</code> function is called when the component updates. It then changes the state of the <code>user</code> variable, and this change causes the component to update, and so on and so on.</p>
<p>What I have since learned is that we can't always control exactly when or how often the component will re-render so, for use cases like this, we need <code>useEffect()</code>…</p>
<h3 id="useeffect-is-a-major" tabindex="-1">useEffect is a major 🔑 <a class="header-anchor" href="https://bitsofco.de/what-i-wish-i-knew-about-react/">#</a></h3>
<p><code>useEffect()</code> is like a safe haven to me in the confusing world of React components. It’s for those cases where you’re just like, “Can I just write Javascript that will execute once please?”.</p>
<p>Although by default, the code within a <code>useEffect()</code> block runs on each re-render, we can choose to execute them only when certain values have changed or just once. We do this by passing a second argument to <code>useEffect()</code>, with an array of variables that, if their value should change, we want the function to be run again.</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> <span class="token function-variable function">BetterExamplePage</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">props</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br><br> <span class="token keyword">const</span> <span class="token punctuation">[</span>user<span class="token punctuation">,</span> setUser<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">useState</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token function">useEffect</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> <span class="token function">getUser</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">u</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token function">setUser</span><span class="token punctuation">(</span>u<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">[</span> <span class="token comment">/* dependencies */</span> <span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token keyword">return</span> <span class="token punctuation">(</span><br> …<br> <span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<p>If we leave the array empty, that effectively means that the function will only run once.</p>
<p>I’m still on this React journey so I'm sure there’s still a lot for me to learn, but these are the things that I've found challenging so far.</p>
How I created 488 "live images"2019-11-26T00:00:00Zhttps://bitsofco.de/how-i-created-488-live-images/<p>I've recently been going down a rabbit hole of making improvements to my <a href="https://caniuse.bitsofco.de/">CanIUse embed</a>. To give a bit of a background, it is an interactive embed I created to easily embed data from <a href="https://caniuse.com">caniuse.com</a> in my blog posts and anywhere else. I previously wrote about <a href="https://bitsofco.de/caniuse-embed">how I first created the embed</a> and how I progressively enhanced it by adding a fallback screenshot <a href="https://bitsofco.de/using-a-headless-browser-to-capture-page-screenshots">captured with puppeteer</a> and <a href="https://bitsofco.de/how-to-upload-a-screenshot-from-puppeteer-to-cloudinary">hosted on Cloudinary</a>.</p>
<p>The next improvement on my path down this hole was to create what I’m calling "live images" for each feature on caniuse.com. What I wanted to do was have a URL linking to an image (hosted on Cloudinary) that I would periodically update with the support table for that feature. For example, to get the latest image of CSS Grid support, anyone could use this URL:</p>
<pre><code>https://caniuse.bitsofco.de/image/css-grid.png
</code></pre>
<p><img src="https://caniuse.bitsofco.de/image/css-grid.png" alt="Browser support for CSS Grid from caniuse.com"></p>
<p>That image will be periodically updated (right now I’m doing once a day) and could then be used in places where Javascript is completely unsupported, e.g. Github READMEs, or just as a fallback for the full blown embed.</p>
<p>Since it’s also hosted on Cloudinary, any file type is supported and can be accessed on the fly, simply by changing the extension.</p>
<pre><code>https://caniuse.bitsofco.de/image/css-grid.webp
https://caniuse.bitsofco.de/image/css-grid.jpeg
https://caniuse.bitsofco.de/image/css-grid.gif
</code></pre>
<p>Here’s how I did this!</p>
<h2 id="step-1-capture-488-images-using-puppeteer" tabindex="-1">Step 1: Capture 488 images using Puppeteer <a class="header-anchor" href="https://bitsofco.de/how-i-created-488-live-images/">#</a></h2>
<p>The first step is to create the images for the live embed, which involved capturing a screenshot of the embed page. I’ve covered in previous articles <a href="https://bitsofco.de/using-a-headless-browser-to-capture-page-screenshots">how to use puppeteer to capture a screenshot</a>, so I won’t go over that again here.</p>
<p>However, there are 488 features on caniuse.com, and there is one thing to mention about trying to take 488 screenshots using Puppeteer in a row. You need to be careful about <em>how</em> you’re navigating to 488 different pages with Puppeteer.</p>
<p>At first, I was launching a new <code>browser</code> then attempting to to open 488 different pages on that <code>browser</code>.</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> features <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token operator">...</span> <span class="token punctuation">]</span><span class="token punctuation">;</span><br><span class="token keyword">const</span> browser <span class="token operator">=</span> <span class="token keyword">await</span> puppeteer<span class="token punctuation">.</span><span class="token function">launch</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token operator">...</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br><span class="token keyword">const</span> screenshots <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span><br><br><span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">let</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator"><</span> features<span class="token punctuation">.</span>length<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br><br> <span class="token comment">// Opening a new page for each feature - didn't work</span><br> <span class="token keyword">const</span> page <span class="token operator">=</span> <span class="token keyword">await</span> browser<span class="token punctuation">.</span><span class="token function">newPage</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token keyword">await</span> page<span class="token punctuation">.</span><span class="token function">goto</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">https://caniuse.bitsofco.de/embed/index.html?feat=</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>features<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">&screenshot=true</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> screenshots<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br> <span class="token literal-property property">feature</span><span class="token operator">:</span> feature<span class="token punctuation">,</span><br> <span class="token literal-property property">screenshot</span><span class="token operator">:</span> <span class="token keyword">await</span> page<span class="token punctuation">.</span><span class="token function">screenshot</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token operator">...</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token keyword">await</span> browser<span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token keyword">return</span> screenshots<span class="token punctuation">;</span></code></pre>
<div class="warning code-addendum">Doesn't work</div>
<p>This didn’t work because, unsurprisingly, you can’t open 488 different pages on the <code>browser</code>. After about 12 pages, Puppeteer would crash. I was able to fix this by slightly changing this around and using the same <code>page</code> to navigate to the 488 different pages.</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> features <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token operator">...</span> <span class="token punctuation">]</span><span class="token punctuation">;</span><br><span class="token keyword">const</span> browser <span class="token operator">=</span> <span class="token keyword">await</span> puppeteer<span class="token punctuation">.</span><span class="token function">launch</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token operator">...</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br><span class="token comment">// Open a single page for all features</span><br><span class="token keyword">const</span> page <span class="token operator">=</span> <span class="token keyword">await</span> browser<span class="token punctuation">.</span><span class="token function">newPage</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br><span class="token keyword">const</span> screenshots <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span><br><br><span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">let</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator"><</span> features<span class="token punctuation">.</span>length<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">await</span> page<span class="token punctuation">.</span><span class="token function">goto</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">https://caniuse.bitsofco.de/embed/index.html?feat=</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>features<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">&screenshot=true</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> screenshots<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br> <span class="token literal-property property">feature</span><span class="token operator">:</span> feature<span class="token punctuation">,</span><br> <span class="token literal-property property">screenshot</span><span class="token operator">:</span> <span class="token keyword">await</span> page<span class="token punctuation">.</span><span class="token function">screenshot</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token operator">...</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token keyword">await</span> browser<span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token keyword">return</span> screenshots<span class="token punctuation">;</span></code></pre>
<div class="success code-addendum">Works!</div>
<h2 id="step-2-upload-488-images-to-cloudinary" tabindex="-1">Step 2: Upload 488 images to Cloudinary <a class="header-anchor" href="https://bitsofco.de/how-i-created-488-live-images/">#</a></h2>
<p>I have also covered how to <a href="https://bitsofco.de/how-to-upload-a-screenshot-from-puppeteer-to-cloudinary">upload images from Puppeteer to Cloudinary</a> in a previous article, so I won’t go into that here again. Luckily, there was no issue uploading 488 images to Cloudinary in one go, so I didn’t have to make any special considerations there.</p>
<p>The only thing I had to make sure I did was to make the file name of the image the exact slug as used by caniuse.com. So, for example, the slug for CSS Grid is <code>css-grid</code>, and that’s the file name for the image too.</p>
<p>I also had to ensure that any new images with that same name will override the previous image, and not just create another image. To do this, I set the file name by setting the <code>public_id</code> in the options passed.</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> options <span class="token operator">=</span> <span class="token punctuation">{</span><br> <span class="token literal-property property">folder</span><span class="token operator">:</span> <span class="token string">'caniuse-embed/all'</span><span class="token punctuation">,</span><br> <span class="token literal-property property">public_id</span><span class="token operator">:</span> feature<br><span class="token punctuation">}</span><span class="token punctuation">;</span><br><br>cloudinary<span class="token punctuation">.</span>uploader<span class="token punctuation">.</span><span class="token function">upload_stream</span><span class="token punctuation">(</span>options<span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">error<span class="token punctuation">,</span> result</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span>error<span class="token punctuation">)</span> <span class="token function">reject</span><span class="token punctuation">(</span>error<span class="token punctuation">)</span><br> <span class="token keyword">else</span> <span class="token function">resolve</span><span class="token punctuation">(</span>result<span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">end</span><span class="token punctuation">(</span>screenshot<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>With both those things covered, I was able to capture and upload all 488 images!</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/mvTzhxbqCQ-1618.avif 1618w"><source type="image/webp" srcset="https://bitsofco.de/img/mvTzhxbqCQ-1618.webp 1618w"><img alt="Console output of uploaded images" loading="lazy" decoding="async" src="https://bitsofco.de/img/mvTzhxbqCQ-1618.png" width="1618" height="1238"></picture></p>
<p>One thing you may have noticed is that Cloudinary adds a version number in the URL of any uploaded image. For example, a newly uploaded image may have the following URL:</p>
<pre><code>https://res.cloudinary.com/ireaderinokun/image/upload/v1574685647/caniuse-embed/all/css-grid.png
</code></pre>
<p>They do this to allow bypassing of a previously cached version of an image. So, if we upload an image with the exact same URL, Cloudinary can know which one to use. This, of course, presents a problem as I want to maintain the same URL for an image that will update. That’s where the next step comes in.</p>
<h2 id="step-3-create-url-redirects-on-cloudflare" tabindex="-1">Step 3: Create URL redirects on Cloudflare <a class="header-anchor" href="https://bitsofco.de/how-i-created-488-live-images/">#</a></h2>
<p>This is the step that ties everything together. Because of the version numbers in the URL of Cloudinary images, I didn’t want users of the live image to use the Cloudinary image path directly. I wanted a middle-man that will allow me to control the exact path of the image used. And that’s where Cloudflare comes in.</p>
<p>With Cloudflare, we can create Page Rules which allow us to create complex redirect paths. The Page Rule I created looks like this:</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/obiMKHHB_o-1586.avif 1586w"><source type="image/webp" srcset="https://bitsofco.de/img/obiMKHHB_o-1586.webp 1586w"><img alt="Page Rule:" loading="lazy" decoding="async" src="https://bitsofco.de/img/obiMKHHB_o-1586.png" width="1586" height="1052"></picture></p>
<p>If the URL matches: caniuse.bitsofco.de/image/*.*</p>
<p>Forward URL (302 - Temporary Redirect) to: res.cloudinary.com/ireaderinokun/image/upload/v1/caniuse-embed/all/$1.$2</p>
<p>The asterisks in the incoming URL are numbered according to their position, and can be used in the forwarding URL. So, the following URL:</p>
<pre><code>https://caniuse.bitsofco.de/image/css-grid.png
</code></pre>
<p>Will redirect to:</p>
<pre><code>https://res.cloudinary.com/ireaderinokun/image/upload/v1/caniuse-embed/all/css-grid.png
</code></pre>
<p>The version number on Cloudinary doesn’t actually have to be the same one that was given in the upload phase, so I can use any version number I like. I just need to make sure that I change it to a different version number every time I upload new images, so Cloudinary knows to override the cache.</p>
<p>To do this programatically, I made use of Cloudflare’s API. They have a pretty straightforward REST API for reading or updating existing page rules. For example, to get the page rules for a given site, we would make a GET request to the following URL:</p>
<pre><code>https://api.cloudflare.com/client/v4/zones/ZONE_ID/pagerules
</code></pre>
<p>The <code>ZONE_ID</code> is the ID for the site, in my case my bitsofco.de site. So, getting the page rules for bitsofco.de would look like this:</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> fetch <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'node-fetch'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br><span class="token keyword">const</span> url <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">https://api.cloudflare.com/client/v4/zones/BITSOFCODE_ZONE_ID_HERE/pagerules</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span><br><span class="token keyword">const</span> options <span class="token operator">=</span> <span class="token punctuation">{</span><br> <span class="token literal-property property">headers</span><span class="token operator">:</span> <span class="token punctuation">{</span><br> <span class="token string-property property">'Content-Type'</span><span class="token operator">:</span> <span class="token string">'application/json'</span><span class="token punctuation">,</span><br> <span class="token string-property property">'X-Auth-Email'</span><span class="token operator">:</span> process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">CLOUDFLARE_EMAIL</span><span class="token punctuation">,</span><br> <span class="token string-property property">'X-Auth-Key'</span><span class="token operator">:</span> process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">CLOUDFLARE_API_KEY</span><br> <span class="token punctuation">}</span><br><span class="token punctuation">}</span><span class="token punctuation">;</span><br><br><span class="token function">fetch</span><span class="token punctuation">(</span>url<span class="token punctuation">,</span> options<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>This will return something like this:</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> pageRules <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">{</span><br> <span class="token literal-property property">id</span><span class="token operator">:</span> <span class="token string">'bfb990382de1cfadb25b0dec7c113b27'</span><span class="token punctuation">,</span><br> <span class="token literal-property property">priority</span><span class="token operator">:</span> <span class="token number">3</span><span class="token punctuation">,</span><br> <span class="token literal-property property">status</span><span class="token operator">:</span> <span class="token string">'active'</span><span class="token punctuation">,</span><br> <span class="token literal-property property">targets</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">{</span><br> <span class="token literal-property property">target</span><span class="token operator">:</span> <span class="token string">'url'</span><span class="token punctuation">,</span><br> <span class="token literal-property property">constraint</span><span class="token operator">:</span> <span class="token punctuation">{</span><br> <span class="token literal-property property">operator</span><span class="token operator">:</span> <span class="token string">'matches'</span><span class="token punctuation">,</span><br> <span class="token literal-property property">value</span><span class="token operator">:</span> <span class="token string">'caniuse.bitsofco.de/image/*.*'</span><br> <span class="token punctuation">}</span><br> <span class="token punctuation">}</span><span class="token punctuation">]</span><span class="token punctuation">,</span><br> <span class="token literal-property property">actions</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">{</span><br> <span class="token literal-property property">id</span><span class="token operator">:</span> <span class="token string">'forwarding_url'</span><span class="token punctuation">,</span><br> <span class="token literal-property property">value</span><span class="token operator">:</span> <span class="token punctuation">{</span><br> <span class="token literal-property property">url</span><span class="token operator">:</span> <span class="token string">'https://res.cloudinary.com/ireaderinokun/image/upload/v1574685900077/caniuse-embed/all/$1.$2'</span><span class="token punctuation">,</span><br> <span class="token literal-property property">status_code</span><span class="token operator">:</span> <span class="token number">302</span><br> <span class="token punctuation">}</span><br> <span class="token punctuation">}</span><span class="token punctuation">]</span><br><span class="token punctuation">}</span><span class="token punctuation">]</span><span class="token punctuation">;</span></code></pre>
<p>To update a given page rule, we make a PUT request to the following URL:</p>
<pre><code>https://api.cloudflare.com/client/v4/zones/ZONE_ID/pagerules/RULE_ID
</code></pre>
<p>For example:</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> pageRule <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token comment">/* original page rule */</span> <span class="token punctuation">}</span><span class="token punctuation">;</span><br><br><span class="token keyword">const</span> url <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">https://api.cloudflare.com/client/v4/zones/BITSOFCODE_ZONE_ID_HERE/pagerules/bfb990382de1cfadb25b0dec7c113b27</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span><br><span class="token keyword">const</span> options <span class="token operator">=</span> <span class="token punctuation">{</span><br> <span class="token literal-property property">method</span><span class="token operator">:</span> <span class="token string">'PUT'</span><span class="token punctuation">,</span><br> <span class="token literal-property property">headers</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token operator">...</span> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token literal-property property">body</span><span class="token operator">:</span> <span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br> <span class="token literal-property property">targets</span><span class="token operator">:</span> pageRule<span class="token punctuation">.</span>targets<span class="token punctuation">,</span><br> <span class="token literal-property property">actions</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">{</span><br> <span class="token literal-property property">id</span><span class="token operator">:</span> pageRule<span class="token punctuation">.</span>actions<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">.</span>id<span class="token punctuation">,</span><br> <span class="token literal-property property">value</span><span class="token operator">:</span> <span class="token punctuation">{</span><br> <span class="token comment">/* use timestamp as version number */</span><br> <span class="token literal-property property">url</span><span class="token operator">:</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">https://res.cloudinary.com/ireaderinokun/image/upload/v</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token keyword">new</span> <span class="token class-name">Date</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getTime</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">/caniuse-embed/all/$1.$2</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">,</span><br> <span class="token literal-property property">status_code</span><span class="token operator">:</span> <span class="token number">302</span><br> <span class="token punctuation">}</span><br> <span class="token punctuation">}</span><span class="token punctuation">]</span><br> <span class="token punctuation">}</span><span class="token punctuation">)</span><br><span class="token punctuation">}</span><span class="token punctuation">;</span><br><br><span class="token function">fetch</span><span class="token punctuation">(</span>url<span class="token punctuation">,</span> options<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>So now, I can update the version number in the page rule immediately after I upload the new images.</p>
<h2 id="step-4-deploy-to-heroku-and-schedule-daily-job" tabindex="-1">Step 4: Deploy to Heroku and Schedule Daily Job <a class="header-anchor" href="https://bitsofco.de/how-i-created-488-live-images/">#</a></h2>
<p>Finally, I needed to set everything up on Heroku so the script could be run automatically and without needing to be on my machine. I didn’t need to make any changes to my code for it to run on Heroku, but I did need to install the following buildpacks:</p>
<table>
<thead>
<tr>
<th>Buildpack</th>
<th>Purpose</th>
</tr>
</thead>
<tbody>
<tr>
<td>heroku/nodejs</td>
<td>Run Node</td>
</tr>
<tr>
<td><a href="https://github.com/jontewks/puppeteer-heroku-buildpack.git">https://github.com/jontewks/puppeteer-heroku-buildpack.git</a></td>
<td>Puppeteer</td>
</tr>
<tr>
<td><a href="https://github.com/ello/heroku-buildpack-imagemagick">https://github.com/ello/heroku-buildpack-imagemagick</a></td>
<td>For trimming the images to remove whitespace (I didn’t cover that in this article)</td>
</tr>
</tbody>
</table>
<p>Then, I used <a href="https://devcenter.heroku.com/articles/scheduler">Heroku Scheduler</a> to schedule the script to run once each day.</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/7dfCVfa6T7-1974.avif 1974w"><source type="image/webp" srcset="https://bitsofco.de/img/7dfCVfa6T7-1974.webp 1974w"><img alt="Heroku Scheduler configuration" loading="lazy" decoding="async" src="https://bitsofco.de/img/7dfCVfa6T7-1974.png" width="1974" height="1082"></picture></p>
<p>And that's it! The entire project is hosted publicly on github at <a href="https://github.com/ireade/caniuse-embed-screenshot-api/">ireade/caniuse-embed-screenshot-api</a> so you can see how everything works together.</p>
<p>I'm really happy with how it all turned out, especially being able to automate the whole process so I never need to manually do anything myself!</p>
Highlights from Chrome Dev Summit 20192019-11-18T00:00:00Zhttps://bitsofco.de/chrome-dev-summit-2019/<p>I’ve been going to Chrome Dev Summit for a couple of years now (<a href="https://bitsofco.de/chrome-dev-summit-2016">2016</a> & <a href="https://bitsofco.de/chrome-dev-summit-2018/">2018</a>) and I always like to do this roundup article, highlighting what I thought were the most interesting ideas and projects from the conference. So let’s get into it!</p>
<h2 id="html-is-finally-getting-some" tabindex="-1">HTML is (finally) getting some 💕 <a class="header-anchor" href="https://bitsofco.de/chrome-dev-summit-2019/">#</a></h2>
<p>It seems like the design and functionality of pretty much all control elements have stayed the same since the dawn of the browser. This is true even despite the fact that the way we use browsers have changed a lot, for example with the shift to mobile. Well, no more! In the talk, <a href="https://www.youtube.com/watch?v=ZFvPLrKZywA&feature=emb_title">HTML isn’t done</a>, Nicole and Greg discussed a few updates that will be introduced to Chrome soon.</p>
<h3 id="redesigned-form-elements" tabindex="-1">Redesigned form elements <a class="header-anchor" href="https://bitsofco.de/chrome-dev-summit-2019/">#</a></h3>
<p>Several form elements are finally getting a visual refresh! Not only are the elements being designed to look more modern, but they are being designed with accessibility and mobile in mind. The date picker, for example, will now allow for larger touch targets when selecting values.</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/iSaQge_jEj-4032.avif 4032w"><source type="image/webp" srcset="https://bitsofco.de/img/iSaQge_jEj-4032.webp 4032w"><img alt="Redesigned date picker element" loading="lazy" decoding="async" src="https://bitsofco.de/img/iSaQge_jEj-4032.jpeg" width="4032" height="3024"></picture></p>
<h3 id="extensibility-of-form-elements" tabindex="-1">Extensibility of form elements <a class="header-anchor" href="https://bitsofco.de/chrome-dev-summit-2019/">#</a></h3>
<p>Form elements are also getting more flexible! An example of this is the <code><select></code> element. So many of us end up using custom select components because we want to do something as simple as adding an icon to an option, or something as complex as allowing users to search through options. In future versions of Chrome, this should all be possible with the native <code><select></code> element itself!</p>
<h3 id="new-elements" tabindex="-1">New elements? <a class="header-anchor" href="https://bitsofco.de/chrome-dev-summit-2019/">#</a></h3>
<p>Although this is more an improvement for the further-away future, the Chrome team are looking to add elements to the web that don’t exist yet. Elements like a table view similar to a UITableView in iOS, or a toggle switch element are in the pipeline.</p>
<h2 id="css-is-getting-better-too" tabindex="-1">CSS is getting better too! <a class="header-anchor" href="https://bitsofco.de/chrome-dev-summit-2019/">#</a></h2>
<p>CSS is also getting some much-needed refinements. In their packed talk on <a href="https://www.youtube.com/watch?v=-oyeaIirVC0&feature=emb_title">Next-generation web styling</a>, Una and Adam talked about 12 new features coming to CSS. Although there were many shiny features like Houdini mentioned, what had me really excited were the refinements that will allow us to write CSS more simply and effectively.</p>
<h3 id="the-is-selector" tabindex="-1">The <code>:is()</code> selector <a class="header-anchor" href="https://bitsofco.de/chrome-dev-summit-2019/">#</a></h3>
<p>The <code>:is()</code> selector allows us to group multiple alike selectors. This is better explained with an example.</p>
<p>Typically, if we want to apply the same style to multiple elements that share a part of their selector, we would still have to write out the entire selector each time.</p>
<pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">body.theme--light button,<br>body.theme--light a,<br>body.theme--light p,<br>body.theme--light h1</span> <span class="token punctuation">{</span><br> <span class="token comment">/* ... */</span><br><span class="token punctuation">}</span></code></pre>
<p>With <code>:is()</code>, we can group the parts of the selectors that are different together.</p>
<pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">body.theme--light :is(button, a, p, h1)</span> <span class="token punctuation">{</span><br> <span class="token comment">/* ... */</span><br><span class="token punctuation">}</span></code></pre>
<p>Amazing, right?!</p>
<h3 id="logical-properties" tabindex="-1">Logical properties <a class="header-anchor" href="https://bitsofco.de/chrome-dev-summit-2019/">#</a></h3>
<p>Logical properties are a move from CSS that is biased to left-to-right languages like English, to a more agnostic approach. Instead of thinking of the block model as top, right, bottom, left, it’s changing to block-start, inline-end, block-end, inline-start.</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/1d5hb01qaS-1542.avif 1542w"><source type="image/webp" srcset="https://bitsofco.de/img/1d5hb01qaS-1542.webp 1542w"><img alt="Logical-properties" loading="lazy" decoding="async" src="https://bitsofco.de/img/1d5hb01qaS-1542.png" width="1542" height="858"></picture></p>
<p>Credit: Una Kravets</p>
<p>This can be a little difficult to wrap your head around the first time, but once you get the hang of it, you see why it makes more sense. A lot of times, when we want a margin or a padding to the left, what we actually want is a margin/padding to the start of the inline block. With these logical properties, we can apply those kinds of styles that will adapt based on the language of the user. For times when left really means left, the directional properties are still there.</p>
<h2 id="the-web-is-coming-for-native" tabindex="-1">The web is coming for native <a class="header-anchor" href="https://bitsofco.de/chrome-dev-summit-2019/">#</a></h2>
<p>Through the new <a href="https://www.chromium.org/teams/web-capabilities-fugu">Project Fugu</a>, the Chromium team is aggressively trying to bridge the gap between the capabilities of web and native applications. There were two talks at the conference that really dove into the new APIs that are coming to the web.</p>
<p>In <a href="https://www.youtube.com/watch?v=WxXF17k1dko&feature=emb_title">What’s new in sign-up and sign-in</a>, Eiji talked about the new SMS Receiver API, which will allow browsers to access text messages received on the device to help auto-fill during the authentication process.</p>
<p>He also talked about the Web Authentication API, which will allow browsers to access local credential mechanisms such as FaceID. Using what is definitely the longest ever method name, we can check if one of these credentialm mechanisms are available:</p>
<pre class="language-js" tabindex="0"><code class="language-js">PublicKeyCredential<span class="token punctuation">.</span><span class="token function">isUserVerifyingPlatformAuthenticatorAvailable</span><span class="token punctuation">(</span><span class="token punctuation">)</span><br> <span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">available</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> <span class="token comment">// Proceed with authentication...</span><br> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>In <a href="https://www.youtube.com/watch?v=JKVZMqpiY7w&feature=emb_title">Bridging the native app gap</a>, Sam discussed several other APIs coming to the web soon, including:</p>
<ul>
<li>Web Share API</li>
<li>Web Share Target API</li>
<li>Contact Picker API</li>
<li>Native File System API</li>
</ul>
<p>In the sandbox area, I also witnessed some amazing demos of APIs like WebXR, Web Bluetooth, and Web NFC.</p>
<blockquote>
<p>WebAR demo at <a href="https://twitter.com/hashtag/ChromeDevSummit?src=hash&ref_src=twsrc%5Etfw">#ChromeDevSummit</a> ! It's really amazing what can be done in the browser nowadays, the WebVR demo was so powerful too! <a href="https://t.co/njrMyK0QeD">pic.twitter.com/njrMyK0QeD</a>
— Ire Aderinokun (@ireaderinokun) <a href="https://twitter.com/ireaderinokun/status/1194026341538004992?ref_src=twsrc%5Etfw">November 11, 2019</a></p>
</blockquote>
<h2 id="progressive-enhancement-through-adaptive-loading" tabindex="-1">Progressive enhancement through “adaptive loading” <a class="header-anchor" href="https://bitsofco.de/chrome-dev-summit-2019/">#</a></h2>
<p>I’m always interested in the topic of progressive enhancement and how we can better serve appropriate experiences based on the user’s capabilities. Addy Osmani introduced a a concept he called “<a href="https://www.youtube.com/watch?v=puUPpVrIRkc&feature=emb_title">Adaptive Loading</a>", which is the practice of adapting the experience for the user depending on their device/network/browser capabilities.</p>
<p>In order to create this adaptive experience, we can leverage several browser APIs that let us know what type of device/network/browser the user is currently on.</p>
<p>For example, with the Network Information API, we can determine if the user is on a 4g network, or if they have the data savings setting on.</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> network <span class="token operator">=</span> navigator<span class="token punctuation">.</span>connection<span class="token punctuation">.</span>effectiveType<span class="token punctuation">;</span><br><span class="token comment">// => 4g</span><br><br><span class="token keyword">const</span> isOnSaveData <span class="token operator">=</span> navigator<span class="token punctuation">.</span>connection<span class="token punctuation">.</span>saveData<span class="token punctuation">;</span><br><span class="token comment">// => true</span></code></pre>
<p>It’s worth noting that the support for these features isn’t widely available yet. That said, they can still be used for extra information wherever available.</p>
<p>That's it for my roundup! All the videos from the conference are available to watch on YouTube on the <a href="https://www.youtube.com/playlist?list=PLNYkxOF6rcIDA1uGhqy45bqlul0VcvKMr">Chrome Dev Summit 2019 playlist</a>. What were your favourite talks?</p>
How to use Puppeteer in a Netlify (AWS Lambda) function2019-10-28T00:00:00Zhttps://bitsofco.de/how-to-use-puppeteer-in-a-netlify-aws-lambda-function/<p>I recently gave a talk at JAMstack_conf San Francisco about how I used headless chrome (via Puppeteer) and Cloudinary to capture screenshots of my <a href="https://caniuse.bitsofco.de">interactive caniuse embed</a>. I did this to have a fallback image to use in case the embed couldn’t be loaded, for example in cases where Javascript couldn’t be run.</p>
<p>My talk, which you can <a href="https://www.youtube.com/watch?v=rm58d5proWI&list=PLkdLzUzD1Bv0DqK1niYv9X1FzPrvZnA7E">watch on YouTube</a>, focused on the code I wrote to capture the screenshot with Puppeteer and upload the screenshot to Cloudinary. Because it was just a 10-minute talk, I didn’t have time to go into where that server-side code was hosted, Netlify.</p>
<p>Using Puppeteer in a Netlify function requires some additional consideration, so in this article, I want to show a stripped down example of how to get everything working.</p>
<h2 id="puppeteer-101" tabindex="-1">Puppeteer 101 <a class="header-anchor" href="https://bitsofco.de/how-to-use-puppeteer-in-a-netlify-aws-lambda-function/">#</a></h2>
<p>Before getting into the Netlify portion of this, let’s briefly go over what we are trying to achieve with Puppeteer. In order to take a screenshot of a given URL with Puppeteer, we have to go through four steps:</p>
<ol>
<li>Launch a new browser</li>
<li>Open a new page</li>
<li>Navigate to the given URL</li>
<li>Capture the screenshot</li>
</ol>
<p>Here’s what that looks like:</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> puppeteer <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'puppeteer'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br><span class="token punctuation">(</span><span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br><br> <span class="token comment">// 1. Launch a new browser</span><br> <span class="token keyword">const</span> browser <span class="token operator">=</span> <span class="token keyword">await</span> puppeteer<span class="token punctuation">.</span><span class="token function">launch</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token comment">// 2. Open a new page</span><br> <span class="token keyword">const</span> page <span class="token operator">=</span> <span class="token keyword">await</span> browser<span class="token punctuation">.</span><span class="token function">newPage</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token comment">// 3. Navigate to the given URL</span><br> <span class="token keyword">await</span> page<span class="token punctuation">.</span><span class="token function">goto</span><span class="token punctuation">(</span><span class="token string">'https://bitsofco.de'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token comment">// 4. Take screenshot</span><br> <span class="token keyword">const</span> screenshot <span class="token operator">=</span> <span class="token keyword">await</span> page<span class="token punctuation">.</span><span class="token function">screenshot</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token literal-property property">encoding</span><span class="token operator">:</span> <span class="token string">'binary'</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token keyword">await</span> browser<span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>From there, we can do whatever we like with the <code>screenshot</code> variable. If you’re interested in seeing how I went from this to Cloudinary, you can read my other article on <a href="https://bitsofco.de/how-to-upload-a-screenshot-from-puppeteer-to-cloudinary">how to upload a screenshot from Puppeteer to Cloudinary</a>.</p>
<h2 id="netlify-functions-101" tabindex="-1">Netlify functions 101 <a class="header-anchor" href="https://bitsofco.de/how-to-use-puppeteer-in-a-netlify-aws-lambda-function/">#</a></h2>
<p>Now onto Netlify functions. These are node functions that can be called from our frontend website. They give us the power of a backend server, without having to worry about actually creating and maintaining a fully blown API. All we have to do is create a function file, for example <code>take-screenshot.js</code>, and we can call that function by making a request to the URL <code>/.netlify/functions/take-screenshot</code> from our frontend.</p>
<p>Here’s what that looks like. First, we create the <code>take-screenshot.js</code> function file. This typically lives in a <code>functions</code> directory in the Netlify project.</p>
<div class="code-heading">functions/take-screenshot/take-screenshot.js</div>
<pre class="language-js" tabindex="0"><code class="language-js">exports<span class="token punctuation">.</span><span class="token function-variable function">handler</span> <span class="token operator">=</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token parameter">event<span class="token punctuation">,</span> context</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> <span class="token comment">/* do stuff here */</span><br><span class="token punctuation">}</span></code></pre>
<p>The file exports one function, which is what is called when a request is made to the function. We have access to any arguments passed in the <code>event</code> variable. For example, if we were expecting a string, <code>pageToScreenshot</code>, which would define which URL of page to capture the screenshot of with cloudinary, we would be able to access that from the <code>event.body</code>.</p>
<div class="code-heading">functions/take-screenshot/take-screenshot.js</div>
<pre class="language-js" tabindex="0"><code class="language-js">exports<span class="token punctuation">.</span><span class="token function-variable function">handler</span> <span class="token operator">=</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token parameter">event<span class="token punctuation">,</span> context</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> <span class="token keyword">const</span> params <span class="token operator">=</span> <span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">parse</span><span class="token punctuation">(</span>event<span class="token punctuation">.</span>body<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">const</span> pageToScreenshot <span class="token operator">=</span> params<span class="token punctuation">.</span>pageToScreenshot<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre>
<p>To call this function from our frontend, we just need to make a request to the special Netlify functions path, <code>/.netlify/functions/take-screenshot</code>. Note that the name of the function file is used in the URL.</p>
<div class="code-heading">public/script.js</div>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> options <span class="token operator">=</span> <span class="token punctuation">{</span><br> <span class="token literal-property property">method</span><span class="token operator">:</span> <span class="token string">"POST"</span><span class="token punctuation">,</span><br> <span class="token literal-property property">headers</span><span class="token operator">:</span> <span class="token punctuation">{</span><br> <span class="token string-property property">"Content-Type"</span><span class="token operator">:</span> <span class="token string">"application/json; charset=utf-8"</span><br> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token literal-property property">body</span><span class="token operator">:</span> <span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br> <span class="token literal-property property">pageToScreenshot</span><span class="token operator">:</span> <span class="token string">"https://bitsofco.de"</span><br> <span class="token punctuation">}</span><span class="token punctuation">)</span><br><span class="token punctuation">}</span><span class="token punctuation">;</span><br><br><span class="token function">fetch</span><span class="token punctuation">(</span><span class="token string">"/.netlify/functions/take-screenshot"</span><span class="token punctuation">,</span> options<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<h2 id="putting-it-all-together" tabindex="-1">Putting it all together <a class="header-anchor" href="https://bitsofco.de/how-to-use-puppeteer-in-a-netlify-aws-lambda-function/">#</a></h2>
<p>Next, we need to put it all together. Although it should be as simple as moving the Puppeteer logic into the Netlify function file, there are a couple gotchas to be aware of.</p>
<h3 id="gotcha-1-puppeteer-vs-puppeteer-core" tabindex="-1">Gotcha 1: Puppeteer vs Puppeteer Core <a class="header-anchor" href="https://bitsofco.de/how-to-use-puppeteer-in-a-netlify-aws-lambda-function/">#</a></h3>
<p>Netlify functions have a maximum file size of 50MB. This means that we can’t actually use the full Puppeteer node library because it’s too large. Instead, we need to use <code>puppeteer-core</code>, which comes without any headless browser installed. Then we’ll need to add a lite version of chrome to use with it instead.</p>
<p>The two packages we need are <code>puppeteer-core</code> and <code>chrome-aws-lambda</code>.</p>
<div class="code-heading">functions/take-screenshot/take-screenshot.js</div>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> puppeteer <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'puppeteer-core'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token keyword">const</span> chromium <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'chrome-aws-lambda'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>We’ll also have to make a few changes to how we configure our browser. When launching the browser, we need to pass an <code>executablePath</code> option, so Puppeteer knows which browser to work with.</p>
<div class="code-heading">functions/take-screenshot/take-screenshot.js</div>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> puppeteer <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'puppeteer-core'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token keyword">const</span> chromium <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'chrome-aws-lambda'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br>exports<span class="token punctuation">.</span><span class="token function-variable function">handler</span> <span class="token operator">=</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token parameter">event<span class="token punctuation">,</span> context</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br><br> <span class="token comment">/* ... */</span><br><br> <span class="token keyword">const</span> browser <span class="token operator">=</span> <span class="token keyword">await</span> puppeteer<span class="token punctuation">.</span><span class="token function">launch</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br> <span class="token comment">// Required</span><br> <span class="token literal-property property">executablePath</span><span class="token operator">:</span> <span class="token keyword">await</span> chromium<span class="token punctuation">.</span>executablePath<span class="token punctuation">,</span><br><br> <span class="token comment">// Optional</span><br> <span class="token literal-property property">args</span><span class="token operator">:</span> chromium<span class="token punctuation">.</span>args<span class="token punctuation">,</span><br> <span class="token literal-property property">defaultViewport</span><span class="token operator">:</span> chromium<span class="token punctuation">.</span>defaultViewport<span class="token punctuation">,</span><br> <span class="token literal-property property">headless</span><span class="token operator">:</span> chromium<span class="token punctuation">.</span>headless<br> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre>
<p>We can also pass some other optional confirmation options that the <code>chromium</code> package defines as shown above.</p>
<h3 id="gotcha-2-local-development" tabindex="-1">Gotcha 2: Local development <a class="header-anchor" href="https://bitsofco.de/how-to-use-puppeteer-in-a-netlify-aws-lambda-function/">#</a></h3>
<p>Another thing to be aware of is that this probably won’t work locally. This is because, when working locally, the <code>chromium.headless</code> boolean will likely return false, which in turn means that <code>chromium.executablePath</code> will return <code>null</code>.</p>
<p>The best way I found to get around this is documented in the <a href="https://github.com/alixaxel/chrome-aws-lambda/wiki/HOWTO:-Local-Development">chrome-aws-lambda Wiki page</a>. They suggest the following changes:</p>
<ul>
<li>Install the full <code>puppeteer</code> package as a development dependency</li>
<li>Install <code>puppeteer-core</code> and <code>chrome-aws-lambda</code> as production dependencies</li>
<li>Access Puppeteer via the <code>chromium</code> package, which will determine which Puppeteer package to use</li>
</ul>
<p>Here’s what that looks like. First, we install our packages.</p>
<pre class="language-bash" tabindex="0"><code class="language-bash"><span class="token function">npm</span> <span class="token function">install</span> puppeteer --save-dev<br><span class="token function">npm</span> <span class="token function">install</span> puppeteer-core chrome-aws-lambda --save-prod</code></pre>
<p>Next, we access Puppeteer via the <code>chromium</code> package, which will determine which of the Puppeteer packages to use.</p>
<div class="code-heading">functions/take-screenshot/take-screenshot.js</div>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> chromium <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'chrome-aws-lambda'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br>exports<span class="token punctuation">.</span><span class="token function-variable function">handler</span> <span class="token operator">=</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token parameter">event<span class="token punctuation">,</span> context</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br><br> <span class="token comment">/* ... */</span><br><br> <span class="token keyword">const</span> browser <span class="token operator">=</span> <span class="token keyword">await</span> chromium<span class="token punctuation">.</span>puppeteer<span class="token punctuation">.</span><span class="token function">launch</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token comment">/* ... */</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre>
<p>So even though we are installing and saving two different Puppeteer packages to our function project's <code>package.json</code>, <em>we never directly access either package</em>.</p>
<h2 id="putting-it-all-together-again" tabindex="-1">Putting it all together (again) <a class="header-anchor" href="https://bitsofco.de/how-to-use-puppeteer-in-a-netlify-aws-lambda-function/">#</a></h2>
<p>Finally, we’re done! This is what the final <code>take-screenshot.js</code> function file looks like:</p>
<div class="code-heading">functions/take-screenshot/take-screenshot.js</div>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> chromium <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'chrome-aws-lambda'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br>exports<span class="token punctuation">.</span><span class="token function-variable function">handler</span> <span class="token operator">=</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token parameter">event<span class="token punctuation">,</span> context</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br><br> <span class="token keyword">const</span> pageToScreenshot <span class="token operator">=</span> <span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">parse</span><span class="token punctuation">(</span>event<span class="token punctuation">.</span>body<span class="token punctuation">)</span><span class="token punctuation">.</span>pageToScreenshot<span class="token punctuation">;</span><br><br> <span class="token keyword">const</span> browser <span class="token operator">=</span> <span class="token keyword">await</span> chromium<span class="token punctuation">.</span>puppeteer<span class="token punctuation">.</span><span class="token function">launch</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br> <span class="token literal-property property">executablePath</span><span class="token operator">:</span> <span class="token keyword">await</span> chromium<span class="token punctuation">.</span>executablePath<span class="token punctuation">,</span><br> <span class="token literal-property property">args</span><span class="token operator">:</span> chromium<span class="token punctuation">.</span>args<span class="token punctuation">,</span><br> <span class="token literal-property property">defaultViewport</span><span class="token operator">:</span> chromium<span class="token punctuation">.</span>defaultViewport<span class="token punctuation">,</span><br> <span class="token literal-property property">headless</span><span class="token operator">:</span> chromium<span class="token punctuation">.</span>headless<span class="token punctuation">,</span><br> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token keyword">const</span> page <span class="token operator">=</span> <span class="token keyword">await</span> browser<span class="token punctuation">.</span><span class="token function">newPage</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token keyword">await</span> page<span class="token punctuation">.</span><span class="token function">goto</span><span class="token punctuation">(</span>pageToScreenshot<span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token keyword">const</span> screenshot <span class="token operator">=</span> <span class="token keyword">await</span> page<span class="token punctuation">.</span><span class="token function">screenshot</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token literal-property property">encoding</span><span class="token operator">:</span> <span class="token string">'binary'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token keyword">await</span> browser<span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token keyword">return</span> <span class="token punctuation">{</span><br> <span class="token literal-property property">statusCode</span><span class="token operator">:</span> <span class="token number">200</span><span class="token punctuation">,</span><br> <span class="token literal-property property">body</span><span class="token operator">:</span> <span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br> <span class="token literal-property property">message</span><span class="token operator">:</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">Complete screenshot of </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>pageToScreenshot<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">,</span><br> <span class="token literal-property property">buffer</span><span class="token operator">:</span> screenshot<br> <span class="token punctuation">}</span><span class="token punctuation">)</span><br> <span class="token punctuation">}</span><br><br><span class="token punctuation">}</span></code></pre>
<p>To illustrate how it all works, I put together a simple website to demo all of this.</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/WtM-QLb8Dg-1972.avif 1972w"><source type="image/webp" srcset="https://bitsofco.de/img/WtM-QLb8Dg-1972.webp 1972w"><img alt="Screenshot of Demo page" loading="lazy" decoding="async" src="https://bitsofco.de/img/WtM-QLb8Dg-1972.png" width="1972" height="1492"></picture></p>
<p>You can visit the site at <a href="https://netlify-puppeteer-screenshot-demo.netlify.com/">netlify-puppeteer-screenshot-demo.netlify.com</a> and view the source code on <a href="https://github.com/ireade/netlify-puppeteer-screenshot-demo">GitHub</a>.</p>
Calling smart contract functions using web3.js - call() vs send()2019-09-03T00:00:00Zhttps://bitsofco.de/calling-smart-contract-functions-using-web3-js-call-vs-send/<p>I’ve recently been doing <a href="https://www.udacity.com/course/blockchain-developer-nanodegree--nd1309">Udacity’s Blockchain Developer Nanodegree</a>, and the gotcha that has caused me the most wasted hours is the difference between calling a method in my smart contract using the <code>call()</code> vs <code>send()</code> web3.js methods.</p>
<h2 id="using-web3-js-to-call-smart-contract-functions" tabindex="-1">Using web3.js to call smart contract functions <a class="header-anchor" href="https://bitsofco.de/calling-smart-contract-functions-using-web3-js-call-vs-send/">#</a></h2>
<p><a href="https://web3js.readthedocs.io/en/v1.2.0/getting-started.html">Web3.js</a> is a library that allows you to do a number of things related to developing for the ethereum ecosystem, including interacting with a smart contract from a frontend application.</p>
<p>Let’s say we have a simple smart contract that allows you to do just two things - get a string variable <code>name</code>, and set that <code>name</code> variable to a new string.</p>
<pre class="language-js" tabindex="0"><code class="language-js">pragma solidity <span class="token operator">>=</span><span class="token number">0.4</span><span class="token number">.24</span><span class="token punctuation">;</span><br><br>contract NameContract <span class="token punctuation">{</span><br><br> string <span class="token keyword">private</span> name <span class="token operator">=</span> <span class="token string">"Ire"</span><span class="token punctuation">;</span><br><br> <span class="token keyword">function</span> <span class="token function">getName</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">public</span> view <span class="token function">returns</span> <span class="token punctuation">(</span><span class="token parameter">string</span><span class="token punctuation">)</span><br> <span class="token punctuation">{</span><br> <span class="token keyword">return</span> name<span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br><br> <span class="token keyword">function</span> <span class="token function">setName</span><span class="token punctuation">(</span><span class="token parameter">string newName</span><span class="token punctuation">)</span> <span class="token keyword">public</span><br> <span class="token punctuation">{</span><br> name <span class="token operator">=</span> newName<span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br><br><span class="token punctuation">}</span></code></pre>
<p>In order to interact with this smart contract via a frontend application, we would first initialise web3.js with the smart contract ABI and address.</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">import</span> Web3 <span class="token keyword">from</span> <span class="token string">'web3'</span><span class="token punctuation">;</span><br><br><span class="token keyword">const</span> web3 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Web3</span><span class="token punctuation">(</span>window<span class="token punctuation">.</span>ethereum<span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token keyword">await</span> window<span class="token punctuation">.</span>ethereum<span class="token punctuation">.</span><span class="token function">enable</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br><span class="token keyword">const</span> NameContract <span class="token operator">=</span> web3<span class="token punctuation">.</span>eth<span class="token punctuation">.</span><span class="token function">Contract</span><span class="token punctuation">(</span>contract_abi<span class="token punctuation">,</span> contract_address<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Once initialised, we can call any of the methods of our smart contract using either the <code>call()</code> or <code>send()</code> methods.</p>
<pre class="language-js" tabindex="0"><code class="language-js">NameContract<span class="token punctuation">.</span>methods<span class="token punctuation">.</span><span class="token function">getName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br>NameContract<span class="token punctuation">.</span>methods<span class="token punctuation">.</span><span class="token function">setName</span><span class="token punctuation">(</span><span class="token string">"bitsofcode"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<h2 id="what-s-the-difference-between-call-and-send" tabindex="-1">What’s the difference between <code>call()</code> and <code>send()</code>? <a class="header-anchor" href="https://bitsofco.de/calling-smart-contract-functions-using-web3-js-call-vs-send/">#</a></h2>
<p>The difference between the <code>call()</code> and <code>send()</code> methods has to do with the type of function they are calling and their effect. Solidity functions can be divided into two categories:</p>
<ul>
<li>Functions that <strong>alter</strong> the state of the contract</li>
<li>Functions that <strong>do not alter</strong> the state of the contract</li>
</ul>
<p>To make it clear which category a function belongs to, we can specify the function type.</p>
<pre class="language-js" tabindex="0"><code class="language-js">contract MyContract <span class="token punctuation">{</span><br><br> <span class="token keyword">function</span> <span class="token function">myFunction</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">[</span>visibility<span class="token punctuation">]</span> <span class="token punctuation">[</span>type<span class="token punctuation">]</span> <span class="token punctuation">{</span><br> <span class="token comment">// do something</span><br> <span class="token punctuation">}</span><br><br><span class="token punctuation">}</span></code></pre>
<p>There are 4 function type keywords we can use, including leaving the function type unspecified.</p>
<table>
<thead>
<tr>
<th>&nsp;</th>
<th>Description</th>
<th>Alter's State?</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>pure</code></td>
<td>Doesn't read or write data</td>
<td>🚫</td>
</tr>
<tr>
<td><code>view</code></td>
<td>Reads, but doesn't write data</td>
<td>🚫</td>
</tr>
<tr>
<td><code>payable</code></td>
<td>Expecting payment of ether</td>
<td>✅</td>
</tr>
<tr>
<td>(unspecified)</td>
<td>Probably writes data</td>
<td>✅</td>
</tr>
</tbody>
</table>
<p>Going back to my example contract, we can see that the function <code>getName()</code> has the type <code>view</code> which means it does not change the state of the contract. When calling this function via web3.js, we should use the <code>call()</code> method.</p>
<pre class="language-js" tabindex="0"><code class="language-js">NameContract<span class="token punctuation">.</span>methods<span class="token punctuation">.</span><span class="token function">getName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>On the contrary, the <code>setName()</code> function doesn’t have a specified type and does change the state of the contract, in this case the value of the <code>name</code> variable. For these types of funtions, we should use the <code>send()</code> method.</p>
<pre class="language-js" tabindex="0"><code class="language-js">NameContract<span class="token punctuation">.</span>methods<span class="token punctuation">.</span><span class="token function">setName</span><span class="token punctuation">(</span><span class="token string">"bitsofcode"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<h2 id="what-s-the-effect-of-call-vs-send" tabindex="-1">What’s the effect of <code>call()</code> vs <code>send()</code>? <a class="header-anchor" href="https://bitsofco.de/calling-smart-contract-functions-using-web3-js-call-vs-send/">#</a></h2>
<p>The reason we have two separate methods for calling functions that do or do not alter the state of a smart contract is because the former requires a transaction on the blockchain and the expense of ether in order to be effective.</p>
<p>The ether required to execute a smart contract function is called “<strong>gas</strong>” and it is a transaction that the user of the application will have to accept. If the user is using an ethereum wallet such as the Metamask plugin in their browser, they will have to accept the expense of the transaction via a popup window.</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/t-yaH-ap29-2330.avif 2330w"><source type="image/webp" srcset="https://bitsofco.de/img/t-yaH-ap29-2330.webp 2330w"><img alt="Screenshot-2019-08-30-at-16.04.03" loading="lazy" decoding="async" src="https://bitsofco.de/img/t-yaH-ap29-2330.png" width="2330" height="1750"></picture></p>
<p>With functions called via the <code>call()</code> method on the other hand, they can happen silently in the background and do not require user input to complete.</p>
<table>
<thead>
<tr>
<th>&nsp;</th>
<th>Creates Transaction?</th>
<th>Alter's State?</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>call()</code></td>
<td>🚫</td>
<td>🚫</td>
</tr>
<tr>
<td><code>send()</code></td>
<td>✅</td>
<td>✅</td>
</tr>
</tbody>
</table>
<h2 id="what-happens-if-you-use-the-wrong-method" tabindex="-1">What happens if you use the wrong method? <a class="header-anchor" href="https://bitsofco.de/calling-smart-contract-functions-using-web3-js-call-vs-send/">#</a></h2>
<p>The reason this difference is such a big gotcha is because there is no explicit warning if you are using the wrong method to call your smart contract function.</p>
<p>For example, if I were to use the <code>call()</code> method to execute the <code>setName()</code> function, I would get no warnings.</p>
<pre class="language-js" tabindex="0"><code class="language-js">NameContract<span class="token punctuation">.</span>methods<span class="token punctuation">.</span><span class="token function">setName</span><span class="token punctuation">(</span><span class="token string">"bitsofcode"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<div class="code-addendum warning">Will not work</div>
<p>Ostensibly, the method would have successfully completed with no errors. However, if we were to query the <code>name</code> variable that is supposed to have changed, we will see that it was not changed.</p>
<pre class="language-js" tabindex="0"><code class="language-js">NameContract<span class="token punctuation">.</span>methods<span class="token punctuation">.</span><span class="token function">getName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// => "Ire"</span><br><br>NameContract<span class="token punctuation">.</span>methods<span class="token punctuation">.</span><span class="token function">setName</span><span class="token punctuation">(</span><span class="token string">"bitsofcode"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br>NameContract<span class="token punctuation">.</span>methods<span class="token punctuation">.</span><span class="token function">getName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// => "Ire"</span></code></pre>
<p>This is why I spent hours trying to debug why my function wasn't working, when in fact the problem was I was using the wrong method to call it!</p>
Solidity function visibility, explained2019-08-27T00:00:00Zhttps://bitsofco.de/solidity-function-visibility-explained/<p>When writing a smart contract, we can control who/what can call functions within that contract by specifying what is known as the function visibility. This allows us to easily secure certain parts of a contract without having to write too much custom logic.</p>
<h2 id="who-can-call-a-smart-contract-function" tabindex="-1">Who can call a smart contract function? <a class="header-anchor" href="https://bitsofco.de/solidity-function-visibility-explained/">#</a></h2>
<p>Before getting into the different types of function visibility, it’s helpful to understand what the different categories of potential callers to a function are first.</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/XpvKE0oiIq-718.avif 718w"><source type="image/webp" srcset="https://bitsofco.de/img/XpvKE0oiIq-718.webp 718w"><img alt="Actors-2" loading="lazy" decoding="async" src="https://bitsofco.de/img/XpvKE0oiIq-718.png" width="718" height="102"></picture></p>
<p>There are three types of callers:</p>
<ol>
<li>The main contract itself (<code>MyContract</code>)</li>
<li>A contract derived (i.e. inheriting) from the main contract (<code>DerivedContract</code>)</li>
<li>A third party (<code>AnotherContract</code>)</li>
</ol>
<p>The visibility of a function determines what subset of these callers can legitimately execute the function.</p>
<h2 id="function-visibility-types" tabindex="-1">Function visibility types <a class="header-anchor" href="https://bitsofco.de/solidity-function-visibility-explained/">#</a></h2>
<p>The visibility of a smart contract function is specified using one of the four visibility keywords (<code>private</code>, <code>internal</code>, <code>external</code>, or <code>public</code>) and placed immediately following the function parameter list.</p>
<pre class="language-js" tabindex="0"><code class="language-js">contract MyContract <span class="token punctuation">{</span><br><br> <span class="token keyword">function</span> <span class="token function">myFunction</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">[</span>visibility<span class="token operator">-</span>here<span class="token punctuation">]</span> <span class="token punctuation">{</span><br> <span class="token comment">// do something</span><br> <span class="token punctuation">}</span><br><br><span class="token punctuation">}</span></code></pre>
<h3 id="private" tabindex="-1"><code>private</code> <a class="header-anchor" href="https://bitsofco.de/solidity-function-visibility-explained/">#</a></h3>
<p>A <code>private</code> function is one that can only be called by the main contract itself. Although it’s not the default, it is generally good practice to keep your functions private unless a scope with more visibility is needed.</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/QuCRIe-w7w-718.avif 718w"><source type="image/webp" srcset="https://bitsofco.de/img/QuCRIe-w7w-718.webp 718w"><img alt="private-1" loading="lazy" decoding="async" src="https://bitsofco.de/img/QuCRIe-w7w-718.png" width="718" height="259"></picture></p>
<h3 id="internal" tabindex="-1"><code>internal</code> <a class="header-anchor" href="https://bitsofco.de/solidity-function-visibility-explained/">#</a></h3>
<p>An <code>internal</code> function can be called by the main contract itself, plus any derived contracts. As with <code>private</code> functions, it's generally a good idea to keep your functions <code>internal</code> wherever possible.</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/T4tRWTPKxI-718.avif 718w"><source type="image/webp" srcset="https://bitsofco.de/img/T4tRWTPKxI-718.webp 718w"><img alt="internal" loading="lazy" decoding="async" src="https://bitsofco.de/img/T4tRWTPKxI-718.png" width="718" height="259"></picture></p>
<h3 id="external" tabindex="-1"><code>external</code> <a class="header-anchor" href="https://bitsofco.de/solidity-function-visibility-explained/">#</a></h3>
<p>An <code>external</code> function can only be called from a third party. It cannot be called from the main contract itself or any contracts derived from it.</p>
<p>External functions have the benefit that they can be more performant due to the fact that their arguments do not need to be copied to memory. So, where possible, it's advisable to keep logic that only needs to be accessed by an external party to an <code>external</code> function.</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/wlXVUrBuqJ-718.avif 718w"><source type="image/webp" srcset="https://bitsofco.de/img/wlXVUrBuqJ-718.webp 718w"><img alt="external" loading="lazy" decoding="async" src="https://bitsofco.de/img/wlXVUrBuqJ-718.png" width="718" height="259"></picture></p>
<h3 id="public" tabindex="-1"><code>public</code> <a class="header-anchor" href="https://bitsofco.de/solidity-function-visibility-explained/">#</a></h3>
<p>Finally, a <code>public</code> function can be called from all potential parties. Unless otherwise specified, all functions are made public by default.</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/pT9tIWnPLF-718.avif 718w"><source type="image/webp" srcset="https://bitsofco.de/img/pT9tIWnPLF-718.webp 718w"><img alt="public-1" loading="lazy" decoding="async" src="https://bitsofco.de/img/pT9tIWnPLF-718.png" width="718" height="259"></picture></p>
Your first performance budget with Lighthouse2019-05-28T00:00:00Zhttps://bitsofco.de/your-first-performance-budget-with-lighthouse/<p>I asked on Twitter the other day how many people had created and enforced a performance budget for a website they were working on. Not surprisingly, the vast majority of people hadn't.</p>
<blockquote>
<p>I'm curious, have you ever created (and enforced) a performance budget for a site you're working on?
— Ire Aderinokun (@ireaderinokun) <a href="https://twitter.com/ireaderinokun/status/1131847762948050945?ref_src=twsrc%5Etfw">May 24, 2019</a></p>
</blockquote>
<p>Until recently, I also hadn't setup an official performance budget and enforced it. This isn’t to say that I never did performance audits. I frequently use tools like PageSpeed Insights and take the feedback to make improvements. But what I had never done was set a list of metrics that I needed the site to meet, and enforce them using some automated tool.</p>
<p>The reasons for this were a combination of not knowing what exact numbers I should use for budgets as well as there being a disconnect between setting a budget and testing/enforcing it. This is why I was really excited when, at Google I/O this year, the Lighthouse team announced support for performance budgets that can be integrated with Lighthouse. We can now define a simple performance budget in a JSON file, which will be tested as part of the lighthouse audit!</p>
<h2 id="the-lighthouse-performance-budget" tabindex="-1">The Lighthouse performance budget <a class="header-anchor" href="https://bitsofco.de/your-first-performance-budget-with-lighthouse/">#</a></h2>
<p>As we know, there are countless performance metrics out there. From user-centric milestones such as Time to Interactive, to resource-centric metrics such as the size of your javascript files.</p>
<p>The performance budget we can set on Lighthouse focuses on the latter and is purely based on the raw values of your website’s assets and resources. There are two types of budgets we can create:</p>
<ol>
<li><strong>The number of a given resource</strong> - e.g., a budget which enforces that only 2 javascript files be loaded by the given page</li>
<li><strong>The size of a given resource</strong> - e.g., a budget which enforces that the given page only loads 500kb of javascript</li>
</ol>
<h3 id="creating-the-budget-json" tabindex="-1">Creating the <code>budget.json</code> <a class="header-anchor" href="https://bitsofco.de/your-first-performance-budget-with-lighthouse/">#</a></h3>
<p>Lighthouse expects the performance budget to be written in a JSON file, typically named <code>budget.json</code>.</p>
<p>The JSON file is setup as an array of objects, each object being a set of budgets for a single path. It should be noted that currently, Lighthouse only supports one set of budgets for one path, which means that the array in <code>budgets.json</code> should only have one object. However, the intention is that <a href="https://github.com/GoogleChrome/lighthouse/issues/9046">we'll be able to specify budgets for different paths in a single file</a>, so it's setup this way for future proofing.</p>
<p>The main budget object is organised by budget type (<code>resourceCounts</code> or <code>resourceSizes</code>), each containing an array of budgets for each type of resource. For example, let’s create a <code>budget.json</code> that enforces the following budget -</p>
<ol>
<li>10 third-party resources</li>
<li>300kb Javascript</li>
<li>100kb CSS</li>
</ol>
<p>Here’s what the corresponding <code>budget.json</code> would look like:</p>
<pre class="language-json" tabindex="0"><code class="language-json"><span class="token punctuation">[</span><br> <span class="token punctuation">{</span><br> <span class="token property">"resourceCounts"</span><span class="token operator">:</span> <span class="token punctuation">[</span><br> <span class="token punctuation">{</span><span class="token property">"resourceType"</span><span class="token operator">:</span> <span class="token string">"third-party"</span><span class="token punctuation">,</span><span class="token property">"budget"</span><span class="token operator">:</span> <span class="token number">10</span><span class="token punctuation">}</span><br> <span class="token punctuation">]</span><span class="token punctuation">,</span><br> <span class="token property">"resourceSizes"</span><span class="token operator">:</span> <span class="token punctuation">[</span><br> <span class="token punctuation">{</span><span class="token property">"resourceType"</span><span class="token operator">:</span> <span class="token string">"script"</span><span class="token punctuation">,</span><span class="token property">"budget"</span><span class="token operator">:</span> <span class="token number">300</span><span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token punctuation">{</span><span class="token property">"resourceType"</span><span class="token operator">:</span> <span class="token string">"stylesheet"</span><span class="token punctuation">,</span><span class="token property">"budget"</span><span class="token operator">:</span> <span class="token number">100</span><span class="token punctuation">}</span><br> <span class="token punctuation">]</span><br> <span class="token punctuation">}</span><br><span class="token punctuation">]</span></code></pre>
<p>The following resource types are available:</p>
<table>
<thead>
<tr>
<th>Resource type</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>document</code></td>
<td>HTML documents</td>
</tr>
<tr>
<td><code>script</code></td>
<td>Javascript files</td>
</tr>
<tr>
<td><code>stylesheet</code></td>
<td>CSS files</td>
</tr>
<tr>
<td><code>image</code></td>
<td>Images</td>
</tr>
<tr>
<td><code>media</code></td>
<td>Other media</td>
</tr>
<tr>
<td><code>font</code></td>
<td>Webfonts</td>
</tr>
<tr>
<td><code>other</code></td>
<td>Any resources that don’t match the above, e.g. data transfers over Websocket connections</td>
</tr>
<tr>
<td><code>third-party</code></td>
<td>All resources from a third-party domain</td>
</tr>
<tr>
<td><code>total</code></td>
<td>All resources combined</td>
</tr>
</tbody>
</table>
<h2 id="choosing-a-performance-budget" tabindex="-1">Choosing a performance budget <a class="header-anchor" href="https://bitsofco.de/your-first-performance-budget-with-lighthouse/">#</a></h2>
<p>Now that we know <em>how</em> to create a performance budget, the next question is what should we put in it? Although the answer to this will change dramatically depending on the specifics of your site, you can use this <a href="https://perf-budget-calculator.firebaseapp.com/">Performance Budget Calculator</a> to get an idea of what different budgets will mean for user-centric metrics such as time to interactive.</p>
<p>For the example, the default budget below will result in an estimated 4.4s time to interactive:</p>
<pre class="language-json" tabindex="0"><code class="language-json"><span class="token punctuation">[</span><br> <span class="token punctuation">{</span><br> <span class="token property">"resourceSizes"</span><span class="token operator">:</span> <span class="token punctuation">[</span><br> <span class="token punctuation">{</span><span class="token property">"resourceType"</span><span class="token operator">:</span><span class="token string">"document"</span><span class="token punctuation">,</span><span class="token property">"budget"</span><span class="token operator">:</span><span class="token number">20</span><span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token punctuation">{</span><span class="token property">"resourceType"</span><span class="token operator">:</span><span class="token string">"stylesheet"</span><span class="token punctuation">,</span><span class="token property">"budget"</span><span class="token operator">:</span><span class="token number">50</span><span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token punctuation">{</span><span class="token property">"resourceType"</span><span class="token operator">:</span><span class="token string">"font"</span><span class="token punctuation">,</span><span class="token property">"budget"</span><span class="token operator">:</span><span class="token number">50</span><span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token punctuation">{</span><span class="token property">"resourceType"</span><span class="token operator">:</span><span class="token string">"image"</span><span class="token punctuation">,</span><span class="token property">"budget"</span><span class="token operator">:</span><span class="token number">300</span><span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token punctuation">{</span><span class="token property">"resourceType"</span><span class="token operator">:</span><span class="token string">"script"</span><span class="token punctuation">,</span><span class="token property">"budget"</span><span class="token operator">:</span><span class="token number">100</span><span class="token punctuation">}</span><br> <span class="token punctuation">]</span><br> <span class="token punctuation">}</span><br><span class="token punctuation">]</span></code></pre>
<h2 id="enforcing-the-performance-budget-with-lighthouse" tabindex="-1">Enforcing the performance budget with Lighthouse <a class="header-anchor" href="https://bitsofco.de/your-first-performance-budget-with-lighthouse/">#</a></h2>
<p>At the moment, the performance budget can only be consumed by the Lighthouse CLI. This means that we can’t yet use it with the in-browser audit panel or online tools like PageSpeed Insights or Web.dev. These integrations are being worked on, but for now, there are primarily 2 ways you’ll want to use the Lighthouse performance budget.</p>
<h3 id="basic-command-line-usage" tabindex="-1">Basic command line usage <a class="header-anchor" href="https://bitsofco.de/your-first-performance-budget-with-lighthouse/">#</a></h3>
<p>The simplest way is to install the Lighthouse CLI, and run the audit command, passing in your <code>budget.json</code> file as an option.</p>
<pre class="language-bash" tabindex="0"><code class="language-bash">lighthouse <span class="token punctuation">[</span>url<span class="token punctuation">]</span> --budget-path<span class="token operator">=</span><span class="token punctuation">[</span>path/to/budget.json<span class="token punctuation">]</span></code></pre>
<p>For example:</p>
<pre class="language-bash" tabindex="0"><code class="language-bash">lighthouse https://bitsofco.de --budget-path<span class="token operator">=</span>budget.json</code></pre>
<p>This command will trigger the lighthouse audit and will output the typical lighthouse results page with one addition - a section on how your page performs against your specified budgets.</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/_eXyVm5j9P-1986.avif 1986w"><source type="image/webp" srcset="https://bitsofco.de/img/_eXyVm5j9P-1986.webp 1986w"><img alt="Lighthouse results with budgets" loading="lazy" decoding="async" src="https://bitsofco.de/img/_eXyVm5j9P-1986.png" width="1986" height="1372"></picture></p>
<h3 id="continuous-integration" tabindex="-1">Continuous integration <a class="header-anchor" href="https://bitsofco.de/your-first-performance-budget-with-lighthouse/">#</a></h3>
<p>Because the audits are available using the command line interface, they can be used wherever the CLI can be installed. This means we can include it in our continuous integration process.</p>
<p>There’s an excellent <a href="https://web.dev/using-lighthouse-bot-to-set-a-performance-budget">article on web.dev</a> that shows how you can setup the Lighthouse Bot to run checks against GitHub Pull Requests.</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/9ba2zSQf2w-1704.avif 1704w"><source type="image/webp" srcset="https://bitsofco.de/img/9ba2zSQf2w-1704.webp 1704w"><img alt="Lighthouse audit check on Github" loading="lazy" decoding="async" src="https://bitsofco.de/img/9ba2zSQf2w-1704.png" width="1704" height="748"></picture></p>
<p>Image from github.com/GoogleChromeLabs/lighthousebot</p>
<p>This is probably the best way to set things up so the checks happen automatically.</p>
Removing duplicate objects from an Array (is hard)2019-03-12T00:00:00Zhttps://bitsofco.de/removing-duplicate-objects-from-an-array-is-hard/<p>Let’s say we have an array of objects such as the following:</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> books <span class="token operator">=</span> <span class="token punctuation">[</span><br> <span class="token punctuation">{</span><br> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">"My Sister the Serial Killer"</span><span class="token punctuation">,</span> <br> <span class="token literal-property property">author</span><span class="token operator">:</span> <span class="token string">"Oyinkan Braithwaite"</span> <br> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token punctuation">{</span><br> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">"Educated"</span><span class="token punctuation">,</span> <br> <span class="token literal-property property">author</span><span class="token operator">:</span> <span class="token string">"Tara Westover"</span> <br> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token punctuation">{</span><br> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">"My Sister the Serial Killer"</span><span class="token punctuation">,</span> <br> <span class="token literal-property property">author</span><span class="token operator">:</span> <span class="token string">"Oyinkan Braithwaite"</span> <br> <span class="token punctuation">}</span><br><span class="token punctuation">]</span><span class="token punctuation">;</span></code></pre>
<p>The first and the last objects in the array are identical. So what if we want to remove such duplicate objects from the array? Surprisingly, this is quite a difficult problem to solve. To understand why, let’s look at how we could remove duplicates from an Array of flat items, such as strings.</p>
<h2 id="removing-duplicate-flat-items-from-an-array-is-easy" tabindex="-1">Removing duplicate flat items from an Array (is easy) <a class="header-anchor" href="https://bitsofco.de/removing-duplicate-objects-from-an-array-is-hard/">#</a></h2>
<p>Let’s say we had an Array of strings such as the following:</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> strings <span class="token operator">=</span> <span class="token punctuation">[</span><br> <span class="token string">"My Sister the Serial Killer"</span><span class="token punctuation">,</span> <br> <span class="token string">"Educated"</span><span class="token punctuation">,</span> <br> <span class="token string">"My Sister the Serial Killer"</span><br><span class="token punctuation">]</span><span class="token punctuation">;</span></code></pre>
<p>If we wanted to remove any duplicates from this array, we could use the <code>filter()</code> method along with the <code>indexOf()</code> method to check if any given item is a duplicate.</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> filteredStrings <span class="token operator">=</span> strings<span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">item<span class="token punctuation">,</span> index</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br><br> <span class="token comment">// Return to new array if the index of the current item is the same </span><br> <span class="token comment">// as the first occurence of the item</span><br> <span class="token keyword">return</span> strings<span class="token punctuation">.</span><span class="token function">indexOf</span><span class="token punctuation">(</span>item<span class="token punctuation">)</span> <span class="token operator">===</span> index<span class="token punctuation">;</span><br><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Since <code>strings.indexOf(item)</code> will always return the index of the first occurrence of the <code>item</code>, we can tell if the current item within the filter loop is a duplicate. If it is, we don't return it to the new array created by the <code>filter()</code> method</p>
<h2 id="objects-don-t-work-the-same-way" tabindex="-1">Objects don’t work the same way <a class="header-anchor" href="https://bitsofco.de/removing-duplicate-objects-from-an-array-is-hard/">#</a></h2>
<p>The reason this same method doesn’t work with objects is because any 2 objects that have identical properties and values are not actually considered identical.</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> a <span class="token operator">=</span> <span class="token punctuation">{</span><br> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">"My Sister the Serial Killer"</span><span class="token punctuation">,</span> <br> <span class="token literal-property property">author</span><span class="token operator">:</span> <span class="token string">"Oyinkan Braithwaite"</span> <br><span class="token punctuation">}</span><span class="token punctuation">;</span><br><span class="token keyword">const</span> b <span class="token operator">=</span> <span class="token punctuation">{</span><br> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">"My Sister the Serial Killer"</span><span class="token punctuation">,</span> <br> <span class="token literal-property property">author</span><span class="token operator">:</span> <span class="token string">"Oyinkan Braithwaite"</span> <br><span class="token punctuation">}</span><span class="token punctuation">;</span><br><br>a <span class="token operator">===</span> b <span class="token comment">// false</span></code></pre>
<p>This is because objects are compared based on <em>reference</em> rather than <em>structure</em>. The fact that the two objects have the same orperties and values is not considered when comparing the two. Therefore, the <code>indexOf(object)</code> within an array of objects will always return the index of the exact <code>object</code> passed, even if there exists another object with the exact same properties and values.</p>
<h2 id="my-solution" tabindex="-1">My solution <a class="header-anchor" href="https://bitsofco.de/removing-duplicate-objects-from-an-array-is-hard/">#</a></h2>
<p>Given this information, the only way to check if two objects have the same properties and values is to actually check the properties and values of each object. The solution I came up with involved doing this manual check, but with some enhacements to improve performance and reduce unnecessary nested loops.</p>
<p>In particular, I did 3 things -</p>
<ol>
<li>Only check each item in the array against every other item that comes after it in order to avoid comparing the same objects more than once</li>
<li>Only check items that have not been found to be a duplicate of any other item</li>
<li>Before checking if the values of each property are the same, check that both objects have the same keys</li>
</ol>
<p>Here’s the final function:</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">function</span> <span class="token function">removeDuplicates</span><span class="token punctuation">(</span><span class="token parameter">arr</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br><br> <span class="token keyword">const</span> result <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span><br> <span class="token keyword">const</span> duplicatesIndices <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span><br><br> <span class="token comment">// Loop through each item in the original array</span><br> arr<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">current<span class="token punctuation">,</span> index</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> <br> <span class="token keyword">if</span> <span class="token punctuation">(</span>duplicatesIndices<span class="token punctuation">.</span><span class="token function">includes</span><span class="token punctuation">(</span>index<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">return</span><span class="token punctuation">;</span><br> <br> result<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>current<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <br> <span class="token comment">// Loop through each other item on array after the current one</span><br> <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">let</span> comparisonIndex <span class="token operator">=</span> index <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">;</span> comparisonIndex <span class="token operator"><</span> arr<span class="token punctuation">.</span>length<span class="token punctuation">;</span> comparisonIndex<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <br> <span class="token keyword">const</span> comparison <span class="token operator">=</span> arr<span class="token punctuation">[</span>comparisonIndex<span class="token punctuation">]</span><span class="token punctuation">;</span><br> <span class="token keyword">const</span> currentKeys <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">keys</span><span class="token punctuation">(</span>current<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">const</span> comparisonKeys <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">keys</span><span class="token punctuation">(</span>comparison<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <br> <span class="token comment">// Check number of keys in objects</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span>currentKeys<span class="token punctuation">.</span>length <span class="token operator">!==</span> comparisonKeys<span class="token punctuation">.</span>length<span class="token punctuation">)</span> <span class="token keyword">continue</span><span class="token punctuation">;</span><br> <br> <span class="token comment">// Check key names</span><br> <span class="token keyword">const</span> currentKeysString <span class="token operator">=</span> currentKeys<span class="token punctuation">.</span><span class="token function">sort</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">join</span><span class="token punctuation">(</span><span class="token string">""</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toLowerCase</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">const</span> comparisonKeysString <span class="token operator">=</span> comparisonKeys<span class="token punctuation">.</span><span class="token function">sort</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">join</span><span class="token punctuation">(</span><span class="token string">""</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toLowerCase</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span>currentKeysString <span class="token operator">!==</span> comparisonKeysString<span class="token punctuation">)</span> <span class="token keyword">continue</span><span class="token punctuation">;</span><br> <br> <span class="token comment">// Check values</span><br> <span class="token keyword">let</span> valuesEqual <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">;</span><br> <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">let</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator"><</span> currentKeys<span class="token punctuation">.</span>length<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">const</span> key <span class="token operator">=</span> currentKeys<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">;</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span> current<span class="token punctuation">[</span>key<span class="token punctuation">]</span> <span class="token operator">!==</span> comparison<span class="token punctuation">[</span>key<span class="token punctuation">]</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span><br> valuesEqual <span class="token operator">=</span> <span class="token boolean">false</span><span class="token punctuation">;</span><br> <span class="token keyword">break</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br> <span class="token punctuation">}</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span>valuesEqual<span class="token punctuation">)</span> duplicatesIndices<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>comparisonIndex<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <br> <span class="token punctuation">}</span> <span class="token comment">// end for loop</span><br><br> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// end arr.forEach()</span><br> <br> <span class="token keyword">return</span> result<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre>
Tips for making interactive elements accessible on mobile devices2019-02-12T00:00:00Zhttps://bitsofco.de/tips-for-making-interactive-elements-accessible-on-mobile-devices/<p>The <a href="https://www.w3.org/TR/WCAG21/">Web Content Accessibility Guidelines</a> (<abbr>WCAG</abbr>) is a set a guidelines for how to make websites accessible. The second version was published in 2008, before websites were available on mobile devices in any meaningful way.</p>
<p>A few months ago, the WCAG was updated to version 2.1, which includes a whole new section on <a href="https://www.w3.org/TR/mobile-accessibility-mapping/">guidelines for mobile accessibility</a>. There are several new mobile-specific rules, such as <a href="https://www.w3.org/TR/WCAG21/#orientation">rule 1.3.4</a>, that “content should not restrict its view and operation to a single display orientation, such as portrait or landscape, unless a specific display orientation is essential.”</p>
<p>In this article, I will cover some of the new guidelines that relate to interactive elements on a web page.</p>
<h2 id="place-interactive-elements-where-they-can-be-easily-accessed" tabindex="-1">Place interactive elements where they can be easily accessed <a class="header-anchor" href="https://bitsofco.de/tips-for-making-interactive-elements-accessible-on-mobile-devices/">#</a></h2>
<p>The first thing to consider with mobile devices is where any interactive/actionable elements are placed. Compared to the relatively rigid way we interact with desktop devices, mobile devices can be and are used in many different ways - one hand, two hands, left hand, right hand, etc.</p>
<p>Due to this dynamic handling of mobile devices, this guideline suggests to create interfaces that are flexible. For example, it may be easy to think that placing a call-to-action in the bottom right corner is best because most users may use their device with their right hand. However, this would be the most inconvenient place to put it for left-handed users. A better placement for a critical call-to-action could be spanning the entire width of the viewport.</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/zjwdiri1O7-1815.avif 1815w"><source type="image/webp" srcset="https://bitsofco.de/img/zjwdiri1O7-1815.webp 1815w"><img alt="Full-width button better than smaller, side-aligned button" loading="lazy" decoding="async" src="https://bitsofco.de/img/zjwdiri1O7-1815.jpeg" width="1815" height="1253"></picture></p>
<h2 id="make-it-clear-that-an-element-is-actionable" tabindex="-1">Make it clear that an element is actionable <a class="header-anchor" href="https://bitsofco.de/tips-for-making-interactive-elements-accessible-on-mobile-devices/">#</a></h2>
<p>Compared to desktop, it can be a bit more challenging to let a user know that an element is actionable on touchscreen devices. Without hover states, we have to use the upfront look of the element alone to convey that a user can interact with it.</p>
<p>The best way to convey that an element is actionable is by relying on convention and what actionable elements are expected to look like. For example, <a href="https://bitsofco.de/tips-for-making-interactive-elements-accessible-on-mobile-devices/">links</a> are typically underlined and/or have a distinct colour from the body text and <button>buttons</button> are usually a rectangular shape with rounded corners.</p>
<p>We can also use the position of an element to convey it’s use. The navigation, for example, is typically at the top of the page for conventional websites. For mobile devices, a navigation fixed to the bottom of the viewport is also common.</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/1hzaIhsmbR-2263.avif 2263w"><source type="image/webp" srcset="https://bitsofco.de/img/1hzaIhsmbR-2263.webp 2263w"><img alt="Navigation on instagram on desktop compared to mobile" loading="lazy" decoding="async" src="https://bitsofco.de/img/1hzaIhsmbR-2263.jpeg" width="2263" height="1558"></picture></p>
<p>On Instagram desktop, the navigation is at the top of the page, whereas on mobile, it is in a bar at the bottom of the viewport</p>
<h2 id="provide-instructions-for-custom-gestures" tabindex="-1">Provide instructions for custom gestures <a class="header-anchor" href="https://bitsofco.de/tips-for-making-interactive-elements-accessible-on-mobile-devices/">#</a></h2>
<p>Following on from the previous guideline, it’s also more important on touchscreen devices to provide clear instructions on the gestures available.</p>
<p>A good example of this is the pull to refresh gesture. Unless there is some indication that this gesture is possible, users will never interact with it. On mobile twitter, they have an icon that pops up when the user scrolls to the top of the page, showing that there is an interaction available if they pull up more.</p>
<figure>
<video autoplay="" loop="" muted="" playsinline="" height="500" poster="https://res.cloudinary.com/ireaderinokun/video/upload/v1549360984/bitsofcode/Pull_to_refresh.jpg">
<source type="video/webm" src="https://res.cloudinary.com/ireaderinokun/video/upload/v1549360984/bitsofcode/Pull_to_refresh.webm">
<img src="https://res.cloudinary.com/ireaderinokun/video/upload/v1549360984/bitsofcode/Pull_to_refresh.gif" alt="Twitter pull to refresh">
</video>
<figcaption>Scrolling up on a profile page on Twitter mobile will reveal an icon to pull to refresh</figcaption>
</figure>
<h2 id="provide-a-reasonable-touch-target-size-and-spacing" tabindex="-1">Provide a reasonable touch target size and spacing <a class="header-anchor" href="https://bitsofco.de/tips-for-making-interactive-elements-accessible-on-mobile-devices/">#</a></h2>
<p>On touchscreen devices, the pointer is the user’s finger which, of course, is going to be a lot less precise that a mouse. Therefore, the area in which a user can tap to activate an interactive element needs to be big enough to accommodate different finger sizes.</p>
<p>According to <a href="https://www.w3.org/TR/WCAG21/#target-size">guideline 2.5.5</a>, actionable elements should generally have a size of at least 44px by 44px, unless the target element is inline within a block of text.</p>
<h2 id="group-elements-that-perform-the-same-action" tabindex="-1">Group elements that perform the same action <a class="header-anchor" href="https://bitsofco.de/tips-for-making-interactive-elements-accessible-on-mobile-devices/">#</a></h2>
<p>This guideline is best illustrated with an example. Let’s say we have a navigation link going to the website homepage, and beside it we also have a home icon. For layout purposes, we may want to separate the two elements and have two links going to the same place.</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>img</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/home/png<span class="token punctuation">"</span></span> <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Home icon<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Home<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></code></pre>
<p>Not advisable</p>
<p>This method has two main drawbacks:</p>
<ol>
<li>It reduces the touch target space, since the space between the two links will not be clickable</li>
<li>It increases the number of elements that would need to be traversed through for a keyboard user. If navigating the page by tabbing on the keyboard, this duplication could become tedious if repeated numerous times</li>
</ol>
<p>A better alternative would be to group both elements under the same interactive link.</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>img</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/home/png<span class="token punctuation">"</span></span> <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Home icon<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br> Home<br><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></code></pre>
<p>Better!</p>
<p>This not only increases the touch target size for the link, it reduces the number of links that will need to be navigated through on the page.</p>
<h2 id="make-typing-easier-with-custom-keyboard-layouts" tabindex="-1">Make typing easier with custom keyboard layouts <a class="header-anchor" href="https://bitsofco.de/tips-for-making-interactive-elements-accessible-on-mobile-devices/">#</a></h2>
<p>One big hassle with touchscreen devices is typing on onscreen keyboards. We can make this a little easier by providing a custom keyboard layout that is specific to the type of input required.</p>
<p>For example, if a numeric input is required, we can use a number-only keyboard layout by specifying the <code>type</code> on the <code><input></code> element.</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>label</span><span class="token punctuation">></span></span><br> How old are you?<br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>number<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></code></pre>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/8Fr4J6o54l-1815.avif 1815w"><source type="image/webp" srcset="https://bitsofco.de/img/8Fr4J6o54l-1815.webp 1815w"><img alt="Numeric keyboard layout" loading="lazy" decoding="async" src="https://bitsofco.de/img/8Fr4J6o54l-1815.png" width="1815" height="1253"></picture></p>
<p>In some cases, we need the <code>type</code> of the <code><input></code> element to be one thing, e.g. <code>text</code>, but we still want to restrict the keyboard layout to a numeric one. This can be achieved using the <code>inputmode</code> attribute, which allows us to specify what particular keyboard layout we want for the input, regardless of the input <code>type</code>.</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>label</span><span class="token punctuation">></span></span><br> How old are you?<br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>text<span class="token punctuation">"</span></span> <span class="token attr-name">inputmode</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>numeric<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span></code></pre>
<p>At the time of writing, the <code>inputmode</code> attribute is <a href="https://caniuse.com/#feat=input-inputmode">not very well supported</a>.</p>
<h2 id="provide-alternatives-to-typing" tabindex="-1">Provide alternatives to typing <a class="header-anchor" href="https://bitsofco.de/tips-for-making-interactive-elements-accessible-on-mobile-devices/">#</a></h2>
<p>Another way to handle the hassle of typing on a touchscreen device is to reduce the amount of typing the user needs to do in the first place.</p>
<p>Generally speaking, on mobile devices, inputs such as checkboxes, radios, and selects are easier to work with on mobile than plain text boxes.</p>
<p>In places that need text entry, we can try to automatically fill out information. For example, by pulling location data to fill out addresses.</p>
Revisiting the abbr element2019-02-05T00:00:00Zhttps://bitsofco.de/revisiting-the-abbr-element/<p>A few weeks ago, I wrote about how to <a href="https://bitsofco.de/making-abbr-work-for-touchscreen-keyboard-mouse">make the <code>abbr</code> element work for touchscreen, keyboard, and mouse</a>. My suggestion involved making the <code><abbr></code> element focusable with the <code>tabindex</code> attribute and using pseudo-elements to create a tooltip from the <code>title</code> attribute.</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>abbr</span> <span class="token attr-name">title</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Cascading Style Sheets<span class="token punctuation">"</span></span> <span class="token attr-name">tabindex</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>CSS<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>abbr</span><span class="token punctuation">></span></span></code></pre>
<pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">abbr[title]:hover::after,<br>abbr[title]:focus::after</span> <span class="token punctuation">{</span><br> <span class="token property">content</span><span class="token punctuation">:</span> <span class="token function">attr</span><span class="token punctuation">(</span>title<span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token comment">/* more styles to position the elelent like a tooltip */</span><br><span class="token punctuation">}</span></code></pre>
<p>Since then, I’ve gotten some feedback on some accessibility-related issues to the implementation. <a href="https://twitter.com/scottohara">Scott O’Hara</a> in particular raised a lot of important concerns, which can be summarised in the following points:</p>
<ol>
<li>It’s generally a bad idea to add <code>tabindex</code> to non-interactive elements for a few reasons:
<ol>
<li>It’s unexpected behaviour. Especially as there is no associated <code>role</code>, users of assistive technology may be left wondering why it is in the focus order since it’s not an interactive element.</li>
<li>If there are many instances of it, it makes navigating the DOM a pain for keyboard users as they have to traverse through each one.</li>
<li>Having focus move to the <code><abbr></code> element resulted in some awkward behaviour for users of the <a href="https://www.nvaccess.org/">NVDA</a> screen reader. With Firefox for instance, the content of the <code><abbr></code> would be would announced on first focus, but if re-navigating to an <code><abbr></code>, NVDA might not announce anything.</li>
<li>While not a popular pairing, IE11 and NVDA would go into forms mode when an <code><abbr></code> with a <code>tabindex</code> received focus.</li>
</ol>
</li>
<li>There’s no acceptable way to dismiss the tooltip. Particularly on touch devices where the user would have to click on another focusable element. This would fail the new <a href="https://www.w3.org/WAI/WCAG21/Understanding/content-on-hover-or-focus.html">WCAG 1.4.13 rule</a>.</li>
<li>There can be duplication of content for users of the <a href="https://www.freedomscientific.com/Products/Blindness/JAWS">JAWS</a> screen reader who already have a setting to announce the <code>title</code> attribute of <code><abbr></code> elements.</li>
</ol>
<p>With this feedback, I decided to revisit this and create a more accessible solution.</p>
<h2 id="a-quick-aside-avoid-tooltips" tabindex="-1">A quick aside - avoid tooltips! <a class="header-anchor" href="https://bitsofco.de/revisiting-the-abbr-element/">#</a></h2>
<p>Before I go on to how I attempted to fix my original solution, it should be noted that, if all you want to use a tooltip for is to provide the full description of an abbreviation, you’re probably better off just providing it inline.</p>
<p>The first time an abbreviation is used, spell it out entirely.</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Cascading Style Sheets (<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>abbr</span><span class="token punctuation">></span></span>CSS<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>abbr</span><span class="token punctuation">></span></span>)<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></code></pre>
<p>Everywhere else in the document, you can use the <code><abbr></code> element as normal. Since the abbreviation has already been defined earlier in the document, the limitations of the native <code><abbr></code> element are not so harmful.</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>abbr</span> <span class="token attr-name">title</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Cascading Style Sheets<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>CSS<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>abbr</span><span class="token punctuation">></span></span> is awesome.<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></code></pre>
<p>For most use cases, this is probably the simplest solution and provides an adequate experience for all users.</p>
<h2 id="enhancing-the-abbr-element" tabindex="-1">Enhancing the <code>abbr</code> element <a class="header-anchor" href="https://bitsofco.de/revisiting-the-abbr-element/">#</a></h2>
<p>All of that said, here’s my attempt at enhancing the <code><abbr></code> element and creating a tooltip experience that works for touchscreen, keyboard, and mouse. This solution isn’t perfect, as I discuss below under the <a href="https://bitsofco.de/revisiting-the-abbr-element/">limitations</a>, but I wanted to make an attempt anyway :)</p>
<h3 id="refactoring-the-markup" tabindex="-1">Refactoring the markup <a class="header-anchor" href="https://bitsofco.de/revisiting-the-abbr-element/">#</a></h3>
<p>One thing I gathered from the feedback was that the actual <code><abbr></code> element will never be acceptable to use if we want to create the experience of the tooltip. So, I needed to change the actual markup to look something more like this:</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">data-tooltip</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#tt<span class="token punctuation">"</span></span> <span class="token attr-name">aria-describedby</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>tt<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>abbr</span><span class="token punctuation">></span></span>CSS<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>abbr</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>tt<span class="token punctuation">"</span></span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>tooltip<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>hidden<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br> Cascading Style Sheets<br> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></code></pre>
<p>To start with, I changed the element that contains the abbreviation to a <code><a></code>, which links to the tooltip. I used an <code><a></code> element because I needed an element that was actually interactive, instead of applying <code>tabindex</code> to a non-interactive element.</p>
<p>Next, I made the tooltip itself an element within the HTML (as opposed to a CSS pseudo-element), and added the <code>tooltip</code> ARIA role so it’s clear what its purpose is. To comply with the <a href="https://www.w3.org/TR/wai-aria-1.1/#tooltip">tooltip requirements</a>, I added an <code>aria-describedby</code> attribute that references the tooltip itself.</p>
<h3 id="handling-events" tabindex="-1">Handling events <a class="header-anchor" href="https://bitsofco.de/revisiting-the-abbr-element/">#</a></h3>
<p>In order to make the tooltip usable by touchscreen, keyboard, and mouse users, I needed a lot of event listeners.</p>
<table>
<thead>
<tr>
<th>Event</th>
<th>Action</th>
<th>Input device</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>mouseenter</code></td>
<td>Show Tooltip</td>
<td>Mouse</td>
</tr>
<tr>
<td><code>mouseleave</code></td>
<td>Hide Tooltip</td>
<td>Mouse, Touchscreen</td>
</tr>
<tr>
<td><code>keyup</code></td>
<td>Show Tooltip</td>
<td>Keyboard</td>
</tr>
<tr>
<td><code>keydown</code></td>
<td>Hide Tooltip</td>
<td>Keyboard</td>
</tr>
<tr>
<td><code>click</code></td>
<td>Show Tooltip</td>
<td>Touchscreen</td>
</tr>
</tbody>
</table>
<p>Besides the <code>mouseenter</code> and <code>mouseleave</code> events, I could listen for each of these events on the <code>document</code> and show/hide the tooltip depending on the element that triggered the event. For example, this is what the <code>keyup</code> event looks like.</p>
<pre class="language-js" tabindex="0"><code class="language-js">document<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">"keyup"</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">e</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span>e<span class="token punctuation">.</span>keyCode <span class="token operator">===</span> <span class="token number">9</span> <span class="token operator">&&</span> e<span class="token punctuation">.</span>target<span class="token punctuation">.</span>parentElement<span class="token punctuation">.</span><span class="token function">hasAttribute</span><span class="token punctuation">(</span><span class="token string">"data-tooltip"</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token function">showTooltip</span><span class="token punctuation">(</span>e<span class="token punctuation">.</span>target<span class="token punctuation">.</span>parentElement<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<h3 id="showing-and-hiding-the-tooltip" tabindex="-1">Showing and hiding the tooltip <a class="header-anchor" href="https://bitsofco.de/revisiting-the-abbr-element/">#</a></h3>
<p>To show or hide the tooltip, I simply toggled the <code>.hidden</code> class, which sets the <code>display</code> to <code>none</code>.</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">function</span> <span class="token function">showTooltip</span><span class="token punctuation">(</span><span class="token parameter">tt</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> tt<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">"[role='tooltip']"</span><span class="token punctuation">)</span><span class="token punctuation">.</span>classList<span class="token punctuation">.</span><span class="token function">remove</span><span class="token punctuation">(</span><span class="token string">"hidden"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token keyword">function</span> <span class="token function">hideTooltip</span><span class="token punctuation">(</span><span class="token parameter">tt</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> tt<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">"[role='tooltip']"</span><span class="token punctuation">)</span><span class="token punctuation">.</span>classList<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">"hidden"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre>
<h3 id="progressive-enhancement-and-automation" tabindex="-1">Progressive enhancement and automation <a class="header-anchor" href="https://bitsofco.de/revisiting-the-abbr-element/">#</a></h3>
<p>Because the functionality of the tooltip requires Javascript to work, I wanted a case where the written markup would be a plain <code><abbr></code> element and, only if Javascript is enabled, would the element be changed to the refactored tooltip markup above.</p>
<p>So, I wrote a small script that would take a regular <code><abbr></code>, and change it to the markup required. Here’s what that script looks like -</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> abbrs <span class="token operator">=</span> Array<span class="token punctuation">.</span><span class="token function">from</span><span class="token punctuation">(</span>document<span class="token punctuation">.</span><span class="token function">querySelectorAll</span><span class="token punctuation">(</span><span class="token string">"abbr[data-tooltip]"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br>abbrs<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">abbr<span class="token punctuation">,</span> index</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br><br> <span class="token keyword">const</span> tooltipContainer <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">createElement</span><span class="token punctuation">(</span><span class="token string">"span"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> tooltipContainer<span class="token punctuation">.</span><span class="token function">setAttribute</span><span class="token punctuation">(</span><span class="token string">"data-tooltip"</span><span class="token punctuation">,</span> <span class="token string">""</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> tooltipContainer<span class="token punctuation">.</span>innerHTML <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string"><br> <a href="#tt-</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>index<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">" aria-describedby="tt-</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>index<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">"><br> <abbr></span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>abbr<span class="token punctuation">.</span>innerHTML<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"></abbr><br> </a><br> <span role="tooltip" id="tt-</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>index<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">" class="hidden"></span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>abbr<span class="token punctuation">.</span><span class="token function">getAttribute</span><span class="token punctuation">(</span><span class="token string">"title"</span><span class="token punctuation">)</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"></span><br> </span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span><br><br> abbr<span class="token punctuation">.</span>parentElement<span class="token punctuation">.</span><span class="token function">replaceChild</span><span class="token punctuation">(</span>tooltipContainer<span class="token punctuation">,</span> abbr<span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> tooltipContainer<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">"mouseenter"</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">e</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token function">showTooltip</span><span class="token punctuation">(</span>tooltipContainer<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> tooltipContainer<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">"mouseleave"</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">e</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token function">hideTooltip</span><span class="token punctuation">(</span>tooltipContainer<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>So, without Javascript enabled, my markup would simply look like this -</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>abbr</span> <span class="token attr-name">data-tooltip</span> <span class="token attr-name">title</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Cascading Style Sheets<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>CSS<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>abbr</span><span class="token punctuation">></span></span></code></pre>
<p>This is easy to write and maintain. If Javascript is enabled, the markup will be changed to the tooltip.</p>
<p>You can view the full project on <a href="https://codepen.io/ire/pen/NoqWpm?editors=1010">CodePen</a> to see how this works.</p>
<h2 id="limitations" tabindex="-1">Limitations <a class="header-anchor" href="https://bitsofco.de/revisiting-the-abbr-element/">#</a></h2>
<p>As I mentioned, this implementation isn’t without it’s limitations.</p>
<p>For example, <strong>using the <code><a></code> element here may not be the most semantic, as it is not really intended to be a link</strong>. I used the <code><a></code> because I wanted an element that was interactive, not because I wanted anyone to be able to link to the tooltip itself. There doesn't seem to be a widely-accepted implementation of tooltips at the moment, so I just went with what I thought was best given. An alternative to this would be to use a <code><button></code> instead, which will require specifying that it controls another element via the <code>aria-controls</code> attribute.</p>
<p>On the other hand, the fact that a link is used could be leveraged on to provide more useful information. For example, instead of linking to the tooltip itself, the link could go to a page with more information about the abbreviated word.</p>
<p>Additionally, <strong>breaking up the DOM into too many links / interactive elements is still a concern</strong>. Even if this tooltip were to be used, it should probably only be used for the very first instance of the abbreviation on the page.</p>
What self.skipWaiting() does to the service worker lifecycle2019-01-29T00:00:00Zhttps://bitsofco.de/what-self-skipwaiting-does-to-the-service-worker-lifecycle/<p>The <code>ServiceWorker.skipWaiting()</code> method is a life saver. It ensures that any new versions of a service worker will take over the page and become activated immediately.</p>
<p>To understand why this is so important and useful, we need to revisit the service worker lifecycle. I have a more detailed article about each step of the lifecycle, but you just need to know that there are six states that a service worker can be in - parsed, installing, installed (waiting), activating, activated, and redundant.</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/DwOvba2074-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/DwOvba2074-780.webp 780w"><img alt="Service Worker States" loading="lazy" decoding="async" src="https://bitsofco.de/img/DwOvba2074-780.png" width="780" height="273"></picture></p>
<p>When we first try to register a service worker via <code>navigator.serviceWorker.register()</code>, the file passed is parsed and, assuming there are no errors, the service worker is installed. For a page with no previous service worker files, the newly installed service worker becomes activated immediately.</p>
<p>If, however, the page already has an activated service worker file, things are more complicated.</p>
<h2 id="endless-waiting" tabindex="-1">Endless waiting <a class="header-anchor" href="https://bitsofco.de/what-self-skipwaiting-does-to-the-service-worker-lifecycle/">#</a></h2>
<p>If the page already has an activated service worker and a new file is pushed, the new file will still be parsed and installed. Once installed, it will wait for an opportunity to become activated.</p>
<p>Without <code>self.skipWaiting()</code>, a waiting service worker will only become active itself once the currently active service worker is released and becomes redundant. This can only happen in two scenarios:</p>
<ul>
<li>If the user has navigated away from the page, thereby releasing the previous active worker</li>
<li>If a specified period of time has passed, thereby releasing the previous active worker</li>
</ul>
<p>So, unlike we are used to when pushing new versions of assets to our website, a new version of a service worker can be waiting for what seems like forever. Even if a user refreshes their page, they may not receive the updated version for a very long time.</p>
<h2 id="self-skipwaiting-to-the-rescue" tabindex="-1"><code>self.skipWaiting()</code> to the rescue! <a class="header-anchor" href="https://bitsofco.de/what-self-skipwaiting-does-to-the-service-worker-lifecycle/">#</a></h2>
<p>The <code>ServiceWorker.skipWaiting()</code> method solves this problem by telling the newly installed service worker to skip the waiting state and move directly to activating.</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/y2sbZYG-wY-759.avif 759w"><source type="image/webp" srcset="https://bitsofco.de/img/y2sbZYG-wY-759.webp 759w"><img alt="Waiting phase is skipped" loading="lazy" decoding="async" src="https://bitsofco.de/img/y2sbZYG-wY-759.png" width="759" height="189"></picture></p>
<p>The <code>self.skipWaiting()</code> method is typically used in the <code>install</code> event of the service worker. As long as the method is called before the waiting phase itself, the service worker will skip the waiting phase and become immediately activated.</p>
<div class="code-heading">service-worker.js</div>
<pre class="language-js" tabindex="0"><code class="language-js">self<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'install'</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">event</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br><br> self<span class="token punctuation">.</span><span class="token function">skipWaiting</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> event<span class="token punctuation">.</span><span class="token function">waitUntil</span><span class="token punctuation">(</span><br> <span class="token comment">// Do stuff</span><br> <span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
What is tree shaking and how does it work?2019-01-22T00:00:00Zhttps://bitsofco.de/what-is-tree-shaking/<p>When Javascript applications get to a certain size, it’s helpful to separate the code into modules. However, when we do so, we can end up with code imported that isn’t actually used. Tree shaking is a method of optimising our code bundles by eliminating any code from the final file that isn’t actually being used.</p>
<p>Let’s say we have a utilities file with some math operations we may want to use in our main script.</p>
<div class="code-heading">mathUtils.js</div>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token function">add</span><span class="token punctuation">(</span><span class="token parameter">a<span class="token punctuation">,</span> b</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"add"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">return</span> a <span class="token operator">+</span> b<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token function">minus</span><span class="token punctuation">(</span><span class="token parameter">a<span class="token punctuation">,</span> b</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"minus"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">return</span> a <span class="token operator">-</span> b<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token function">multiply</span><span class="token punctuation">(</span><span class="token parameter">a<span class="token punctuation">,</span> b</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"multiply"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">return</span> a <span class="token operator">*</span> b<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token function">divide</span><span class="token punctuation">(</span><span class="token parameter">a<span class="token punctuation">,</span> b</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"divide"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">return</span> a <span class="token operator">/</span> b<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre>
<p>In our main script, we may only ever import and use the <code>add()</code> function.</p>
<div class="code-heading">index.js</div>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">import</span> <span class="token punctuation">{</span> add <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"./mathUtils"</span><span class="token punctuation">;</span><br><br><span class="token function">add</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Assuming we are using a tool like webpack to create our bundle, we will see that all functions from the <code>mathUtils.js</code> file are included in the final bundle, even though we only imported and used the <code>add()</code> function.</p>
<div class="code-heading">bundle.js</div>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token comment">/***/</span> <span class="token string">"./src/mathUtils.js"</span><span class="token operator">:</span><br><span class="token comment">/*!**************************!*\<br> !*** ./src/mathUtils.js ***!<br> \**************************/</span><br><span class="token comment">/*! exports provided: add, minus, multiply, divide */</span><br><span class="token comment">/***/</span> <span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">module<span class="token punctuation">,</span> __webpack_exports__<span class="token punctuation">,</span> __webpack_require__</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br><br><span class="token string">"use strict"</span><span class="token punctuation">;</span><br><span class="token function">eval</span><span class="token punctuation">(</span><span class="token string">"__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"add\", function() { return add; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"minus\", function() { return minus; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"multiply\", function() { return multiply; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"divide\", function() { return divide; });\nfunction add(a, b) {\n console.log(\"add\");\n return a + b;\n}\n\nfunction minus(a, b) {\n console.log(\"minus\");\n return a - b;\n}\n\nfunction multiply(a, b) {\n console.log(\"multiply\");\n return a * b;\n}\n\nfunction divide(a, b) {\n console.log(\"divide\");\n return a / b;\n}\n\n\n//# sourceURL=webpack:///./src/mathUtils.js?"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br><span class="token comment">/***/</span> <span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre>
<p>With tree shaking enabled, only what is imported and actually used will make it to the final bundle.</p>
<h2 id="how-does-tree-shaking-work" tabindex="-1">How does tree shaking work? <a class="header-anchor" href="https://bitsofco.de/what-is-tree-shaking/">#</a></h2>
<p>Although the concept of tree shaking has been around since at least the 1990s, it has only been able to work with Javascript since the introduction of ES6-style modules. This is because tree shaking can only work if the modules are "static".</p>
<p>Before ES6 modules, we had CommonJS modules which used the <code>require()</code> syntax. These modules were "dynamic", meaning that we could import new modules based on conditions in our code.</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">var</span> myDynamicModule<span class="token punctuation">;</span><br><br><span class="token keyword">if</span> <span class="token punctuation">(</span>condition<span class="token punctuation">)</span> <span class="token punctuation">{</span><br> myDynamicModule <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">"foo"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span><br> myDynamicModule <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">"bar"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre>
<p>This dynamic nature of the CommonJS modules meant that tree shaking couldn’t be applied because it would be impossible to determine which modules will be needed before the code is actually run.</p>
<p>In ES6, a new syntax for modules was introduced, which was entirely static. Using the <code>import</code> syntax, we can no longer dynamically import a module.</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">if</span> <span class="token punctuation">(</span>condition<span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">import</span> foo <span class="token keyword">from</span> <span class="token string">"foo"</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span><br> <span class="token keyword">import</span> bar <span class="token keyword">from</span> <span class="token string">"bar"</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre>
<div class="code-addendum warning">Doesn't work</div>
<p>Instead, we have to define all imports in the global scope, outside of any conditions.</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">import</span> foo <span class="token keyword">from</span> <span class="token string">"foo"</span><span class="token punctuation">;</span><br><span class="token keyword">import</span> bar <span class="token keyword">from</span> <span class="token string">"bar"</span><span class="token punctuation">;</span><br><br><span class="token keyword">if</span> <span class="token punctuation">(</span>condition<span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token comment">// do stuff with foo</span><br><span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span><br> <span class="token comment">// do stuff with bar</span><br><span class="token punctuation">}</span></code></pre>
<p>Among other benefits, this new syntax allows for effective tree shaking, as any code that is used from an import can be determined without the code needing to be first run.</p>
<h2 id="what-does-tree-shaking-shake-off" tabindex="-1">What does tree shaking shake off? <a class="header-anchor" href="https://bitsofco.de/what-is-tree-shaking/">#</a></h2>
<p>Tree shaking, at least webpack's implementation of the feature, is pretty good at eliminating as much unused code as possible. For example, imports that are <code>import</code>-ed but not used are eliminated.</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">import</span> <span class="token punctuation">{</span> add<span class="token punctuation">,</span> multiply <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"./mathUtils"</span><span class="token punctuation">;</span><br><br><span class="token function">add</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>In the above example, the <code>multiply()</code> function is never used and will be removed from the final bundle.</p>
<p>Even specific properties from imported objects that are never accessed are removed.</p>
<div class="code-heading">myInfo.js</div>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">export</span> <span class="token keyword">const</span> myInfo <span class="token operator">=</span> <span class="token punctuation">{</span><br> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">"Ire Aderinokun"</span><span class="token punctuation">,</span><br> <span class="token literal-property property">birthday</span><span class="token operator">:</span> <span class="token string">"2 March"</span><br><span class="token punctuation">}</span></code></pre>
<div class="code-heading">index.js</div>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">import</span> <span class="token punctuation">{</span> myInfo <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"./myInfo.js"</span><span class="token punctuation">;</span><br><br>console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>myInfo<span class="token punctuation">.</span>name<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>In the above example, the <code>birthday</code> property never makes it to the final bundle because it is never actually used.</p>
<p>However, tree shaking doesn’t eliminate <em>all</em> unused code. The details of what is and isn’t eliminated is beyond the scope of this article, but it should be noted that the use of tree shaking doesn’t solve the problem of unused code entirely.</p>
<h2 id="what-about-side-effects" tabindex="-1">What about "side effects"? <a class="header-anchor" href="https://bitsofco.de/what-is-tree-shaking/">#</a></h2>
<p>A side effect is code that performs some action when imported, not necessarily related to any export. A good example of a side effect is a polyfill. Polyfills typically don’t provide an export to be used in the main script, but rather add to the project as a whole.</p>
<p>Tree shaking can’t automatically tell which scripts are side effects, so it’s important to specify them manually, as we will see below.</p>
<h2 id="how-to-tree-shake" tabindex="-1">How to tree shake <a class="header-anchor" href="https://bitsofco.de/what-is-tree-shaking/">#</a></h2>
<p>Tree shaking is typically implemented via a code bundler. If you’re using webpack, for example, you can simply set the <code>mode</code> to <code>production</code> in your <code>webpack.config.js</code> configuration file. This will, among other optimisations, enable tree shaking.</p>
<pre class="language-js" tabindex="0"><code class="language-js">module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span><br> <span class="token operator">...</span><span class="token punctuation">,</span><br> <span class="token literal-property property">mode</span><span class="token operator">:</span> <span class="token string">"production"</span><span class="token punctuation">,</span><br> <span class="token operator">...</span><span class="token punctuation">,</span><br><span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<p>To mark certain files as side effects, we need to add them to our <code>package.json</code> file.</p>
<pre class="language-json" tabindex="0"><code class="language-json"><span class="token punctuation">{</span><br> ...<span class="token punctuation">,</span><br> <span class="token property">"sideEffects"</span><span class="token operator">:</span> <span class="token punctuation">[</span><br> <span class="token string">"./src/polyfill.js"</span><br> <span class="token punctuation">]</span><span class="token punctuation">,</span><br> ...<span class="token punctuation">,</span><br><span class="token punctuation">}</span></code></pre>
<p>For more information on how to implement tree shaking using webpack, check out their <a href="https://webpack.js.org/guides/tree-shaking/">documentation</a>.</p>
Use the :lang pseudo-class over the lang attribute selector for language-specific styles2019-01-16T00:00:00Zhttps://bitsofco.de/use-the-lang-pseudo-class-over-the-lang-attribute-for-language-specific-styles/<p>The content of HTML documents can be in many different languages.
To specify the primary language of a document, we use the <code>lang</code> attribute on the root element.</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>html</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>en<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></code></pre>
<p>We can also use the <code>lang</code> attribute within the page to demarcate specific elements or sections that are in a different language to the document’s primary language.</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>html</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>en<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>head</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>head</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>body</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>fr<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Comment dites-vous "Bonjour" en Espanol?<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>body</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>html</span><span class="token punctuation">></span></span></code></pre>
<p>If we wanted to style these sections of the page differently, we could use the <code>[lang]</code> attribute selector to select all elements, or children of elements, with the specific language attribute.</p>
<pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">[lang="fr"] p</span> <span class="token punctuation">{</span><br> <span class="token property">background-color</span><span class="token punctuation">:</span> #002295<span class="token punctuation">;</span><br> <span class="token property">color</span><span class="token punctuation">:</span> #fff<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre>
<h2 id="the-lang-attribute-selector-doesn-t-know-the-element-s-language" tabindex="-1">The <code>[lang]</code> attribute selector doesn’t know the element’s language <a class="header-anchor" href="https://bitsofco.de/use-the-lang-pseudo-class-over-the-lang-attribute-for-language-specific-styles/">#</a></h2>
<p>The problem with having language-specific styles based on the <code>[lang]</code> attribute selector is that the selector isn’t actually aware of the language of the element. It’s just like any other “dumb” attribute selector.</p>
<p>This can become a problem if we have a document with multiple, nested elements of different languages. Let’s take this section below. The section has a French <code>lang</code> attribute but, within the section, we have a quote in Spanish.</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>fr<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Comment dites-vous "Bonjour" en Espanol?<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>blockquote</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>es<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Hola!<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>blockquote</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span></code></pre>
<p>In our CSS, we may have simply styled all elements with a particular <code>lang</code> attribute and its descendants.</p>
<pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">[lang="es"] p</span> <span class="token punctuation">{</span><br> <span class="token property">background-color</span><span class="token punctuation">:</span> yellow<span class="token punctuation">;</span><br> <span class="token property">color</span><span class="token punctuation">:</span> #000<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token selector">[lang="fr"] p</span> <span class="token punctuation">{</span><br> <span class="token property">background-color</span><span class="token punctuation">:</span> blue<span class="token punctuation">;</span><br> <span class="token property">color</span><span class="token punctuation">:</span> #fff<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/dnsZlT5gkp-1678.avif 1678w"><source type="image/webp" srcset="https://bitsofco.de/img/dnsZlT5gkp-1678.webp 1678w"><img alt="Both paragraphs result in blue background-color" loading="lazy" decoding="async" src="https://bitsofco.de/img/dnsZlT5gkp-1678.png" width="1678" height="1144"></picture></p>
<p>CSS has no concept of prioritising closer parents, so it doesn’t matter that the Spanish <code>lang</code> attribute is closer to the block quote paragraph element than the French one. Both of these styles are equally applicable. Because the style of the French language happened to be declared later in the CSS file, it prevails, even though, looking at the markup, we would have wanted the Spanish style to be used.</p>
<h2 id="the-lang-pseudo-class" tabindex="-1">The <code>:lang</code> pseudo-class <a class="header-anchor" href="https://bitsofco.de/use-the-lang-pseudo-class-over-the-lang-attribute-for-language-specific-styles/">#</a></h2>
<p>The <code>:lang</code> pseudo-class provides a solution to the limitations of styling the <code>lang</code> attribute in two ways:</p>
<ol>
<li>It uses the actual language of the element selected</li>
<li>It can be applied to any element and is not directly based on the <code>lang</code> attribute</li>
</ol>
<h3 id="lang-and-the-content-language" tabindex="-1"><code>:lang</code> and the "content language" <a class="header-anchor" href="https://bitsofco.de/use-the-lang-pseudo-class-over-the-lang-attribute-for-language-specific-styles/">#</a></h3>
<p>The <code>:lang</code> pseudo-class is used to select an element based on its content language. The content language of an element is determined by a combination of three things:</p>
<ul>
<li>Any <code>lang</code> attributes</li>
<li>Meta tags, e.g. <code><meta http-equiv="content-language" content="en"></code></li>
<li>HTTP headers, e.g. <code>Content-language: en</code></li>
</ul>
<p>This means that the <code>:lang</code> pseudo-class can be used even in cases where no <code>lang</code> attribute is specified.</p>
<h3 id="lang-and-nesting" tabindex="-1"><code>:lang</code> and nesting <a class="header-anchor" href="https://bitsofco.de/use-the-lang-pseudo-class-over-the-lang-attribute-for-language-specific-styles/">#</a></h3>
<p>Being a pseudo-class, <code>:lang</code> is best used on specific elements rather than on children.</p>
<p>Using the same example as above, we can switch to using the <code>:lang()</code> attribute to select the paragraph elements. But instead of selecting the child paragraph elements of the specified languages, we can select the paragraph elements that are themselves of the specified language.</p>
<pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">p:lang(es)</span> <span class="token punctuation">{</span><br> <span class="token property">background-color</span><span class="token punctuation">:</span> yellow<span class="token punctuation">;</span><br> <span class="token property">color</span><span class="token punctuation">:</span> #000<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token selector">p:lang(fr)</span> <span class="token punctuation">{</span><br> <span class="token property">background-color</span><span class="token punctuation">:</span> blue<span class="token punctuation">;</span><br> <span class="token property">color</span><span class="token punctuation">:</span> #fff<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre>
<p>Even though there is no <code>lang</code> attribute on the paragraph elements themselves, we are still able to use the pseudo-class because the content language of the paragraph is inherited from its parent.</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/biwPnJdXOV-1660.avif 1660w"><source type="image/webp" srcset="https://bitsofco.de/img/biwPnJdXOV-1660.webp 1660w"><img alt="The paragraph in Spanish is yellow" loading="lazy" decoding="async" src="https://bitsofco.de/img/biwPnJdXOV-1660.png" width="1660" height="1182"></picture></p>
<p>As we can see, the correct colours are applied to each paragraph.</p>
A Siri Shortcut to Generate PWA Icons and Web App Manifest2018-12-25T00:00:00Zhttps://bitsofco.de/a-siri-shortcut-to-generate-pwa-icons-and-web-app-manifest/<p><ins>If you've been following along with my <a href="https://bitsofco.de/learning-to-write-again">daily blogging challenge</a>, this is my final article! I've done 30 articles in 42 days!</ins></p>
<p>Since <a href="https://support.apple.com/en-us/HT209055">Siri Shortcuts</a> were announced in iOS 12, I’ve been trying to find some interesting and useful applications for it. Suprisingly, it packs a lot of power and is actually a great automation tool. In addition to being able to run actual scripts, we can do things image manipulation, which gave me the idea of creating a shortcut to automate the generation of the different icon sizes for progressive web applications.</p>
<h2 id="using-the-shortcut" tabindex="-1">Using the shortcut <a class="header-anchor" href="https://bitsofco.de/a-siri-shortcut-to-generate-pwa-icons-and-web-app-manifest/">#</a></h2>
<p>To use the shortcut, all you need to do is pass in the name of the application and the original icon file. Here's what that looks like:</p>
<video controls="" poster="https://bitsofco.de/content/images/2018/12/Screenshot-2018-12-25-at-08.11.52.png">
<source type="video/webm" src="https://res.cloudinary.com/ireaderinokun/video/upload/v1545677861/shortcut_demo_oaehev.webm">
<source type="video/mp4" src="https://res.cloudinary.com/ireaderinokun/video/upload/v1545677861/shortcut_demo_oaehev.mp4">
<img src="https://bitsofco.de/content/images/2018/12/Screenshot-2018-12-25-at-08.11.52.png" alt="Screen recording of the shortcut being used">
</video>
<p>The shortcut will generate icons in sizes 32px, 48px, 128px, 144px, 152px, 192px, 256px, and 512px. Additionally, a basic manifest.json file is generated along with a head.html file with <code><link></code>s to all the icons.</p>
<p>manifest.json</p>
<pre class="language-json" tabindex="0"><code class="language-json"><span class="token punctuation">{</span><br> <span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"bitsofcode"</span><span class="token punctuation">,</span><br> <span class="token property">"start_url"</span><span class="token operator">:</span> <span class="token string">"/"</span><span class="token punctuation">,</span><br> <span class="token property">"scope"</span><span class="token operator">:</span> <span class="token string">"/"</span><span class="token punctuation">,</span><br> <span class="token property">"display"</span><span class="token operator">:</span> <span class="token string">"standalone"</span><span class="token punctuation">,</span><br> <span class="token property">"background_color"</span><span class="token operator">:</span> <span class="token string">"#fff"</span><span class="token punctuation">,</span><br> <span class="token property">"icons"</span><span class="token operator">:</span> <span class="token punctuation">[</span><br> <span class="token punctuation">{</span><br> <span class="token property">"src"</span><span class="token operator">:</span> <span class="token string">"/icon-32.png"</span><span class="token punctuation">,</span><br> <span class="token property">"sizes"</span><span class="token operator">:</span> <span class="token string">"32x32"</span><span class="token punctuation">,</span><br> <span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"image/png"</span><br> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token punctuation">{</span><br> <span class="token property">"src"</span><span class="token operator">:</span> <span class="token string">"/icon-48.png"</span><span class="token punctuation">,</span><br> <span class="token property">"sizes"</span><span class="token operator">:</span> <span class="token string">"48x48"</span><span class="token punctuation">,</span><br> <span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"image/png"</span><br> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token punctuation">{</span><br> <span class="token property">"src"</span><span class="token operator">:</span> <span class="token string">"/icon-128.png"</span><span class="token punctuation">,</span><br> <span class="token property">"sizes"</span><span class="token operator">:</span> <span class="token string">"128x128"</span><span class="token punctuation">,</span><br> <span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"image/png"</span><br> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token punctuation">{</span><br> <span class="token property">"src"</span><span class="token operator">:</span> <span class="token string">"/icon-144.png"</span><span class="token punctuation">,</span><br> <span class="token property">"sizes"</span><span class="token operator">:</span> <span class="token string">"144x144"</span><span class="token punctuation">,</span><br> <span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"image/png"</span><br> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token punctuation">{</span><br> <span class="token property">"src"</span><span class="token operator">:</span> <span class="token string">"/icon-152.png"</span><span class="token punctuation">,</span><br> <span class="token property">"sizes"</span><span class="token operator">:</span> <span class="token string">"152x152"</span><span class="token punctuation">,</span><br> <span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"image/png"</span><br> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token punctuation">{</span><br> <span class="token property">"src"</span><span class="token operator">:</span> <span class="token string">"/icon-192.png"</span><span class="token punctuation">,</span><br> <span class="token property">"sizes"</span><span class="token operator">:</span> <span class="token string">"192x192"</span><span class="token punctuation">,</span><br> <span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"image/png"</span><br> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token punctuation">{</span><br> <span class="token property">"src"</span><span class="token operator">:</span> <span class="token string">"/icon-256.png"</span><span class="token punctuation">,</span><br> <span class="token property">"sizes"</span><span class="token operator">:</span> <span class="token string">"256x256"</span><span class="token punctuation">,</span><br> <span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"image/png"</span><br> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token punctuation">{</span><br> <span class="token property">"src"</span><span class="token operator">:</span> <span class="token string">"/icon-512.png"</span><span class="token punctuation">,</span><br> <span class="token property">"sizes"</span><span class="token operator">:</span> <span class="token string">"512x512"</span><span class="token punctuation">,</span><br> <span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"image/png"</span><br> <span class="token punctuation">}</span><br> <span class="token punctuation">]</span><br><span class="token punctuation">}</span></code></pre>
<p>head.html</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>head</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>title</span><span class="token punctuation">></span></span>bitsofcode<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>title</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>manifest<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/manifest.json<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>icon<span class="token punctuation">"</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>image/png<span class="token punctuation">"</span></span> <span class="token attr-name">sizes</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>32x32<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/icon-32.png<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>icon<span class="token punctuation">"</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>image/png<span class="token punctuation">"</span></span> <span class="token attr-name">sizes</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>48x48<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/icon-48.png<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>icon<span class="token punctuation">"</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>image/png<span class="token punctuation">"</span></span> <span class="token attr-name">sizes</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>128x128<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/icon-128.png<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>icon<span class="token punctuation">"</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>image/png<span class="token punctuation">"</span></span> <span class="token attr-name">sizes</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>144x144<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/icon-144.png<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>icon<span class="token punctuation">"</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>image/png<span class="token punctuation">"</span></span> <span class="token attr-name">sizes</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>152x152<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/icon-152.png<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>icon<span class="token punctuation">"</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>image/png<span class="token punctuation">"</span></span> <span class="token attr-name">sizes</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>192x192<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/icon-192.png<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>icon<span class="token punctuation">"</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>image/png<span class="token punctuation">"</span></span> <span class="token attr-name">sizes</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>256x256<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/icon-256.png<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>icon<span class="token punctuation">"</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>image/png<span class="token punctuation">"</span></span> <span class="token attr-name">sizes</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>512x512<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/icon-512.png<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>apple-touch-icon<span class="token punctuation">"</span></span> <span class="token attr-name">sizes</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>152x152<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/icon-152.png<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>msapplication-TileImage<span class="token punctuation">"</span></span> <span class="token attr-name">sizes</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>144x144<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/icon-144.png<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>head</span><span class="token punctuation">></span></span></code></pre>
<h2 id="breaking-it-down" tabindex="-1">Breaking it down <a class="header-anchor" href="https://bitsofco.de/a-siri-shortcut-to-generate-pwa-icons-and-web-app-manifest/">#</a></h2>
<p>The syntax of Siri Shortcuts can take a while to get used to, but it is actually quite similar to regular code. We can have variables, if/else statements, loops, and even structured data like arrays and objects.</p>
<p>You can view an <a href="https://bitsofco.de/a-siri-shortcut-to-generate-pwa-icons-and-web-app-manifest/Full.png">image of the full shortcut here</a>, but here is a breakdown of each step and what it does.</p>
<h3 id="get-application-name" tabindex="-1">Get application name <a class="header-anchor" href="https://bitsofco.de/a-siri-shortcut-to-generate-pwa-icons-and-web-app-manifest/">#</a></h3>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/gUFR081bwT-600.avif 600w"><source type="image/webp" srcset="https://bitsofco.de/img/gUFR081bwT-600.webp 600w"><img alt="Screenshot of Siri Shortcut. See following paragraph for description" loading="lazy" decoding="async" src="https://bitsofco.de/img/gUFR081bwT-600.png" width="600" height="729"></picture></p>
<p>The first thing I do is prompt the user for the name of the application and save it to the <code>Application Full Name</code> variable. I use this variable to name the folder that the resulting icons and files are stored to, as well as to fill in the name in the web app manifest.</p>
<h3 id="get-full-sized-icon" tabindex="-1">Get full-sized icon <a class="header-anchor" href="https://bitsofco.de/a-siri-shortcut-to-generate-pwa-icons-and-web-app-manifest/">#</a></h3>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/GgC0MGXLlT-600.avif 600w"><source type="image/webp" srcset="https://bitsofco.de/img/GgC0MGXLlT-600.webp 600w"><img alt="Screenshot of Siri Shortcut. See following paragraph for description" loading="lazy" decoding="async" src="https://bitsofco.de/img/GgC0MGXLlT-600.png" width="600" height="2082"></picture></p>
<p>Next, I prompt the user to select an image from the Photos library, which will be used to generate the different icon sizes needed. I save the image to the <code>Image</code> variable and the image file extension to the <code>Image Extension</code> variable.</p>
<p>I also check if the image selected is square. If not, I show an alert to the user warning that the icon will be distorted and asking if they want to continue.</p>
<p>In the future, I plan to add more checks to the image such as making sure the image format is appropriate and the size is at least 512px.</p>
<h3 id="prepping-the-directory-manifest-file-and-head-file" tabindex="-1">Prepping the directory, manifest file, and head file <a class="header-anchor" href="https://bitsofco.de/a-siri-shortcut-to-generate-pwa-icons-and-web-app-manifest/">#</a></h3>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/k-5U9YSrMp-600.avif 600w"><source type="image/webp" srcset="https://bitsofco.de/img/k-5U9YSrMp-600.webp 600w"><img alt="Screenshot of Siri Shortcut. See following paragraph for description" loading="lazy" decoding="async" src="https://bitsofco.de/img/k-5U9YSrMp-600.png" width="600" height="1374"></picture></p>
<p>Next, I create the directory that the files will be stored in and save it to the <code>Folder</code> variable. The directory is named after the application name.</p>
<p>Then, I start the files that will become the head.html and the manifest.json. As the shortcut progresses, more will be added to these files.</p>
<h3 id="generating-the-icons" tabindex="-1">Generating the icons <a class="header-anchor" href="https://bitsofco.de/a-siri-shortcut-to-generate-pwa-icons-and-web-app-manifest/">#</a></h3>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/GAfo-nc6VN-600.avif 600w"><source type="image/webp" srcset="https://bitsofco.de/img/GAfo-nc6VN-600.webp 600w"><img alt="Screenshot of Siri Shortcut. See following paragraph for description" loading="lazy" decoding="async" src="https://bitsofco.de/img/GAfo-nc6VN-600.png" width="600" height="2663"></picture></p>
<p>Next is to generate the different icon sizes. I start by creating a Dictionary with an <code>icon_sizes</code> array. This array contains all the icon sizes that we want to generate.</p>
<p>Referencing the <code>Image</code> that was selected by the user, I loop through each icon size and resize the image to whichever size we are currently on in the loop (<code>Repeat Item</code> variable).</p>
<p>In addition to resizing the image, I also add to the head.html and manifest.json files with the icon details.</p>
<h3 id="completing-the-head-html-and-manifest-json" tabindex="-1">Completing the head.html and manifest.json <a class="header-anchor" href="https://bitsofco.de/a-siri-shortcut-to-generate-pwa-icons-and-web-app-manifest/">#</a></h3>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/ziegyKzuPf-600.avif 600w"><source type="image/webp" srcset="https://bitsofco.de/img/ziegyKzuPf-600.webp 600w"><img alt="Screenshot of Siri Shortcut. See following paragraph for description" loading="lazy" decoding="async" src="https://bitsofco.de/img/ziegyKzuPf-600.png" width="600" height="1984"></picture></p>
<p>Finally, I complete the head.html and manifest.json files and save them to the same directory.</p>
<h2 id="get-the-shortcut" tabindex="-1">Get the shortcut <a class="header-anchor" href="https://bitsofco.de/a-siri-shortcut-to-generate-pwa-icons-and-web-app-manifest/">#</a></h2>
<p>If you want to use the shortcut yourself, you can <a href="https://www.icloud.com/shortcuts/f7adf6b082664f17afc2389b79f9dfd3">download the shortcut here</a>.</p>
Understanding the Virtual DOM2018-12-24T00:00:00Zhttps://bitsofco.de/understanding-the-virtual-dom/<p>I’ve recently been writing about what exactly the <a href="https://bitsofco.de/what-exactly-is-the-dom">DOM</a> and the <a href="https://bitsofco.de/what-is-the-shadow-dom">shadow DOM</a> are and how they differ. To recap, the Document Object Model is an object-based representation of an HTML document and an interface to manipulating that object. The shadow DOM can be thought of as a “lite” version of the DOM. It is also an object-based representation of HTML elements, but not of a full standalone document. Instead, the shadow DOM allows us to separate our DOM into smaller, encapsulated bits that can be used across HTML documents.</p>
<p>Another similar term you may have come across is the “virtual DOM”. Although the concept has been around for several years, it was made more popular by its use in the React framework. In this article, I will cover exactly what the virtual DOM is, how it differs from the original DOM, and how it is used.</p>
<h2 id="why-do-we-need-a-virtual-dom" tabindex="-1">Why do we need a virtual DOM? <a class="header-anchor" href="https://bitsofco.de/understanding-the-virtual-dom/">#</a></h2>
<p>To understand why the concept of the virtual DOM arose, let’s revisit the original DOM. As I mentioned, there are two parts to the DOM - the object-based representation of the HTML document and the API to manipulating that object.</p>
<p>For example, let’s take this simple HTML document with an unordered list and one list item.</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token doctype"><span class="token punctuation"><!</span><span class="token doctype-tag">doctype</span> <span class="token name">html</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>html</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>en<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>head</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>head</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>body</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ul</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>list<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>list__item<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>List item<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ul</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>body</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>html</span><span class="token punctuation">></span></span></code></pre>
<p>This document can be represented as the following DOM tree:</p>
<ul>
<li>
<p>html</p>
<ul>
<li>
<p>head lang="en"</p>
</li>
<li>
<p>body</p>
<ul>
<li>
<p>ul class="list"</p>
<ul>
<li>
<p>li class="list__item"</p>
<ul>
<li>"List item"</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>Let’s say we want to modify the content of the first list item to <code>"List item one"</code> and also add a second list item. To do this, we will need use the DOM APIs to find the elements we want to update, create the new elements, add attributes and content, then finally update the DOM elements themselves.</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> listItemOne <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">getElementsByClassName</span><span class="token punctuation">(</span><span class="token string">"list__item"</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">;</span><br>listItemOne<span class="token punctuation">.</span>textContent <span class="token operator">=</span> <span class="token string">"List item one"</span><span class="token punctuation">;</span><br><br><span class="token keyword">const</span> list <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">getElementsByClassName</span><span class="token punctuation">(</span><span class="token string">"list"</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">;</span><br><span class="token keyword">const</span> listItemTwo <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">createElement</span><span class="token punctuation">(</span><span class="token string">"li"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br>listItemTwo<span class="token punctuation">.</span>classList<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">"list__item"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br>listItemTwo<span class="token punctuation">.</span>textContent <span class="token operator">=</span> <span class="token string">"List item two"</span><span class="token punctuation">;</span><br>list<span class="token punctuation">.</span><span class="token function">appendChild</span><span class="token punctuation">(</span>listItemTwo<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<h3 id="the-dom-wasn-t-made-for-this" tabindex="-1">The DOM wasn’t made for this… <a class="header-anchor" href="https://bitsofco.de/understanding-the-virtual-dom/">#</a></h3>
<p>When the <a href="https://www.w3.org/TR/REC-DOM-Level-1/">first specification for the DOM</a> was released in 1998, we built and managed web pages in very differently. There was far less reliance on the DOM APIs to create and update the page content as frequently as we do today.</p>
<p>Simple methods such as <code>document.getElementsByClassName()</code> are fine to use on a small scale, but if we are updating multiple elements on a page every few seconds, it can start to become really expensive to constantly query and update the DOM.</p>
<p>Even further, because of the way the APIs are setup, it is usually simpler to perform more expensive operations where we update larger parts of the document than to find and update the specific elements. Going back to our list example, it is in some ways easier to replace the entire unordered list with a new one than to modify the specific elements.</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> list <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">getElementsByClassName</span><span class="token punctuation">(</span><span class="token string">"list"</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">;</span><br>list<span class="token punctuation">.</span>innerHTML <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string"><br><li class="list__item">List item one</li><br><li class="list__item">List item two</li><br></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span></code></pre>
<p>In this particular example, the performance difference between the methods is probably insignificant. However, as the size of the web page grows, it becomes more important to only select and update what is needed.</p>
<h3 id="but-the-virtual-dom-was" tabindex="-1">… but the virtual DOM was! <a class="header-anchor" href="https://bitsofco.de/understanding-the-virtual-dom/">#</a></h3>
<p>The virtual DOM was created to solve these problems of needing to frequently update the DOM in a more performant way. Unlike the DOM or the shadow DOM, the virtual DOM isn't an official specification, but rather a new method of interfacing with the DOM.</p>
<p>A virtual DOM can be thought of as a copy of the original DOM. This copy can be frequently manipulated and updated, without using the DOM APIs. Once all the updates have been made to the virtual DOM, we can look at what specific changes need to be made to the original DOM and make them in a targetted and optimised way.</p>
<h2 id="what-does-a-virtual-dom-look-like" tabindex="-1">What does a virtual DOM look like? <a class="header-anchor" href="https://bitsofco.de/understanding-the-virtual-dom/">#</a></h2>
<p>The name "virtual DOM" tends to add to the mystery of what the concept actually is. In fact, a virtual DOM is just a regular Javascript object.</p>
<p>Let’s revisit the DOM tree we created earlier:</p>
<ul>
<li>
<p>html</p>
<ul>
<li>
<p>head lang="en"</p>
</li>
<li>
<p>body</p>
<ul>
<li>
<p>ul class="list"</p>
<ul>
<li>
<p>li class="list__item"</p>
<ul>
<li>"List item"</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>This tree can also be represented as a Javascript object.</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> vdom <span class="token operator">=</span> <span class="token punctuation">{</span><br> <span class="token literal-property property">tagName</span><span class="token operator">:</span> <span class="token string">"html"</span><span class="token punctuation">,</span><br> <span class="token literal-property property">children</span><span class="token operator">:</span> <span class="token punctuation">[</span><br> <span class="token punctuation">{</span> <span class="token literal-property property">tagName</span><span class="token operator">:</span> <span class="token string">"head"</span> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token punctuation">{</span><br> <span class="token literal-property property">tagName</span><span class="token operator">:</span> <span class="token string">"body"</span><span class="token punctuation">,</span><br> <span class="token literal-property property">children</span><span class="token operator">:</span> <span class="token punctuation">[</span><br> <span class="token punctuation">{</span><br> <span class="token literal-property property">tagName</span><span class="token operator">:</span> <span class="token string">"ul"</span><span class="token punctuation">,</span><br> <span class="token literal-property property">attributes</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token string-property property">"class"</span><span class="token operator">:</span> <span class="token string">"list"</span> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token literal-property property">children</span><span class="token operator">:</span> <span class="token punctuation">[</span><br> <span class="token punctuation">{</span><br> <span class="token literal-property property">tagName</span><span class="token operator">:</span> <span class="token string">"li"</span><span class="token punctuation">,</span><br> <span class="token literal-property property">attributes</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token string-property property">"class"</span><span class="token operator">:</span> <span class="token string">"list__item"</span> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token literal-property property">textContent</span><span class="token operator">:</span> <span class="token string">"List item"</span><br> <span class="token punctuation">}</span> <span class="token comment">// end li</span><br> <span class="token punctuation">]</span><br> <span class="token punctuation">}</span> <span class="token comment">// end ul</span><br> <span class="token punctuation">]</span><br> <span class="token punctuation">}</span> <span class="token comment">// end body</span><br> <span class="token punctuation">]</span><br><span class="token punctuation">}</span> <span class="token comment">// end html</span></code></pre>
<p>We can think of this object as our virtual DOM. Like the original DOM, it is an object-based representation of our HTML document. But since it is a plain Javascript object, we can manipulate it freely and frequently without touching the actual DOM until we need to.</p>
<p>Instead of using one object for the entire object, it is more common to work with small sections of the virtual DOM. For example, we may work on a <code>list</code> component, which would corespond to our unordered list element.</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> list <span class="token operator">=</span> <span class="token punctuation">{</span><br> <span class="token literal-property property">tagName</span><span class="token operator">:</span> <span class="token string">"ul"</span><span class="token punctuation">,</span><br> <span class="token literal-property property">attributes</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token string-property property">"class"</span><span class="token operator">:</span> <span class="token string">"list"</span> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token literal-property property">children</span><span class="token operator">:</span> <span class="token punctuation">[</span><br> <span class="token punctuation">{</span><br> <span class="token literal-property property">tagName</span><span class="token operator">:</span> <span class="token string">"li"</span><span class="token punctuation">,</span><br> <span class="token literal-property property">attributes</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token string-property property">"class"</span><span class="token operator">:</span> <span class="token string">"list__item"</span> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token literal-property property">textContent</span><span class="token operator">:</span> <span class="token string">"List item"</span><br> <span class="token punctuation">}</span><br> <span class="token punctuation">]</span><br><span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<h2 id="under-the-hood-of-the-virtual-dom" tabindex="-1">Under the hood of the virtual DOM <a class="header-anchor" href="https://bitsofco.de/understanding-the-virtual-dom/">#</a></h2>
<p>Now that we’ve seen what a virtual DOM looks like, how does it work to solve the performance and usability problems of the DOM?</p>
<p>As I mentioned, we can use the virtual DOM to single out the specific changes that need to be made to the DOM and make those specific updates alone. Let's go back to our unordered list example and make the same changes we did using the DOM API.</p>
<p>The first thing we would do is make a copy of the virtual DOM, containing the changes we want to make. Since we don't need to use the DOM APIs, we can actually just create a new object alltogether.</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> copy <span class="token operator">=</span> <span class="token punctuation">{</span><br> <span class="token literal-property property">tagName</span><span class="token operator">:</span> <span class="token string">"ul"</span><span class="token punctuation">,</span><br> <span class="token literal-property property">attributes</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token string-property property">"class"</span><span class="token operator">:</span> <span class="token string">"list"</span> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token literal-property property">children</span><span class="token operator">:</span> <span class="token punctuation">[</span><br> <span class="token punctuation">{</span><br> <span class="token literal-property property">tagName</span><span class="token operator">:</span> <span class="token string">"li"</span><span class="token punctuation">,</span><br> <span class="token literal-property property">attributes</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token string-property property">"class"</span><span class="token operator">:</span> <span class="token string">"list__item"</span> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token literal-property property">textContent</span><span class="token operator">:</span> <span class="token string">"List item one"</span><br> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token punctuation">{</span><br> <span class="token literal-property property">tagName</span><span class="token operator">:</span> <span class="token string">"li"</span><span class="token punctuation">,</span><br> <span class="token literal-property property">attributes</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token string-property property">"class"</span><span class="token operator">:</span> <span class="token string">"list__item"</span> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token literal-property property">textContent</span><span class="token operator">:</span> <span class="token string">"List item two"</span><br> <span class="token punctuation">}</span><br> <span class="token punctuation">]</span><br><span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<p>This <code>copy</code> is used to create what is called a “diff” between the original virtual DOM, in this case the <code>list</code>, and the updated one. A diff could look something like this:</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> diffs <span class="token operator">=</span> <span class="token punctuation">[</span><br> <span class="token punctuation">{</span><br> <span class="token literal-property property">newNode</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token comment">/* new version of list item one */</span> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token literal-property property">oldNode</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token comment">/* original version of list item one */</span> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token literal-property property">index</span><span class="token operator">:</span> <span class="token comment">/* index of element in parent's list of child nodes */</span><br> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token punctuation">{</span><br> <span class="token literal-property property">newNode</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token comment">/* list item two */</span> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token literal-property property">index</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token comment">/* */</span> <span class="token punctuation">}</span><br> <span class="token punctuation">}</span><br><span class="token punctuation">]</span></code></pre>
<p>This diff provides instructions for how to update the actual DOM. Once all the diffs are collected, we can batch changes to the DOM, making only the updates that are needed.</p>
<p>For example we could loop through each diff and either add a new child or update an old one depending on what the diff specifies.</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> domElement <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">getElementsByClassName</span><span class="token punctuation">(</span><span class="token string">"list"</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">;</span><br><br>diffs<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">diff</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br><br> <span class="token keyword">const</span> newElement <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">createElement</span><span class="token punctuation">(</span>diff<span class="token punctuation">.</span>newNode<span class="token punctuation">.</span>tagName<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token comment">/* Add attributes ... */</span><br><br> <span class="token keyword">if</span> <span class="token punctuation">(</span>diff<span class="token punctuation">.</span>oldNode<span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token comment">// If there is an old version, replace it with the new version</span><br> domElement<span class="token punctuation">.</span><span class="token function">replaceChild</span><span class="token punctuation">(</span>diff<span class="token punctuation">.</span>newNode<span class="token punctuation">,</span> diff<span class="token punctuation">.</span>index<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span><br> <span class="token comment">// If no old version exists, create a new node</span><br> domElement<span class="token punctuation">.</span><span class="token function">appendChild</span><span class="token punctuation">(</span>diff<span class="token punctuation">.</span>newNode<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre>
<p>Note that this is a really simplified and stripped-back version of how a virtual DOM could work and there are lot of cases I didn't cover here.</p>
<h2 id="the-virtual-dom-and-frameworks" tabindex="-1">The virtual DOM and frameworks <a class="header-anchor" href="https://bitsofco.de/understanding-the-virtual-dom/">#</a></h2>
<p>It's more common to work with the virtual DOM via a framework, rather than interfacing with it directly as I showed in the example above.</p>
<p>Frameworks such as React and Vue use the virtual DOM concept to make more performant updates to the DOM. For example, our <code>list</code> component can be written in React in the following way.</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">import</span> React <span class="token keyword">from</span> <span class="token string">'react'</span><span class="token punctuation">;</span><br><span class="token keyword">import</span> ReactDOM <span class="token keyword">from</span> <span class="token string">'react-dom'</span><span class="token punctuation">;</span><br><br><span class="token keyword">const</span> list <span class="token operator">=</span> React<span class="token punctuation">.</span><span class="token function">createElement</span><span class="token punctuation">(</span><span class="token string">"ul"</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> <span class="token literal-property property">className</span><span class="token operator">:</span> <span class="token string">"list"</span> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> React<span class="token punctuation">.</span><span class="token function">createElement</span><span class="token punctuation">(</span><span class="token string">"li"</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> <span class="token literal-property property">className</span><span class="token operator">:</span> <span class="token string">"list__item"</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token string">"List item"</span><span class="token punctuation">)</span><br><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br>ReactDOM<span class="token punctuation">.</span><span class="token function">render</span><span class="token punctuation">(</span>list<span class="token punctuation">,</span> document<span class="token punctuation">.</span>body<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>If we wanted to update our list, we could just rewrite the entire list template, and call <code>ReactDOM.render()</code> again, passing in the new list.</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> newList <span class="token operator">=</span> React<span class="token punctuation">.</span><span class="token function">createElement</span><span class="token punctuation">(</span><span class="token string">"ul"</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> <span class="token literal-property property">className</span><span class="token operator">:</span> <span class="token string">"list"</span> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> React<span class="token punctuation">.</span><span class="token function">createElement</span><span class="token punctuation">(</span><span class="token string">"li"</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> <span class="token literal-property property">className</span><span class="token operator">:</span> <span class="token string">"list__item"</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token string">"List item one"</span><span class="token punctuation">)</span><span class="token punctuation">,</span><br> React<span class="token punctuation">.</span><span class="token function">createElement</span><span class="token punctuation">(</span><span class="token string">"li"</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> <span class="token literal-property property">className</span><span class="token operator">:</span> <span class="token string">"list__item"</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token string">"List item two"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br><span class="token function">setTimeout</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> ReactDOM<span class="token punctuation">.</span><span class="token function">render</span><span class="token punctuation">(</span>newList<span class="token punctuation">,</span> document<span class="token punctuation">.</span>body<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token number">5000</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Because React uses the virtual DOM, even though we are re-rendering the entire template, only the parts that actually change are updated. If we look at our developer tools when the change happens, we will see the specific elements and the specific parts of the elements that change.</p>
<video autoplay="" loop="" muted="" playsinline="" poster="https://res.cloudinary.com/ireaderinokun/video/upload/v1545416044/bitsofcode/react-virtual-dom.jpg">
<source type="video/webm" src="https://res.cloudinary.com/ireaderinokun/video/upload/v1545416044/bitsofcode/react-virtual-dom.webm">
<img src="https://res.cloudinary.com/ireaderinokun/video/upload/v1545416044/bitsofcode/react-virtual-dom.jpg" alt="">
</video>
<h2 id="the-dom-vs-the-virtual-dom" tabindex="-1">The DOM vs the virtual DOM <a class="header-anchor" href="https://bitsofco.de/understanding-the-virtual-dom/">#</a></h2>
<p>To recap, the virtual DOM is a tool that enables us to interface with DOM elements in an easier and more performant way. It is a Javascript object representation of the DOM, which we can modify as frequently as we need to. Changes made to this object are then collated, and modifications to the actual DOM are targetted and made less often.</p>
My CSS Reset/Base2018-12-21T00:00:00Zhttps://bitsofco.de/my-css-reset-base/<p>Since writing about the <a href="https://bitsofco.de/a-look-at-css-resets-in-2018">state of CSS resets in 2018</a>, I’ve had a few people ask what my modified CSS reset looks like. As I mentioned in that article, my reset is more like a base at this point because, in addition to resetting some default browser styles, I also include some utilities that I want in every project.</p>
<p>You can view the entire CSS file in my <a href="https://github.com/ireade/css/blob/master/base.scss">Github repository</a>, which will be kept more up to date, but here is an explanation of everything I’ve included at the time of writing.</p>
<p>A warning, <strong>this is very opinionated</strong>. I’m writing about this to share my personal preferences and not arguing that anyone else should work the way I do. That said, I’m interested to hear what you think and how you reset/base your projects, so do leave a comment below.</p>
<h2 id="resetting-margins-paddings-and-borders" tabindex="-1">Resetting margins, paddings, and borders <a class="header-anchor" href="https://bitsofco.de/my-css-reset-base/">#</a></h2>
<pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">html, body,<br>h1, h2, h3, h4, h5, h6,<br>a, p, span,<br>em, small, strong,<br>sub, sup,<br>mark, del, ins, strike,<br>abbr, dfn,<br>blockquote, q, cite,<br>code, pre,<br>ol, ul, li, dl, dt, dd,<br>div, section, article,<br>main, aside, nav,<br>header, hgroup, footer,<br>img, figure, figcaption,<br>address, time,<br>audio, video,<br>canvas, iframe,<br>details, summary,<br>fieldset, form, label, legend,<br>table, caption,<br>tbody, tfoot, thead,<br>tr, th, td</span> <span class="token punctuation">{</span><br> <span class="token property">margin</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span><br> <span class="token property">padding</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span><br> <span class="token property">border</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre>
<p>The first thing I always do is remove the <code>margin</code>, <code>padding</code>, and <code>border</code> on all elements besides the form fields. This is largely taken from <a href="https://meyerweb.com/eric/tools/css/reset/index.html">Meyer’s CSS Reset</a> with two modifications.</p>
<p>The first modification is that I only included the selectors of elements I'm likely to still use. For example, I removed deprecated elements such as <code>menu</code>, but I also didn't include valid elements I never need to use, such as the <code>ruby</code> element.</p>
<p>The second modification is that I moved the resetting of the font styles to another selector, as you'll see below.</p>
<h2 id="typography" tabindex="-1">Typography <a class="header-anchor" href="https://bitsofco.de/my-css-reset-base/">#</a></h2>
<pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">html</span> <span class="token punctuation">{</span><br> <span class="token property">font-size</span><span class="token punctuation">:</span> 62.5%<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token selector">body</span> <span class="token punctuation">{</span><br> <span class="token property">font-size</span><span class="token punctuation">:</span> 1.6rem<span class="token punctuation">;</span><br> <span class="token property">line-height</span><span class="token punctuation">:</span> 1.4<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token selector">*</span> <span class="token punctuation">{</span><br> <span class="token property">font-family</span><span class="token punctuation">:</span> inherit<span class="token punctuation">;</span><br> <span class="token property">font-size</span><span class="token punctuation">:</span> inherit<span class="token punctuation">;</span><br> <span class="token property">line-height</span><span class="token punctuation">:</span> inherit<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token selector">a,<br>a:visited</span> <span class="token punctuation">{</span><br> <span class="token property">color</span><span class="token punctuation">:</span> inherit<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre>
<p>I’m still partial to the method of <a href="https://snook.ca/archives/html_and_css/font-size-with-rem">setting 62.5% font-size</a> on the <code>html</code> element then using the rem unit everywhere else. I don’t have a strong reason for doing it this way, it’s more inertia at this point for me.</p>
<p>I also reset the <code>font-family</code>, <code>font-size</code>, and <code>line-height</code> on every element to inherit from its parent. This is pretty opinionated and is actually a new addition to my reset file. I do it this way because, generally speaking, I want to be proactive about setting any font styles that are different from the paragraph elements. Also, it’s really handy for the form elements like <code>input</code> or <code>button</code> to already be styled like the default body text. Using the default Meyer Reset, I always had to style the font of these elements separately.</p>
<p>Finally, I generally prefer the link colour to be the same as the body text so I set that to inherit as well.</p>
<h2 id="layout-and-box-sizing" tabindex="-1">Layout & box sizing <a class="header-anchor" href="https://bitsofco.de/my-css-reset-base/">#</a></h2>
<pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">article,<br>aside,<br>footer,<br>header,<br>nav,<br>section,<br>main</span> <span class="token punctuation">{</span><br> <span class="token property">display</span><span class="token punctuation">:</span> block<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token selector">*</span> <span class="token punctuation">{</span><br> <span class="token property">box-sizing</span><span class="token punctuation">:</span> border-box<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token selector">*:before,<br>*:after</span> <span class="token punctuation">{</span><br> <span class="token property">box-sizing</span><span class="token punctuation">:</span> inherit<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre>
<p>Here, I set the correct <code>display</code> on the HTML5 elements and set the box-sizing on all elements to <code>border-box</code>.</p>
<h2 id="resetting-specific-element-styles" tabindex="-1">Resetting specific element styles <a class="header-anchor" href="https://bitsofco.de/my-css-reset-base/">#</a></h2>
<pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">table</span> <span class="token punctuation">{</span><br> <span class="token property">border-collapse</span><span class="token punctuation">:</span> collapse<span class="token punctuation">;</span><br> <span class="token property">border-spacing</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token selector">ol,<br>ul</span> <span class="token punctuation">{</span><br> <span class="token property">list-style</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token selector">img,<br>video</span> <span class="token punctuation">{</span><br> <span class="token property">max-width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token selector">img</span> <span class="token punctuation">{</span><br> <span class="token property">border-style</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token selector">blockquote,<br>q</span> <span class="token punctuation">{</span><br> <span class="token property">quotes</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token selector">blockquote:after,<br>blockquote:before,<br>q:after,<br>q:before</span> <span class="token punctuation">{</span><br> <span class="token property">content</span><span class="token punctuation">:</span> <span class="token string">""</span><span class="token punctuation">;</span><br> <span class="token property">content</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre>
<p>In this section, I target specific elements and undo some of the styling that the browsers add by default. Most of this is pretty standard for CSS resets.</p>
<h2 id="attributes-and-states" tabindex="-1">Attributes & states <a class="header-anchor" href="https://bitsofco.de/my-css-reset-base/">#</a></h2>
<pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">[hidden]</span> <span class="token punctuation">{</span><br> <span class="token property">display</span><span class="token punctuation">:</span> none <span class="token important">!important</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token selector">[disabled]</span> <span class="token punctuation">{</span><br> <span class="token property">cursor</span><span class="token punctuation">:</span> not-allowed<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token selector">:focus:not(:focus-visible)</span> <span class="token punctuation">{</span><br> <span class="token property">outline</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre>
<p>Next, I handle the styling for certain HTML attributes or states.</p>
<p>Any element with the <code>hidden</code> attribute should not be visible.</p>
<p>Setting the <code>cursor</code> to <code>not-allowed</code> for disabled elements is a style I personally like to add to all projects.</p>
<p>The <code>:focus:not(:focus-visible)</code> selector allows me to remove the <code>outline</code> in cases where the browser has deemed that the focus does not need to be visible. For browsers that don’t yet support the <code>:focus-visible</code> pseudo-class, the entire rule is ignored and the outline remains as normal.</p>
<h2 id="screen-reader-only-utility" tabindex="-1">Screen reader only utility <a class="header-anchor" href="https://bitsofco.de/my-css-reset-base/">#</a></h2>
<pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.sr-only</span> <span class="token punctuation">{</span><br> <span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span><br> <span class="token property">clip</span><span class="token punctuation">:</span> <span class="token function">rect</span><span class="token punctuation">(</span>1px<span class="token punctuation">,</span> 1px<span class="token punctuation">,</span> 1px<span class="token punctuation">,</span> 1px<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token property">left</span><span class="token punctuation">:</span> -9999px<span class="token punctuation">;</span><br> <span class="token property">top</span><span class="token punctuation">:</span> -9999px<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre>
<p>Finally, I always add this <code>sr-only</code> utility class. I add this to HTML elements that should remain in the document, but should be visually hidden.</p>
<h2 id="what-s-in-your-css-reset-base" tabindex="-1">What’s in your CSS reset/base? <a class="header-anchor" href="https://bitsofco.de/my-css-reset-base/">#</a></h2>
<p>I wouls really love to see what you include in your CSS reset or base, if you have a custom one. Feel free to write your own article and comment the link below!</p>
Making the abbr element work for touchscreen, keyboard, and mouse2018-12-20T00:00:00Zhttps://bitsofco.de/making-abbr-work-for-touchscreen-keyboard-mouse/<p>The <code><abbr></code> element is used to denote and define an acronym. For example, if we want to write the acronym “CSS” anywhere on a web page, we can also provide the full name “Cascading Style Sheets” for readers unfamiliar with the acronym.</p>
<p>This is done by wrapping the acronym in an <code><abbr></code> element and adding the full definition in a <code>title</code> attribute.</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>abbr</span> <span class="token attr-name">title</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Cascading Style Sheets<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>CSS<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>abbr</span><span class="token punctuation">></span></span></code></pre>
<p>If a user hovers their mouse over the acronym, the full definition is displayed in a native browser tooltip.</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/2ELylI8lCM-2315.avif 2315w"><source type="image/webp" srcset="https://bitsofco.de/img/2ELylI8lCM-2315.webp 2315w"><img alt="Screenshot of a browser with the word "CSS" and a tooltip below showing "Cascading Style Sheets"" loading="lazy" decoding="async" src="https://bitsofco.de/img/2ELylI8lCM-2315.png" width="2315" height="1351"></picture></p>
<h2 id="the-problem-s-with-the-abbr-element" tabindex="-1">The problem(s) with the <code>abbr</code> element <a class="header-anchor" href="https://bitsofco.de/making-abbr-work-for-touchscreen-keyboard-mouse/">#</a></h2>
<p>The <code><abbr></code> element works well enough on desktop devices as long as the user is interacting with a pointing device. However, it fails for users who are interacting with the page via the keyboard or a touchscreen device. This is because of two things:</p>
<ol>
<li>The <code><abbr></code> element isn’t able to be brought into focus, e.g. by a keyboard user pressing the tab key</li>
<li>Mobile browsers don’t display the tooltip on any interaction from the user</li>
</ol>
<p>Additionally, I’ve always thought the delay before the native browser tooltip shows up was too long. It takes about 2 seconds, which is really slow compared to most other interactions on the web.</p>
<h2 id="fixing-the-abbr-element" tabindex="-1">Fixing the <code>abbr</code> element <a class="header-anchor" href="https://bitsofco.de/making-abbr-work-for-touchscreen-keyboard-mouse/">#</a></h2>
<p>These issues can be fixed by modifying the HTML and adding some custom styles to re-implement the native browser tooltip in a more effective way.</p>
<h3 id="enabling-focus" tabindex="-1">Enabling focus <a class="header-anchor" href="https://bitsofco.de/making-abbr-work-for-touchscreen-keyboard-mouse/">#</a></h3>
<p>The first thing we need to do is allow the <code><abbr></code> element to be brought into focus to enable keyboard users to interact with it. We can do this by adding the <code>tabindex</code> attribute to the element.</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>abbr</span> <span class="token attr-name">title</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Cascading Style Sheets<span class="token punctuation">"</span></span> <span class="token attr-name">tabindex</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>CSS<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>abbr</span><span class="token punctuation">></span></span></code></pre>
<p>A <code>tabindex</code> value of <code>0</code> will place the element in the default focus order as determined by the element’s position in the HTML document.</p>
<h3 id="displaying-the-title-element" tabindex="-1">Displaying the <code>title</code> element <a class="header-anchor" href="https://bitsofco.de/making-abbr-work-for-touchscreen-keyboard-mouse/">#</a></h3>
<p>Next, we need a way to display the contents of the <code>title</code> element to the user. This can be done by using the <code>attr()</code> function, which allows us to retrieve and use the contents of any element’s attribute as a CSS value.</p>
<p>For example, we can take the <code>title</code> of the <code><abbr></code> and display it immediately after the element in an <code>::after</code> pseudo-element.</p>
<pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">abbr[title]::after</span> <span class="token punctuation">{</span><br> <span class="token property">content</span><span class="token punctuation">:</span> <span class="token function">attr</span><span class="token punctuation">(</span>title<span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre>
<h3 id="showing-the-custom-tooltip-on-mouse-hover-keyboard-focus-or-finger-tap" tabindex="-1">Showing the custom tooltip on mouse hover, keyboard focus, or finger tap <a class="header-anchor" href="https://bitsofco.de/making-abbr-work-for-touchscreen-keyboard-mouse/">#</a></h3>
<p>Although we can leave it at displaying the <code>title</code> of the <code><abbr></code> element immediately after it, our goal here is to recreate a tooltip. So, instead of just plainly displaying the <code>title</code> by default, we can wait for the user interaction we want first.</p>
<p>In order to have the custom tooltip display in the three scenarios we want - mouse hover, keyboard focus, or finger tap - we need to combine the display of the pseudo-element with both the <code>:hover</code> and <code>:focus</code> pseudo-classes.</p>
<pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">abbr[title]:hover::after,<br>abbr[title]:focus::after</span> <span class="token punctuation">{</span><br> <span class="token property">content</span><span class="token punctuation">:</span> <span class="token function">attr</span><span class="token punctuation">(</span>title<span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre>
<p>The <code>:hover</code> pseudo-class takes care of when the user hovers over the device with a mouse, but also when the user taps on the element on a touchscreen device. Additionally, unlike the <code>:focus</code> or <code>:active</code> pseudo-classes, the <code>:hover</code> pseudo-class works on all mobile browsers, including mobile Safari.</p>
<p>The <code>:focus</code> pseudo-class takes care of when the user brings the element into focus via their keyboard. With the <code>tabindex</code> attribute we added, this is now possible for users to do.</p>
<h3 id="styling-the-tooltip" tabindex="-1">Styling the tooltip <a class="header-anchor" href="https://bitsofco.de/making-abbr-work-for-touchscreen-keyboard-mouse/">#</a></h3>
<p>Finally, we can add more styles to make the tooltip appear as a tooltip, and not just as text next to the element.</p>
<pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">abbr[title]</span> <span class="token punctuation">{</span><br> <span class="token property">position</span><span class="token punctuation">:</span> relative<span class="token punctuation">;</span><br><br> <span class="token comment">/* ensure consistent styling across browsers */</span><br> <span class="token property">text-decoration</span><span class="token punctuation">:</span> underline dotted<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token selector">abbr[title]:hover::after,<br>abbr[title]:focus::after</span> <span class="token punctuation">{</span><br> <span class="token property">content</span><span class="token punctuation">:</span> <span class="token function">attr</span><span class="token punctuation">(</span>title<span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token comment">/* position tooltip like the native one */</span><br> <span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span><br> <span class="token property">left</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span><br> <span class="token property">bottom</span><span class="token punctuation">:</span> -30px<span class="token punctuation">;</span><br> <span class="token property">width</span><span class="token punctuation">:</span> auto<span class="token punctuation">;</span><br> <span class="token property">white-space</span><span class="token punctuation">:</span> nowrap<span class="token punctuation">;</span><br><br> <span class="token comment">/* style tooltip */</span><br> <span class="token property">background-color</span><span class="token punctuation">:</span> #1e1e1e<span class="token punctuation">;</span><br> <span class="token property">color</span><span class="token punctuation">:</span> #fff<span class="token punctuation">;</span><br> <span class="token property">border-radius</span><span class="token punctuation">:</span> 3px<span class="token punctuation">;</span><br> <span class="token property">box-shadow</span><span class="token punctuation">:</span> 1px 1px 5px 0 <span class="token function">rgba</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span>0<span class="token punctuation">,</span>0<span class="token punctuation">,</span>0.4<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token property">font-size</span><span class="token punctuation">:</span> 14px<span class="token punctuation">;</span><br> <span class="token property">padding</span><span class="token punctuation">:</span> 3px 5px<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre>
<p>And that’s it! Here's what it would look like when active:</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/m1omVq2Voh-1482.avif 1482w"><source type="image/webp" srcset="https://bitsofco.de/img/m1omVq2Voh-1482.webp 1482w"><img alt="Screenshot of a browser with the word "CSS" and a tooltip below showing "Cascading Style Sheets"" loading="lazy" decoding="async" src="https://bitsofco.de/img/m1omVq2Voh-1482.png" width="1482" height="906"></picture></p>
<p>You can also checkout the <a href="https://codepen.io/ire/pen/bOwdRM?editors=1100">codepen</a> I created to demo this.</p>
Handling broken images with the service worker2018-12-19T00:00:00Zhttps://bitsofco.de/handling-broken-images-with-service-worker/<p>A few years ago, I wrote about how we can use css to <a href="https://bitsofco.de/styling-broken-images">style broken images</a>. The technique leveraged on the fact that any styling to the <code>::before</code> or <code>::after</code> pseudo-elements on the <code><img></code> element will only be applied if the image doesn’t load. So, we could style those elements and they would only display if the image was broken.</p>
<p>Here’s an example of how I’ve styled broken images on this site:</p>
<p><img src="https://bitsofco.de/blah.png" alt="Broken image"></p>
<p><img src="https://bitsofco.de/bitsofcode/content/images/2018/12/Screenshot-2018-12-16-at-21.08.32.png" alt="Broken image"></p>
<p>There are pros and cons to handling broken images this way. One limitation is browser support, as this technique doesn’t work in some major browsers like Safari.</p>
<p>Having recently done a lot of work with service workers, it occurred to me that we could use the service worker to handle broken images in a different way. Since the service worker can tell if an image file isn’t able to be fetched, we can handle that condition by, for example, serving a different image to the browser.</p>
<h2 id="intercepting-requests-for-broken-images" tabindex="-1">Intercepting requests for broken images <a class="header-anchor" href="https://bitsofco.de/handling-broken-images-with-service-worker/">#</a></h2>
<p>In the service worker <code>fetch</code> event, we can tell if and when a request the browser makes goes wrong, whether that be because the user is offline or because the response to the fetch request was bad.</p>
<pre class="language-js" tabindex="0"><code class="language-js">self<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'fetch'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">e</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> e<span class="token punctuation">.</span><span class="token function">respondWith</span><span class="token punctuation">(</span><br> <span class="token function">fetch</span><span class="token punctuation">(</span>e<span class="token punctuation">.</span>request<span class="token punctuation">)</span><br> <span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">response</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span>response<span class="token punctuation">.</span>ok<span class="token punctuation">)</span> <span class="token keyword">return</span> response<span class="token punctuation">;</span><br><br> <span class="token comment">// User is online, but response was not ok</span><br><br> <span class="token punctuation">}</span><span class="token punctuation">)</span><br> <span class="token punctuation">.</span><span class="token function">catch</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">err</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br><br> <span class="token comment">// User is probably offline</span><br><br> <span class="token punctuation">}</span><span class="token punctuation">)</span><br> <span class="token punctuation">)</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>In either of these scenarios, we can check to see if the failed request was for an image and do whatever we like in response.</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">function</span> <span class="token function">isImage</span><span class="token punctuation">(</span><span class="token parameter">fetchRequest</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">return</span> fetchRequest<span class="token punctuation">.</span>method <span class="token operator">===</span> <span class="token string">"GET"</span><br> <span class="token operator">&&</span> fetchRequest<span class="token punctuation">.</span>destination <span class="token operator">===</span> <span class="token string">"image"</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br>self<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'fetch'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">e</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> e<span class="token punctuation">.</span><span class="token function">respondWith</span><span class="token punctuation">(</span><br> <span class="token function">fetch</span><span class="token punctuation">(</span>e<span class="token punctuation">.</span>request<span class="token punctuation">)</span><br> <span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">response</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span>response<span class="token punctuation">.</span>ok<span class="token punctuation">)</span> <span class="token keyword">return</span> response<span class="token punctuation">;</span><br><br> <span class="token comment">// User is online, but response was not ok</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">isImage</span><span class="token punctuation">(</span>e<span class="token punctuation">.</span>request<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token comment">// do something</span><br> <span class="token punctuation">}</span><br><br> <span class="token punctuation">}</span><span class="token punctuation">)</span><br> <span class="token punctuation">.</span><span class="token function">catch</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">err</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br><br> <span class="token comment">// User is probably offline</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">isImage</span><span class="token punctuation">(</span>e<span class="token punctuation">.</span>request<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token comment">// do something</span><br> <span class="token punctuation">}</span><br><br> <span class="token punctuation">}</span><span class="token punctuation">)</span><br> <span class="token punctuation">)</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<h2 id="serving-a-broken-image-image" tabindex="-1">Serving a “broken image” image <a class="header-anchor" href="https://bitsofco.de/handling-broken-images-with-service-worker/">#</a></h2>
<p>One way to handle requests for images that don’t resolve would be to send a placeholder image in its place. For example, we can have an image like the one below to show the user that the image is broken.</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/Qo5mfYDE5v-350.avif 350w"><source type="image/webp" srcset="https://bitsofco.de/img/Qo5mfYDE5v-350.webp 350w"><img alt="Broken image icon with text below that says 'image nto found'" loading="lazy" decoding="async" src="https://bitsofco.de/img/Qo5mfYDE5v-350.png" width="350" height="276"></picture></p>
<p>We can implement this by responding to the <code>fetch</code> request with a new request for the palceholder file, <code>/broken.png</code>.</p>
<pre class="language-js" tabindex="0"><code class="language-js">self<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'fetch'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">e</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> e<span class="token punctuation">.</span><span class="token function">respondWith</span><span class="token punctuation">(</span><br> <span class="token function">fetch</span><span class="token punctuation">(</span>e<span class="token punctuation">.</span>request<span class="token punctuation">)</span><br> <span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">response</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span>response<span class="token punctuation">.</span>ok<span class="token punctuation">)</span> <span class="token keyword">return</span> response<span class="token punctuation">;</span><br><br> <span class="token comment">// User is online, but response was not ok</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">isImage</span><span class="token punctuation">(</span>e<span class="token punctuation">.</span>request<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token comment">// Fetch the broken image placeholder instead</span><br> <span class="token keyword">return</span> <span class="token function">fetch</span><span class="token punctuation">(</span><span class="token string">"/broken.png"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br><br> <span class="token punctuation">}</span><span class="token punctuation">)</span><br> <span class="token punctuation">.</span><span class="token function">catch</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">err</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br><br> <span class="token comment">// User is probably offline</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">isImage</span><span class="token punctuation">(</span>e<span class="token punctuation">.</span>request<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token comment">// do something</span><br> <span class="token punctuation">}</span><br><br> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token comment">// end fetch</span><br> <span class="token punctuation">)</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>This will work when the user is online, but if we want this to also work offline, we will need to cache the placeholder image. This is typically done during the <code>install</code> phase of the service worker lifecycle.</p>
<pre class="language-js" tabindex="0"><code class="language-js">self<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'install'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">e</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> self<span class="token punctuation">.</span><span class="token function">skipWaiting</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> e<span class="token punctuation">.</span><span class="token function">waitUntil</span><span class="token punctuation">(</span><br> caches<span class="token punctuation">.</span><span class="token function">open</span><span class="token punctuation">(</span><span class="token string">"precache"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">cache</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br><br> <span class="token comment">// Add /broken.png to "precache"</span><br> cache<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">"/broken.png"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token punctuation">}</span><span class="token punctuation">)</span><br> <span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Once the image is in the cache, we can respond to the <code>fetch</code> request with a response from the cache instead of having to make a new request over the network.</p>
<pre class="language-js" tabindex="0"><code class="language-js">self<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'fetch'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">e</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> e<span class="token punctuation">.</span><span class="token function">respondWith</span><span class="token punctuation">(</span><br> <span class="token function">fetch</span><span class="token punctuation">(</span>e<span class="token punctuation">.</span>request<span class="token punctuation">)</span><br> <span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">response</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span>response<span class="token punctuation">.</span>ok<span class="token punctuation">)</span> <span class="token keyword">return</span> response<span class="token punctuation">;</span><br><br> <span class="token comment">// User is online, but response was not ok</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">isImage</span><span class="token punctuation">(</span>e<span class="token punctuation">.</span>request<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token comment">// Get broken image placeholder from cache</span><br> <span class="token keyword">return</span> caches<span class="token punctuation">.</span><span class="token function">match</span><span class="token punctuation">(</span><span class="token string">"/broken.png"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br><br> <span class="token punctuation">}</span><span class="token punctuation">)</span><br> <span class="token punctuation">.</span><span class="token function">catch</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">err</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br><br> <span class="token comment">// User is probably offline</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">isImage</span><span class="token punctuation">(</span>e<span class="token punctuation">.</span>request<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token comment">// Get broken image placeholder from cache</span><br> <span class="token keyword">return</span> caches<span class="token punctuation">.</span><span class="token function">match</span><span class="token punctuation">(</span><span class="token string">"/broken.png"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br><br> <span class="token punctuation">}</span><span class="token punctuation">)</span><br> <span class="token punctuation">)</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>I created a <a href="https://gist.github.com/ireade/128adedc16bf1bd1de98397d437c339d">GitHub Gist</a> with the full implementation in case you want to use it in your own projects.</p>
A one-line solution to highlighting search matches2018-12-18T00:00:00Zhttps://bitsofco.de/a-one-line-solution-to-highlighting-search-matches/<p>I’ve recently been making some improvements to the search page of this blog, and wanted to implement a feature where the search term was highlighted everywhere it appeared in the results.</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/pSILz24BB_-1282.avif 1282w"><source type="image/webp" srcset="https://bitsofco.de/img/pSILz24BB_-1282.webp 1282w"><img alt="Screenshot of Search page with instances of "grid" serach query highlighted in the results" loading="lazy" decoding="async" src="https://bitsofco.de/img/pSILz24BB_-1282.png" width="1282" height="843"></picture></p>
<p>Although libraries for this exist, I wanted to try to implement it myself as it seemed like an interesting challenge. I initially spent hours coming up with weird and convoluted solutions, until I realised that the <code>String.replace()</code> method could solve this in one line!</p>
<h2 id="primer-on-string-replace" tabindex="-1">Primer on <code>String.replace()</code> <a class="header-anchor" href="https://bitsofco.de/a-one-line-solution-to-highlighting-search-matches/">#</a></h2>
<p>The <code>replace()</code> method on Strings allows us to simultaneously search for matches of a substring within an original string, and replace that substring with something new. Here’s a basic example of how we can replace the substring “Hi” with "Hello", within the original string “Hi, how are you?”.</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> substring <span class="token operator">=</span> <span class="token string">"Hi"</span><span class="token punctuation">;</span><br><span class="token keyword">const</span> original <span class="token operator">=</span> <span class="token string">"Hi, how are you?"</span><span class="token punctuation">;</span><br><br>original<span class="token punctuation">.</span><span class="token function">replace</span><span class="token punctuation">(</span>substring<span class="token punctuation">,</span> <span class="token string">"Hello"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br><span class="token comment">// Result => "Hello, how are you?"</span></code></pre>
<p>Instead of searching for a basic substring, we can also use regular expressions, which can allow us to search for all instances of a substring.</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> substring <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">RegExp</span><span class="token punctuation">(</span><span class="token string">"Hi"</span><span class="token punctuation">,</span> <span class="token string">"gi"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// "g" for global, "i" for case-insensitive</span><br><span class="token keyword">const</span> original <span class="token operator">=</span> <span class="token string">"Hi, how are you? hi, I'm alright"</span><span class="token punctuation">;</span><br><br>original<span class="token punctuation">.</span><span class="token function">replace</span><span class="token punctuation">(</span>substring<span class="token punctuation">,</span> <span class="token string">"Hello"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br><span class="token comment">// Result => "Hello, how are you? Hello, I'm alright"</span></code></pre>
<p>What I didn’t realise before was that the second argument of the <code>replace()</code> method could be a function. This function has one argument, which is the original matched string.</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> substring <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">RegExp</span><span class="token punctuation">(</span><span class="token string">"Hi"</span><span class="token punctuation">,</span> <span class="token string">"gi"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token keyword">const</span> original <span class="token operator">=</span> <span class="token string">"Hi, how are you? hi, I'm alright"</span><span class="token punctuation">;</span><br><br>original<span class="token punctuation">.</span><span class="token function">replace</span><span class="token punctuation">(</span>substring<span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">match</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br><br> <span class="token comment">// Replace with the original text, but upper case</span><br> <span class="token keyword">return</span> match<span class="token punctuation">.</span><span class="token function">toUpperCase</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br><span class="token comment">// Result => "HI, how are you? HI, I'm alright"</span></code></pre>
<p>This allows us to have greater control over what we replace the substring with. And, it provides the exact use case that I needed which is to modify the original string in order to highlight it.</p>
<h2 id="one-line-match-highlighting" tabindex="-1">One-line match highlighting <a class="header-anchor" href="https://bitsofco.de/a-one-line-solution-to-highlighting-search-matches/">#</a></h2>
<p>Here’s the solution I came up with:</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> term<span class="token punctuation">;</span> <span class="token comment">// search query we want to highlight in results</span><br><span class="token keyword">const</span> results<span class="token punctuation">;</span> <span class="token comment">// search results</span><br><br>results<span class="token punctuation">.</span><span class="token function">replace</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">RegExp</span><span class="token punctuation">(</span>term<span class="token punctuation">,</span> <span class="token string">"gi"</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">match</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string"><mark></span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>match<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"></mark></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Let’s break this down. We have two variables; <code>term</code>, the search term that we want to highlight, and <code>results</code>, the string of text that the search term may be in.</p>
<p>Using the <code>replace()</code> method, I am performing a search for all instances (<code>g</code>) of the query in any case (<code>i</code>). With each match found, I am replacing the original matched text with the same text, but with the <code><mark></code> element surrounding it.</p>
<p>The <code><mark></code> HTML element is used to highlight text, so is the perfect element for this use case.</p>
<p>You can see how this fits within my greater search implementation on my <a href="https://github.com/ireade/bitsofcode-ghost/blob/master/src/js/search.js#L60-L63">GitHub repository for this theme</a>.</p>
<h2 id="a-limitation" tabindex="-1">A limitation <a class="header-anchor" href="https://bitsofco.de/a-one-line-solution-to-highlighting-search-matches/">#</a></h2>
<p>One limitation of this solution to note is that it only works if your search string is plain text. That is, we can't use this <code>replace()</code> if the original string (i.e. the <code>results</code> variable) includes HTML. This is because it will reaplce <em>all</em> instances of the search term, including in HTML attributes such as the <code>href</code> of links.</p>
<p>Take this example:</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> term <span class="token operator">=</span> <span class="token string">"visibility"</span><br><span class="token keyword">const</span> results <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string"><br><h2><br> <a href="/the-visibility-property-isnt-just-about-visibility/"><br> The visibility property isn’t just about visibility<br> </a><br></h2><br></span><span class="token template-punctuation string">`</span></span><br><br>results<span class="token punctuation">.</span><span class="token function">replace</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">RegExp</span><span class="token punctuation">(</span>term<span class="token punctuation">,</span> <span class="token string">"gi"</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">match</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string"><mark></span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>match<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"></mark></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>This will add the mark element to the link matches too :(</p>
<p>This will result in the link itself having the <code><mark></code> element around it:</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</span><span class="token punctuation">></span></span><br> <a href=\"/the-<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mark</span><span class="token punctuation">></span></span>visibility<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mark</span><span class="token punctuation">></span></span>-property-isnt-just-about-<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mark</span><span class="token punctuation">></span></span>visibility<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mark</span><span class="token punctuation">></span></span>/\"><br> The <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mark</span><span class="token punctuation">></span></span>visibility<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mark</span><span class="token punctuation">></span></span> property isn’t just about <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mark</span><span class="token punctuation">></span></span>visibility<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mark</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span></code></pre>
<p>This wasn't an issue for me because, due to the way my search script is implemented, I was easily able to separate the text I wanted to perform the highlight on. However, if you need to perform the highlight on actual HTML, you may need to use one of the highlighting libraries out there.</p>
There’s no reason to use pointer-events for HTML elements2018-12-17T00:00:00Zhttps://bitsofco.de/theres-no-reason-to-use-pointer-events-for-html-elements/<style>
button:hover, button:focus { background-color: #ffdb3a; }
</style>
<p><ins><strong>UPDATE</strong>: Some people have pointed out some genuinely great use cases for this property which I've listed at the end of the article. I still stand by all the points I made against the use cases I pointed out in this article, but I now believe there are <em>some</em> good reasons to use <code>pointer-events</code> for HTML elements!</ins></p>
<p>The <code>pointer-events</code> CSS property controls if and how elements can be targeted by pointer inputs such as a mouse. It initially formed part of the <a href="https://www.w3.org/TR/SVG11/interact.html#PointerEventsProperty">specification for SVGs</a> to allow more fine-tuned control of where in a shape a user could interact with.</p>
<p>This is useful, for example, if we have shapes placed on top of each other and want the shape that is not necessarily at the top of the stack to receive (or not receive) mouse events. In the example below, I have a rectangle placed behind a circle. The circle has a stroke but no fill, meaning that the rectangle behind it is visible.</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>style</span><span class="token punctuation">></span></span><span class="token style"><span class="token language-css"><br> <span class="token selector">circle</span> <span class="token punctuation">{</span> <span class="token property">pointer-events</span><span class="token punctuation">:</span> visiblePoint<span class="token punctuation">;</span> <span class="token punctuation">}</span><br></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>style</span><span class="token punctuation">></span></span><br><br><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0 0 20 10<span class="token punctuation">"</span></span> <span class="token attr-name">xmlns</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://www.w3.org/2000/svg<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>rect</span> <span class="token attr-name">x</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0<span class="token punctuation">"</span></span> <span class="token attr-name">y</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>10<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>10<span class="token punctuation">"</span></span> <span class="token attr-name">fill</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>black<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>circle</span> <span class="token attr-name">cx</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>5<span class="token punctuation">"</span></span> <span class="token attr-name">cy</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>5<span class="token punctuation">"</span></span> <span class="token attr-name">r</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>3<span class="token punctuation">"</span></span> <span class="token attr-name">stroke</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>yellow<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></code></pre>
<p>By applying <code>pointer-events: visiblePoint;</code> to the circle, it allows the middle of the circle to be clicked, even though it appears that we are clicking the rectangle behind it.</p>
<p>Try clicking in the middle of the circle below. Even if you click on an area that looks like it belongs to the rectangle behind, the circle receives the click event and the stroke changes colour.</p>
<pre class="language-js" tabindex="0"><code class="language-js">document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">"circle"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">"click"</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">e</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> <span class="token keyword">const</span> randomColour <span class="token operator">=</span> Math<span class="token punctuation">.</span><span class="token function">round</span><span class="token punctuation">(</span>Math<span class="token punctuation">.</span><span class="token function">random</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">*</span> <span class="token number">0xFFFFFF</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> e<span class="token punctuation">.</span>target<span class="token punctuation">.</span>style<span class="token punctuation">.</span>stroke <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">#</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>randomColour<span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token number">16</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">padStart</span><span class="token punctuation">(</span><span class="token number">6</span><span class="token punctuation">,</span><span class="token string">"0"</span><span class="token punctuation">)</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<h2 id="pointer-events-and-html-elements" tabindex="-1"><code>pointer-events</code> and HTML elements <a class="header-anchor" href="https://bitsofco.de/theres-no-reason-to-use-pointer-events-for-html-elements/">#</a></h2>
<p>Since the initial specification in SVG, the <code>point-events</code> property has made its way to CSS. Although limited, we can now use the <code>pointer-events</code> property with regular HTML elements.</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>style</span><span class="token punctuation">></span></span><span class="token style"><span class="token language-css"><br> <span class="token selector">button:hover, button:focus</span> <span class="token punctuation">{</span> <span class="token property">background-color</span><span class="token punctuation">:</span> #ffdb3a<span class="token punctuation">;</span> <span class="token punctuation">}</span><br></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>style</span><span class="token punctuation">></span></span><br><br><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span><span class="token punctuation">></span></span>Hover or focus over me<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">pointer-events</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span>Hover or focus over me too<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></code></pre>
<p><button>Hover or focus over me</button>
<button style="pointer-events: none;">Hover or focus over me too</button></p>
<p>When used with non-SVG elements, only three values are available - <code>inherit</code>, <code>auto</code> and <code>none</code>.</p>
<p>The <code>inherit</code> value works as it does with any other CSS property. When applied to an element, it will use the same value for the property as the element’s parent.</p>
<p>The <code>auto</code> value will restore the default functionality. That is, the pointer events for the element will behave as it does without the <code>pointer-events</code> property.</p>
<p>Finally, we have <code>none</code>. As we saw in the example above, this value will prevent all pointer events - hover, active, click - from being applied to the element.</p>
<h2 id="reasons-to-not-use-pointer-events-none" tabindex="-1">Reasons to (not) use <code>pointer-events: none</code> <a class="header-anchor" href="https://bitsofco.de/theres-no-reason-to-use-pointer-events-for-html-elements/">#</a></h2>
<p>As you can tell from the title of this article, my opinion is that there is no reason to use the <code>pointer-events: none</code> CSS rule on non-SVG, and by extension non-graphical, HTML elements.</p>
<p>My main reason for thinking so is because, by definition, this CSS rule will only be used when the HTML order would result in the element being able to be interacted with. The purpose of this rule seems to be to change source order in a pretty significant way.</p>
<p>Doing some research, I’ve come across a few scenarios in which people use this rule. Here’s what they are, and why I think they aren’t the best reasons.</p>
<h3 id="disabling-interactive-elements" tabindex="-1">Disabling interactive elements <a class="header-anchor" href="https://bitsofco.de/theres-no-reason-to-use-pointer-events-for-html-elements/">#</a></h3>
<p>The most popular use case I have come across for the <code>pointer-events: none</code> rule is to stop users from interacting with disabled elements, such as submit buttons or input fields.</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>style</span><span class="token punctuation">></span></span><span class="token style"><span class="token language-css"><br> <span class="token selector">.disabled</span> <span class="token punctuation">{</span><br> <span class="token property">pointer-events</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>style</span><span class="token punctuation">></span></span><br><br><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>form</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>disabled<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Submit<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>form</span><span class="token punctuation">></span></span></code></pre>
<div class="code-addendum warning">Don’t do this!</div>
<p>This isn’t an effective way to disable a form field because it only affects pointer events. Users can still interact with the element if navigating using a keyboard and there is no communication to assistive technology that the button is actually disabled.</p>
<p>A much better solution would be to use the <code>disabled</code> attribute in addition to visual styles to let the user know that the button is disabled.</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>style</span><span class="token punctuation">></span></span><span class="token style"><span class="token language-css"><br> <span class="token selector">[disabled]</span> <span class="token punctuation">{</span><br> <span class="token property">cursor</span><span class="token punctuation">:</span> not-allowed<span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>style</span><span class="token punctuation">></span></span><br><br><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>form</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">disabled</span><span class="token punctuation">></span></span>Submit<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>form</span><span class="token punctuation">></span></span></code></pre>
<div class="code-addendum success">Better alternative</div>
<p>If we’re using the <code>disabled</code> attribute, there would be no need for the <code>pointer-events: none</code> rule, as the button will be properly disabled to all inputs.</p>
<h3 id="overlays" tabindex="-1">Overlays <a class="header-anchor" href="https://bitsofco.de/theres-no-reason-to-use-pointer-events-for-html-elements/">#</a></h3>
<p>Another example I’ve seen for the <code>pointer-events: none</code> rule is to allow the content behind an overlay to remain interactive.</p>
<p>In this case, it seems almost counterproductive to allow pointer events to the elements behind the overlay. Typically, when an overlay is used, it is a visual indication that only the elements within a restricted section, usually a modal, should be interacted with. For example, it is recommended that keyboard focus should be restricted to elements within a modal if it is active.</p>
<p>Therefore, it seems like cases in which an overlay is used are the exact cases where you would <em>not</em> want users to be able to click on elements behind the overlay.</p>
<h3 id="targeting-specific-children-of-interactive-elements" tabindex="-1">Targeting specific children of interactive elements <a class="header-anchor" href="https://bitsofco.de/theres-no-reason-to-use-pointer-events-for-html-elements/">#</a></h3>
<p>One interesting example of the <code>pointer-events: none</code> rule I’ve seen is to restrict the interactive sections of certain interactive elements. For example, let’s say we have a button with some text and an icon, and we only want the click of the icon to trigger the behaviour.</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">pointer-events</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>img</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>icon<span class="token punctuation">"</span></span> <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Icon<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">pointer-events</span><span class="token punctuation">:</span> auto<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>Click the icon to do stuff!<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></code></pre>
<p>This actually does have some benefit, as the text content and label remains within the <code><button></code> element. However, I think a better and more semantic solution would be to restructure the markup.</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">aria-labelled-by</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>label<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>img</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>icon<span class="token punctuation">"</span></span> <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Icon<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span><br><br><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>label<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Click the icon to do stuff!<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></code></pre>
<div class="code-addendum success">Better alternative</div>
<p>This way, it is clear from the markup alone what each element is for and what each element will do. The <code><span></code> is purely to label, whereas the <code><img></code> is what is interactive. Looking at this markup as compared to the previous solution, it is much clearer what the abilities of the elements are.</p>
<h2 id="is-there-any-reason-to-use-pointer-events-for-non-svg-elements" tabindex="-1">Is there any reason to use pointer-events for non-SVG elements? <a class="header-anchor" href="https://bitsofco.de/theres-no-reason-to-use-pointer-events-for-html-elements/">#</a></h2>
<p>As I’ve said, which is purely my opinion, I don’t think there is a reason to use <code>pointer-events</code> for regular, interactive, HTML elements. However, it exists in CSS which makes me think it might have some use I’m not thinking of. I would love to hear from you if you’ve found any genuinely good use case for it.</p>
<hr>
<p><ins><strong>UPDATE</strong>: Here's some answers I've already got from people who have found some use cases for <code>pointer-events: none</code> on regualr HTML elements.</ins></p>
<blockquote>
<p>Also, on <a href="https://t.co/sCMU334ga8">https://t.co/sCMU334ga8</a> it's used on layout elements where the children of a flexbox should be interactive, but clicks between them should fall to elements in the background
— Jake Archibald (@jaffathecake) <a href="https://twitter.com/jaffathecake/status/1074582984668389376?ref_src=twsrc%5Etfw">December 17, 2018</a></p>
</blockquote>
<blockquote>
<p>Here's one of the examples. This div is pointer-events:none, but all the children are pointer-events:auto.</p>
<p>That means you can click through to the canvas in between the buttons. <a href="https://t.co/Ua5oKKFPP7">pic.twitter.com/Ua5oKKFPP7</a>
— Jake Archibald (@jaffathecake) <a href="https://twitter.com/jaffathecake/status/1074591305760731141?ref_src=twsrc%5Etfw">December 17, 2018</a></p>
</blockquote>
<blockquote>
<p>- making labels inside an input non-responsive, such as the unit of measurement, which typically floats inside the input to the right side.
— Dan Burzo, birdsite edition (@danburzo) <a href="https://twitter.com/danburzo/status/1074585392039501826?ref_src=twsrc%5Etfw">December 17, 2018</a></p>
</blockquote>
<blockquote>
<p>- for CSS-only tooltips (with the :before pseudo-element and generated content), I'm assuming we have pointer-events: none on them to avoid weird hover effects.
- for enhancing performance while transforming some things (I need to source this statement)
— Dan Burzo, birdsite edition (@danburzo) <a href="https://twitter.com/danburzo/status/1074587356974465025?ref_src=twsrc%5Etfw">December 17, 2018</a></p>
</blockquote>
Using aria-live2018-12-14T00:00:00Zhttps://bitsofco.de/using-aria-live/<p>Many web pages today have their content dynamically changed using Javascript. An example of this is the <a href="https://bitsofco.de/archive">search page</a> on this blog. When the page is initially loaded, the only content in the <code><main></code> section of the page is a search form. But, when you type in a query such as “css” and search, several articles related to that term begin to appear on the page.</p>
<p>If you are looking at the page, it is obvious when the articles appear because you can see the content change. However, if you’re using a screen reader, there is no way to tell exactly when this happens. The <code>aria-live</code> attribute allows us to tell assistive technology what parts of the page are likely to change. With this information, it can listen for changes to those particular elements and notify the user of any updates made.</p>
<h2 id="how-aria-live-works" tabindex="-1">How <code>aria-live</code> works <a class="header-anchor" href="https://bitsofco.de/using-aria-live/">#</a></h2>
<p>The <code>aria-live</code> attribute is added to specific HTML elements which we expect the content within them to change and we want the change to be communicated to users of assistive technology.</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">aria-live</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>polite<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br> <span class="token comment"><!-- content will be updated with Javascript --></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></code></pre>
<p>The attribute has three potential values which control if and how changes are communicated.</p>
<h3 id="aria-live-off" tabindex="-1"><code>aria-live="off"</code> <a class="header-anchor" href="https://bitsofco.de/using-aria-live/">#</a></h3>
<p>The <code>off</code> value is practically the same as if the attribute was omitted entirely. Assistive technology will not keep track of changes to the elements content and, as such, will not notify its users of any changes.</p>
<p>In the example below, I have a <code><button></code> element which, on click, will update the content of the <code><div></code> below it.</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token special-attr"><span class="token attr-name">onclick</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value javascript language-javascript">document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span><span class="token string">'content'</span><span class="token punctuation">)</span><span class="token punctuation">.</span>textContent <span class="token operator">=</span> <span class="token string">'I have changed!'</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span><br> Click me to change the content of the div element below<br><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span><br><br><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>content<span class="token punctuation">"</span></span> <span class="token attr-name">aria-live</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>off<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br> My content will change<br><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></code></pre>
<p>As you’ll see in the video below, since the <code><div></code> has the <code>aria-live</code> value of <code>off</code>, nothing happens when the button is clicked and the content changes.</p>
<video controls="" poster="https://res.cloudinary.com/ireaderinokun/video/upload/v1544630832/bitsofcode/aria-live_off.jpg">
<source src="https://res.cloudinary.com/ireaderinokun/video/upload/v1544630832/bitsofcode/aria-live_off.webm" type="video/webm">
<source src="https://res.cloudinary.com/ireaderinokun/video/upload/v1544630832/bitsofcode/aria-live_off.mp4" type="video/mp4">
</video>
<h3 id="aria-live-polite" tabindex="-1"><code>aria-live="polite"</code> <a class="header-anchor" href="https://bitsofco.de/using-aria-live/">#</a></h3>
<p>The <code>polite</code> value, will ensure that users of assistive technology will be notified of any changes to the element. The notification will happen at the next available point, meaning that there will not be an interruption to whatever task or information the user was currently in the process of.</p>
<p>Using the same example above but with the <code>polite</code> value, you’ll see that the screen reader waits to finish reading the label of the button before speaking the contents of the changed <code><div></code>.</p>
<video controls="" poster="https://res.cloudinary.com/ireaderinokun/video/upload/v1544630809/bitsofcode/aria-live_polite.jpg">
<source src="https://res.cloudinary.com/ireaderinokun/video/upload/v1544630809/bitsofcode/aria-live_polite.webm" type="video/webm">
<source src="https://res.cloudinary.com/ireaderinokun/video/upload/v1544630809/bitsofcode/aria-live_polite.mp4" type="video/mp4">
</video>
<h3 id="aria-live-assertive" tabindex="-1"><code>aria-live="assertive"</code> <a class="header-anchor" href="https://bitsofco.de/using-aria-live/">#</a></h3>
<p>Finally, the <code>assertive</code> value will communicate changes to the element immediately, disrupting any other task or information the user was currently in the process of.</p>
<p>Again, using the same example above but with the <code>assertive</code> value, you’ll see that the screen reader interrupts reading the label of the button to speak the contents of the changed <code><div></code>.</p>
<video controls="" poster="https://res.cloudinary.com/ireaderinokun/video/upload/v1544630844/bitsofcode/aria-live_assertive.jpg">
<source src="https://res.cloudinary.com/ireaderinokun/video/upload/v1544630844/bitsofcode/aria-live_assertive.webm" type="video/webm">
<source src="https://res.cloudinary.com/ireaderinokun/video/upload/v1544630844/bitsofcode/aria-live_assertive.mp4" type="video/mp4">
</video>
<h2 id="considerations-when-using-aria-live" tabindex="-1">Considerations when using <code>aria-live</code> <a class="header-anchor" href="https://bitsofco.de/using-aria-live/">#</a></h2>
<p>If applied incorrectly, the <code>aria-live</code> attribute can do more harm than good. So, it’s important to take into account a few things if we want to use it.</p>
<h3 id="include-aria-live-in-the-initial-markup" tabindex="-1">Include <code>aria-live</code> in the initial markup <a class="header-anchor" href="https://bitsofco.de/using-aria-live/">#</a></h3>
<p>Assistive technology will initially scan the document for instances of the <code>aria-live</code> attribute and keep track of elements that include it. This means that, if we want to notify users of a change within an element, we need to include the attribute in the original markup. In other words, we should not use Javascript to add the <code>aria-live</code> attribute to elements dynamically.</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>will-change<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br> <span class="token comment"><!-- content will be updated with Javascript --></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span><br><br><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span><span class="token punctuation">></span></span><br>document.querySelector(".will-change").setAttribute("aria-live", "polite");<br><br>document.querySelector(".will-change").textContent = "New content!";<br><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span><span class="token punctuation">></span></span></code></pre>
<div class="code-addendum warning">Don’t do this!</div>
<p>Instead, make sure that any elements that will change and needs their changes communicated, already have the <code>aria-live</code> attribute:</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">aria-live</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>polite<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>will-change<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br> <span class="token comment"><!-- content will be updated with Javascript --></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span><br><br><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span><span class="token punctuation">></span></span><br>document.querySelector(".will-change").textContent = "New content!";<br><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span><span class="token punctuation">></span></span></code></pre>
<div class="code-addendum success">Do this instead</div>
<h3 id="only-apply-aria-live-to-specific-elements-not-the-entire-page" tabindex="-1">Only apply <code>aria-live</code> to specific elements, not the entire page <a class="header-anchor" href="https://bitsofco.de/using-aria-live/">#</a></h3>
<p>You may be thinking that a good workaround to using <code>aria-live</code>would be to just include the attribute on the <code><body></code> element so assistive technology will keep track of any and all changes on the page.</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>body</span> <span class="token attr-name">aria-live</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>polite<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>header</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>header</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>main</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>will-change<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token comment"><!-- content will change --></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>will-change<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token comment"><!-- content will change --></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>main</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>footer</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>footer</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></code></pre>
<div class="code-addendum warning">Don’t do this!</div>
<p>The problem with this is every single change made on the page will be communicated to the user. Think of a single page application, for example. If the main content of the page changes, the assistive technology will try to read out every single piece of text that has changed on the page, which will essentially be the entire content of the page.</p>
<p>This is rarely what we actually want to happen. So, we should only use <code>aria-live</code> on the elements which we want their change in content actively communicated to the user.</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>body</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>header</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>header</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>main</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span> <span class="token attr-name">aria-live</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>polite<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>will-change<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token comment"><!-- content will change --></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">aria-live</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>polite<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>will-change<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token comment"><!-- content will change --></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>main</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>footer</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>footer</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></code></pre>
<div class="code-addendum success">Do this instead</div>
<h3 id="be-polite" tabindex="-1">Be <code>polite</code> <a class="header-anchor" href="https://bitsofco.de/using-aria-live/">#</a></h3>
<p>As we saw in my demo above, the <code>assertive</code> value will interrupt whatever task the screen reader user is in the middle of to announce any changes to the page. Therefore, it is important to only use this value if it is absolutely necessary to communicate changes immediately as they come. In most cases, the <code>polite</code> value is sufficient.</p>
<h3 id="switch-off" tabindex="-1">Switch <code>off</code> <a class="header-anchor" href="https://bitsofco.de/using-aria-live/">#</a></h3>
<p>If we are certain that an element will no longer be updated, use the <code>off</code> value to tell assistive technology that it no longer needs to keep track of changes.</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token comment">// Change content</span><br>document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">".will-change"</span><span class="token punctuation">)</span><span class="token punctuation">.</span>textContent <span class="token operator">=</span> <span class="token string">"New content!"</span><span class="token punctuation">;</span><br><br><span class="token comment">// Stop tracking changes to the element</span><br>document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">".will-change"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">setAttribute</span><span class="token punctuation">(</span><span class="token string">"aria-live"</span><span class="token punctuation">,</span> <span class="token string">"off"</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<h2 id="applying-aria-live-an-example" tabindex="-1">Applying <code>aria-live</code>: an example <a class="header-anchor" href="https://bitsofco.de/using-aria-live/">#</a></h2>
<p>As I mentioned at the start of this article, a good use case for the <code>aria-live</code> attribute is for places where content on the page is dynamically updated, such as the search page on this blog.</p>
<p>I recently updated that page to include this attribute. All I did was add the <code>aria-live</code> attribute to a <code><span></code> element, the content of which is updated when a user interacts with the form.</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>search-results<span class="token punctuation">"</span></span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>region<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>search-results-message<span class="token punctuation">"</span></span> <span class="token attr-name">aria-live</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>polite<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br> Use the <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#search-form<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>search form<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span> above to search for articles.<br> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span></code></pre>
<p>When the form is submitted, the contents of the <code>#search-results-message</code> element changes to display the number of articles found. This way, the user is informed of the completion of the search and they can continue to navigate the page as expected.</p>
<p>Here is how a screen reader user would interact with the form, before and after the implementation.</p>
<video controls="" poster="https://res.cloudinary.com/ireaderinokun/video/upload/v1544630906/bitsofcode/aria-live_before_and_after.jpg">
<source src="https://res.cloudinary.com/ireaderinokun/video/upload/v1544630906/bitsofcode/aria-live_before_and_after.webm" type="video/webm">
<source src="https://res.cloudinary.com/ireaderinokun/video/upload/v1544630906/bitsofcode/aria-live_before_and_after.mp4" type="video/mp4">
</video>
Cache API 1012018-12-13T00:00:00Zhttps://bitsofco.de/cache-api-101/<p>A cache is a form of storage for “requests” and “responses”. When you visited this page, you made a request for <code>/cache-api-101</code>. The response to that request was the HTML document you are viewing in your browser right now. The cache API allows us to store these requests and responses locally in the browser.</p>
<p>The cache API was created as part of the <a href="https://w3c.github.io/ServiceWorker/#cache">service worker specification</a> to “allow authors to fully manage their content caches for offline use”. In my tutorial, <a href="https://bitsofco.de/going-offline-first">Going Offline First</a>, I demonstrated how I used the cache API to convert this blog to an offline-first strategy, enabling readers to save articles for offline. If you want to see a real-world use case of cache, I would suggest you watch that.</p>
<p>In this article, I want to go over the cache API terminology and how to use the API methods.</p>
<h2 id="cache-storage-organisation-and-terminology" tabindex="-1">Cache storage organisation and terminology <a class="header-anchor" href="https://bitsofco.de/cache-api-101/">#</a></h2>
<p>Before we dive into the specifics of how to create and use a cache, let’s go over how the cache storage is organised and what the different bits are called.</p>
<p>If you open up your browser developer tools, you will see a section for “Cache Storage”.</p>
<ul>
<li><strong>Firefox</strong>: Storage > Cache Storage</li>
<li><strong>Google Chrome</strong>: Application > Cache > Cache Storage</li>
</ul>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/QXEt7z0eEh-2240.avif 2240w"><source type="image/webp" srcset="https://bitsofco.de/img/QXEt7z0eEh-2240.webp 2240w"><img alt="Firefox developer tools with Cache storage open" loading="lazy" decoding="async" src="https://bitsofco.de/img/QXEt7z0eEh-2240.png" width="2240" height="1440"></picture></p>
<p>Each origin, e.g. for this website <code>https://bitsofco.de</code>, has one <strong>cache storage</strong>. This object can contain multiple <strong>cache</strong> objects, each containing a <strong>request-response list</strong>.</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/H_7QC03oCb-738.avif 738w"><source type="image/webp" srcset="https://bitsofco.de/img/H_7QC03oCb-738.webp 738w"><img alt="Diagram of request-response nested with the articles-cache nested within the cache storage" loading="lazy" decoding="async" src="https://bitsofco.de/img/H_7QC03oCb-738.png" width="738" height="577"></picture></p>
<p>Let’s take this in reverse. An example of a request-response could be, as I mentioned at the start of this article, this page’s URL (the request) and the HTML content (the response). We can save this request-response in a cache object that I call “articles-cache”. This “articles-cache” cache would belong to the cache storage for the <code>https://bitsofco.de</code> origin.</p>
<h2 id="crud-with-the-cache-api" tabindex="-1">CRUD with the cache API <a class="header-anchor" href="https://bitsofco.de/cache-api-101/">#</a></h2>
<p>Like any form of storage, we need to be able to perform create, read, update, and delete operations to use it. To illustrate this, let’s create a cache I will call “my-cache”, which will store a page, <code>/subscribe</code>, and an image, <code>/assets/images/profile.png</code>.</p>
<h3 id="creating-a-new-cache-and-adding-items" tabindex="-1">Creating a new cache and adding items <a class="header-anchor" href="https://bitsofco.de/cache-api-101/">#</a></h3>
<p>To add a <strong>request-response</strong> item to a cache, we need to first create the cache to add to it. As I mentioned, an origin can have multiple caches within the cache storage, so we need to specify which cache we want to add the request-response item to.</p>
<p>The method for creating and/or accessing a cache is <code>caches.open()</code>. The <code>caches</code> name is a global variable that refers to the cache storage object. The <code>open()</code> method accepts one argument, the name of the cache we want to access.</p>
<pre class="language-js" tabindex="0"><code class="language-js">caches<span class="token punctuation">.</span><span class="token function">open</span><span class="token punctuation">(</span><span class="token string">"my-cache"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">myCache</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> <span class="token comment">// Do stuff with myCache</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>To add a <strong>request-response</strong> item to a cache, we use the <code>add()</code> method on the cache object returned. This method accepts a request, which can either be a full request object, or just a URL.</p>
<pre class="language-js" tabindex="0"><code class="language-js">caches<span class="token punctuation">.</span><span class="token function">open</span><span class="token punctuation">(</span><span class="token string">"my-cache"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">myCache</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> <span class="token comment">// URL only</span><br> myCache<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">"/subscribe"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token comment">// Full request object</span><br> myCache<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">Request</span><span class="token punctuation">(</span><span class="token string">'/subscribe'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span><br> <span class="token literal-property property">method</span><span class="token operator">:</span> <span class="token string">"GET"</span><span class="token punctuation">,</span><br> <span class="token literal-property property">headers</span><span class="token operator">:</span> <span class="token keyword">new</span> <span class="token class-name">Headers</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br> <span class="token string-property property">'Content-Type'</span><span class="token operator">:</span> <span class="token string">'text/html'</span><br> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">,</span><br> <span class="token comment">/* more request options */</span><br> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>The browser will make the fetch request given the specified <code>Request</code>, and save the response to the cache.</p>
<p>If we have multiple items to add, we can use the <code>addAll()</code> method, which accepts an array of requests.</p>
<pre class="language-js" tabindex="0"><code class="language-js">caches<span class="token punctuation">.</span><span class="token function">open</span><span class="token punctuation">(</span><span class="token string">"my-cache"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">myCache</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> myCache<span class="token punctuation">.</span><span class="token function">addAll</span><span class="token punctuation">(</span><span class="token punctuation">[</span><br> <span class="token string">"/subscribe"</span><span class="token punctuation">,</span><br> <span class="token string">"/assets/images/profile.png"</span><br> <span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<h3 id="reading-items-from-cache-storage" tabindex="-1">Reading items from cache storage <a class="header-anchor" href="https://bitsofco.de/cache-api-101/">#</a></h3>
<p>To find an item from the cache storage, we can use the <code>caches.match()</code> method.</p>
<pre class="language-js" tabindex="0"><code class="language-js">caches<span class="token punctuation">.</span><span class="token function">match</span><span class="token punctuation">(</span><span class="token string">"/subscribe"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">cachedResponse</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span>cachedResponse<span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token comment">// Do something with cachedResponse</span><br> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span><br> <span class="token comment">// Handle if response not found</span><br> <span class="token punctuation">}</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>We can also look within a particular cache with the same method on the <code>cache</code> object. This is helpful if we have multiple cache objects and we know that an item may exist in a particular one.</p>
<pre class="language-js" tabindex="0"><code class="language-js">caches<span class="token punctuation">.</span><span class="token function">open</span><span class="token punctuation">(</span><span class="token string">"my-cache"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">myCache</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> myCache<span class="token punctuation">.</span><span class="token function">match</span><span class="token punctuation">(</span><span class="token string">"/subscribe"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">cachedResponse</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br><br> <span class="token keyword">if</span> <span class="token punctuation">(</span>cachedResponse<span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token comment">// Do something with cachedResponse</span><br> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span><br> <span class="token comment">// Handle if response not found</span><br> <span class="token punctuation">}</span><br><br> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Similar to the <code>addAll()</code> method, we also have a <code>matchAll()</code> method, which will return an array of cached responses. This is useful for searching for all items that match a URL fragment, e.g. any items with <code>/images/</code> in their path.</p>
<pre class="language-js" tabindex="0"><code class="language-js">caches<span class="token punctuation">.</span><span class="token function">open</span><span class="token punctuation">(</span><span class="token string">"my-cache"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">myCache</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> myCache<span class="token punctuation">.</span><span class="token function">matchAll</span><span class="token punctuation">(</span><span class="token string">"/images/"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">cachedResponses</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br><br> <span class="token keyword">if</span> <span class="token punctuation">(</span>cachedResponses<span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token comment">// Do something with cachedResponses</span><br> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span><br> <span class="token comment">// Handle if response not found</span><br> <span class="token punctuation">}</span><br><br> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<h3 id="updating-an-item-in-the-cache" tabindex="-1">Updating an item in the cache <a class="header-anchor" href="https://bitsofco.de/cache-api-101/">#</a></h3>
<p>Next, we may want to update a request-response item in the cache. Let’s say, for example, the content of the <code>/subscribe</code> page has changed and we want to update what is in the cache with the new version.</p>
<p>We can do this in one of two ways. First, we can call the same, <code>cache.add()</code> method. This will first perform a fetch for the request page and replace any request-response item in the cache that has the same URL.</p>
<pre class="language-js" tabindex="0"><code class="language-js">caches<span class="token punctuation">.</span><span class="token function">open</span><span class="token punctuation">(</span><span class="token string">"my-cache"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">myCache</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> <span class="token comment">// browser will request and cache the response</span><br> myCache<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">"/subscribe"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Alternatively, if we want more control over what response is put in the cache, we can use the <code>cache.put()</code> method. Using this method, we need to specify both a request and a response. This means we will need to fetch the document ourselves first.</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> request <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Request</span><span class="token punctuation">(</span><span class="token string">"/subscribe"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br><span class="token function">fetch</span><span class="token punctuation">(</span>request<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">fetchResponse</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> caches<span class="token punctuation">.</span><span class="token function">open</span><span class="token punctuation">(</span><span class="token string">'my-cache'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">myCache</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> cache<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span>request<span class="token punctuation">,</span> fetchResponse<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<h3 id="deleting-an-item-from-the-cache" tabindex="-1">Deleting an item from the Cache <a class="header-anchor" href="https://bitsofco.de/cache-api-101/">#</a></h3>
<p>Finally, we need to be able to remove a request-response item from the cache or delete a cache altogether.</p>
<p>To remove a specific item from the Cache, we use the <code>cache.delete()</code> method, passing in the request URL.</p>
<pre class="language-js" tabindex="0"><code class="language-js">caches<span class="token punctuation">.</span><span class="token function">open</span><span class="token punctuation">(</span><span class="token string">"my-cache"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">myCache</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> myCache<span class="token punctuation">.</span><span class="token function">delete</span><span class="token punctuation">(</span><span class="token string">"/subscribe"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>To remove a cache altogether, we can use the same method on the <code>caches</code> object.</p>
<pre class="language-js" tabindex="0"><code class="language-js">caches<span class="token punctuation">.</span><span class="token function">delete</span><span class="token punctuation">(</span><span class="token string">"my-cache"</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<h2 id="where-to-use-the-cache-api" tabindex="-1">Where to use the cache API <a class="header-anchor" href="https://bitsofco.de/cache-api-101/">#</a></h2>
<p>Although the cache API was created with the Service Worker, it can be used in the main document as well.</p>
<p>The <code>caches</code> object is available to the <code>window</code>. We can check if it exists in the current browser before using it.</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token string">'caches'</span> <span class="token keyword">in</span> window<span class="token punctuation">)</span> <span class="token punctuation">{</span><br> caches<span class="token punctuation">.</span><span class="token function">open</span><span class="token punctuation">(</span><span class="token string">"my-cache"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">myCache</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> <span class="token comment">// Do stuff</span><br> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre>
<p>In our service worker file, we can use the same cache API to intercept fetch requests and return cached responses if they exist.</p>
<div class="code-heading">/service-worker.js</div>
<pre class="language-js" tabindex="0"><code class="language-js">self<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'fetch'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">e</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> e<span class="token punctuation">.</span><span class="token function">respondWith</span><span class="token punctuation">(</span><br><br> <span class="token comment">// Check if item exists in cache</span><br> caches<span class="token punctuation">.</span><span class="token function">match</span><span class="token punctuation">(</span>e<span class="token punctuation">.</span>request<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">cachedResponse</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br><br> <span class="token comment">// If found in cache, return cached response</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span>cachedResponse<span class="token punctuation">)</span> <span class="token keyword">return</span> cachedResponse<span class="token punctuation">;</span><br><br> <span class="token comment">// If not found, fetch over network</span><br> <span class="token keyword">return</span> <span class="token function">fetch</span><span class="token punctuation">(</span>e<span class="token punctuation">.</span>request<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
The visibility property isn’t just about visibility2018-12-12T00:00:00Zhttps://bitsofco.de/the-visibility-property-isnt-just-about-visibility/<style>
.example-one {
width: 100px;
display: inline-block;
background-color: #ffdb3a;
}
</style>
<p>I recently ran into an issue where the text content of a <code><button></code> I created wasn’t in the accessibility tree and therefore wasn’t visible to screen readers. After some debugging, I realised that the reason was because I was animating the text visually by changing the opacity and, more importantly the <code>visbility</code> property.</p>
<p>It was definitely an oversight, as I know and have previously written about the different effects of <a href="https://bitsofco.de/hiding-elements-with-css">different ways to hide elements with CSS</a>. But this made me think about the behaviour of the <code>visibility</code> property, particularly the <code>hidden</code> value, as it relates to the different kinds of “visibility” - visual, spatial, assistive technology, and interaction. When we hear the world “visibility”, we tend to only think of vision. As we will see, the visibility property isn’t just about (visual) visibility.</p>
<h2 id="visibility-hidden-and-visual-visibility" tabindex="-1"><code>visibility: hidden</code> and visual visibility <a class="header-anchor" href="https://bitsofco.de/the-visibility-property-isnt-just-about-visibility/">#</a></h2>
<p>When visual visibility is concerned, the <code>visibility: hidden</code> rule has the effect we would expect. The element becomes, more or less, “invisible”.</p>
<pre class="language-html" tabindex="0"><code class="language-html">It's gone! ---> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">visibility</span><span class="token punctuation">:</span> hidden<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span>I'm gone!<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></code></pre>
<p>It's gone! ---> <span style="visibility: hidden;">I'm gone!</span></p>
<p>To understand exactly what is happening, let’s revisit the browser’s rendering pipeline, a.k.a. the <a href="https://bitsofco.de/understanding-the-critical-rendering-path">critical rendering path</a>. Typically, once the page’s styles have been determined, three things happen:</p>
<ol>
<li>Layout - what space does the element take up on the page?</li>
<li>Paint - what goes in each pixel?</li>
<li>Composite - in what order is each pixel drawn?</li>
</ol>
<p>For an element with the <code>visibility: hidden</code> rule, it would make sense for browsers to optimise by not taking the paint and composite steps, since there are no pixels drawn in the place where the element would be. Although this isn’t set in the specification that browsers must make this optimisation, it helps me understand how this rule works by thinking of it in this way.</p>
<h2 id="visibility-hidden-and-space" tabindex="-1"><code>visibility: hidden</code> and space <a class="header-anchor" href="https://bitsofco.de/the-visibility-property-isnt-just-about-visibility/">#</a></h2>
<p>Although it is technically true to say that an element is invisible when the <code>visibility: hidden</code> rule is applied to it, it can be argued that it isn’t truly invisible because it still occupies space on the page.</p>
<p>As I mentioned, an element with <code>visibility: hidden</code> may not have to go through the paint and composite steps in the rendering pipeline. However, it will go through the layout stage. This means that, although the pixels aren’t painted, they still occupy space on the page. It’s sort of like Harry Potter and his invisibility cloak - you couldn’t see him, but he was still there.</p>
<p>Let’s take these three <code><div></code> elements, each with a width of 100px, with the middle <code><div></code> having the <code>visibility: hidden</code> rule.</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>style</span><span class="token punctuation">></span></span><span class="token style"><span class="token language-css"><br> <span class="token selector">div</span> <span class="token punctuation">{</span><br> <span class="token property">width</span><span class="token punctuation">:</span> 100px<span class="token punctuation">;</span><br> <span class="token property">display</span><span class="token punctuation">:</span> inline-block<span class="token punctuation">;</span><br> <span class="token property">background-color</span><span class="token punctuation">:</span> #ffdb3a<span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>style</span><span class="token punctuation">></span></span><br><br><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span>One<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">visibility</span><span class="token punctuation">:</span> hidden<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span>Two<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span>Three<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></code></pre>
<div class="example-one">One</div>
<div class="example-one" style="visibility: hidden;">Two</div>
<div class="example-one">Three</div>
<p> </p>
<p>Although the middle <code><div></code> is technically “visually hidden”, it can be argued that it isn’t truly invisible due to the fact that it still occupies space.</p>
<h2 id="visibility-hidden-and-interactivity" tabindex="-1"><code>visibility: hidden</code> and interactivity <a class="header-anchor" href="https://bitsofco.de/the-visibility-property-isnt-just-about-visibility/">#</a></h2>
<p>Because an element with the <code>visibility: hidden</code> rule will still occupy physical space on the page, it may seem as if all this rule does is visually hide the element in a similar way to setting <code>opacity</code> to <code>0</code>. On the contrary, the <code>visibility: hidden</code> rule is actually much more similar to <code>display: none</code>.</p>
<p>Any interactive elements, such as forms or links will also lose their ability to be interacted with. For example, take the <code><button></code> below that should pop up an alert when clicked.</p>
<pre class="language-html" tabindex="0"><code class="language-html">It's gone! ---><br><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">visibility</span><span class="token punctuation">:</span> hidden<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span></span><br> <span class="token special-attr"><span class="token attr-name">onclick</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value javascript language-javascript"><span class="token function">alert</span><span class="token punctuation">(</span><span class="token string">'Hello!'</span><span class="token punctuation">)</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span>I'm gone!<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span><br><--- You can't touch it!</code></pre>
<p>It's gone! --->
<button style="visibility: hidden;" onclick="alert('Hello!')">I'm gone!</button>
<--- You can't touch it!</p>
<p>Although the button has physical space, it can’t be interacted with in any way.</p>
<h2 id="visibility-hidden-and-assistive-technology" tabindex="-1"><code>visibility: hidden</code> and assistive technology <a class="header-anchor" href="https://bitsofco.de/the-visibility-property-isnt-just-about-visibility/">#</a></h2>
<p>The main reason the <code>visibility: hidden</code> rule isn’t just about visual visibility is because it affects the elements visibility to assistive technology as well. When we apply <code>visibility: hidden</code> to an element, it also removes it from the accessibility tree, which makes it invisible to technologies like screen readers.</p>
<p>Take, for example, the issue I came across. I have a <code><button></code> element, with a nested <code><span></code> .</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">visibility</span><span class="token punctuation">:</span> hidden</span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span>Button label here<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></code></pre>
<p>If we look at the accessibility properties of the element, we will see that the <code><button></code> appears to have no text content within it and therefore no accessible name.</p>
<button>
<span style="visibility: hidden">Button label here</span>
</button>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/VXN0UIpdTS-1914.avif 1914w"><source type="image/webp" srcset="https://bitsofco.de/img/VXN0UIpdTS-1914.webp 1914w"><img alt="Screenshot of Chrome DevTools inspecting the above button with no accessible name" loading="lazy" decoding="async" src="https://bitsofco.de/img/VXN0UIpdTS-1914.png" width="1914" height="720"></picture></p>
<p>Although the visibility property may seem to only affect visual visibility, we can see that it does much more than that.</p>
When do the :hover, :focus, and :active pseudo-classes apply?2018-12-11T00:00:00Zhttps://bitsofco.de/when-do-the-hover-focus-and-active-pseudo-classes-apply/<style>
#one:hover { background-color: #ffdb3a; }
#two:focus { background-color: #ffdb3a; }
#three:active { background-color: #ffdb3a; }
#four:hover:focus:active {
background-color: #ffdb3a;
}
#five:active {
background-color: green;
}
#five:focus {
background-color: blue;
}
#five:hover {
background-color: red;
}
#six:hover {
background-color: green;
}
#six:focus {
background-color: blue;
}
#six:active {
background-color: red;
}
</style>
<p>When we select an element by its <code>.class</code> or <code>#id</code>, we’re using predefined and unchanging attributes that are baked into the DOM. With pseudo-classes, we can select elements using information that isn’t already in the DOM and can change based on how a user interacts with the page.</p>
<p><code>:hover</code>, <code>:focus</code>, and <code>:active</code> are pseudo-classes that are determined by a user’s actions. They each correspond to a very specific point in how a user will interact with an element on a page such as a link or a button or an input field.</p>
<p>With the range in input devices we have today, these pseudo-classes also behave slightly differently depending on if the user is interacting with the element with a mouse 🐭, keyboard ⌨️, or touchscreen 📱, which can make it difficult to know how and when to style these situations.</p>
<h2 id="when-hover-is-applied" tabindex="-1">When <code>:hover</code> is applied <a class="header-anchor" href="https://bitsofco.de/when-do-the-hover-focus-and-active-pseudo-classes-apply/">#</a></h2>
<p>The <code>:hover</code> pseudo-class, also called the “pointer hover pseudo-class”, applies when a pointing device interacts with an element without necessarily activating it.</p>
<p>A typical example of this is when a mouse 🐭 hovers over an element. If you hover your mouse over the button below, you’ll see that it turns yellow.</p>
<pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">button:hover</span> <span class="token punctuation">{</span> <span class="token property">background-color</span><span class="token punctuation">:</span> #ffdb3a<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre>
<p><button id="one">If you hover over me, I’ll turn yellow</button></p>
<p>When on a mobile device 📱, there’s basically only one interaction you can take with any interactive element, which is tapping/clicking it. You may notice that if you tap on the button above, it also changes the colour despite the fact that you aren’t “just” hovering over it. This is because, on mobile devices, the events that trigger these pseudo-classes can become conflated. We’ll see with <code>:focus</code> and <code>:active</code> pseudo-classes as well.</p>
<p>Finally, for users of keyboard devices ⌨️, the <code>:hover</code> pseudo-class is never triggered. The keyboard is not considered a “pointer” device and so can’t apply to this pointer hover pseudo-class.</p>
<h2 id="when-focus-is-applied" tabindex="-1">When <code>:focus</code> is applied <a class="header-anchor" href="https://bitsofco.de/when-do-the-hover-focus-and-active-pseudo-classes-apply/">#</a></h2>
<p>The <code>:focus</code> pseudo-class applies when an element is in a state that is ready to be interacted with, i.e. it has the focus of the input device. When this applies differs quite greatly between the different input devices.</p>
<p>When using a mouse 🐭 or similar pointing device, the <code>:focus</code> pseudo-class will apply once the user has begun activating the element and, importantly, it will continue to stay in focus until the user activates another element. For example, if you click the button below with a mouse, you’ll notice that it turns yellow once you begin the click interaction. The button will only return to its default state if you click somewhere else on the page.</p>
<pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">button:focus</span> <span class="token punctuation">{</span> <span class="token property">background-color</span><span class="token punctuation">:</span> #ffdb3a<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre>
<p><button id="two">Click me!</button></p>
<p>For keyboard users ⌨️, focusing on an element is very similar to hovering over it for mouse users. I like to think of the <code>:focus</code> pseudo-class as a hover state for keyboard devices, because it signals a similar intention for interaction.</p>
<p>For touchscreen users 📱, the <code>:focus</code> pseudo-class applies, again, when the user taps on the element. However, it should be noted that this doesn’t apply for the mobile Safari browser.</p>
<h2 id="when-active-is-applied" tabindex="-1">When <code>:active</code> is applied <a class="header-anchor" href="https://bitsofco.de/when-do-the-hover-focus-and-active-pseudo-classes-apply/">#</a></h2>
<p>Finally, the <code>:active</code> pseudo-class applies during the period in which the element is being activated. For example, if using a mouse 🐭, it would be the time between when the mouse button is clicked and when it is released.</p>
<p>If you click the button below quickly, you may not notice the brief change in colour, but if you press and maintain, you’ll see when the pseudo-class is applied.</p>
<pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">button:active</span> <span class="token punctuation">{</span> <span class="token property">background-color</span><span class="token punctuation">:</span> #ffdb3a<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre>
<p><button id="three">Click me!</button></p>
<p>The <code>:active</code> pseudo-class works in the same way for mouse and keyboard ⌨️ interactions.</p>
<p>On mobile devices 📱, like the <code>:focus</code> pseudo-class, the <code>:active</code> pseudo-class applies on the tap of the element. And again, it doesn’t apply at all in the mobile Safari browser.</p>
<h2 id="combining-hover-focus-and-active" tabindex="-1">Combining <code>:hover</code>, <code>:focus</code>, and <code>:active</code> <a class="header-anchor" href="https://bitsofco.de/when-do-the-hover-focus-and-active-pseudo-classes-apply/">#</a></h2>
<p>One thing you may have noticed is that the conditions in which each of these pseudo-classes can be applied are not mutually exclusive. In fact, most times when an element is being clicked with a mouse, all three conditions are valid - the mouse if over the element, the element is in focus, and the element is being activated.</p>
<p>We can test this by only changing the background of the button if all three conditions apply.</p>
<pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">button:hover:focus:active</span> <span class="token punctuation">{</span><br> <span class="token property">background-color</span><span class="token punctuation">:</span> #ffdb3a<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre>
<p>If you click and hold the button below, you’ll see that it turns yellow. But if you click and hold while dragging your mouse away from hovering over it, you’ll see that it loses the colour.</p>
<p><button id="four">Click me, hold, then drag your mouse around</button></p>
<p>This ability to combine the pseudo-classes can be really helpful for fine-tuning how we want to style the different states.</p>
<h3 id="order-of-styles-hover-then-focus-then-active" tabindex="-1">Order of styles - <code>:hover</code> then <code>:focus</code> then <code>:active</code> <a class="header-anchor" href="https://bitsofco.de/when-do-the-hover-focus-and-active-pseudo-classes-apply/">#</a></h3>
<p>Due to the fact that these conditions can and frequently are applied at the same time, the order in which we add these styles is important.</p>
<p>Let’s say we have the following styles:</p>
<pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">button:active</span> <span class="token punctuation">{</span><br> <span class="token property">background-color</span><span class="token punctuation">:</span> green<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token selector">button:focus</span> <span class="token punctuation">{</span><br> <span class="token property">background-color</span><span class="token punctuation">:</span> blue<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token selector">button:hover</span> <span class="token punctuation">{</span><br> <span class="token property">background-color</span><span class="token punctuation">:</span> red<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre>
<p>Only the :hover styles will be visible
<button id="five">Try to get the hover, focus, and active styles</button></p>
<p>When you hover and interact with the element above, only the styles applied to <code>:hover</code> will prevail as long as you are still hovering over it. As we’ve seen, since all three events are applied during a typical click event, the cascade takes over and the last-defined style wins.</p>
<p>This is why it’s important to define these styles in the order in which they typically happen, so it becomes clear to the user when a new interaction is recognised. Typically, a user will first hover over an element, then bring it to focus, then activate it. So, the best way to order your pseudo-class styles are <code>:hover</code> then <code>:focus</code> then <code>:active</code> .</p>
<pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">button:hover</span> <span class="token punctuation">{</span><br> <span class="token property">background-color</span><span class="token punctuation">:</span> green<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token selector">button:focus</span> <span class="token punctuation">{</span><br> <span class="token property">background-color</span><span class="token punctuation">:</span> blue<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token selector">button:active</span> <span class="token punctuation">{</span><br> <span class="token property">background-color</span><span class="token punctuation">:</span> red<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre>
<p>All styles will be visible
<button id="six">Try to get the hover, focus, and active styles</button></p>
<h2 id="recap" tabindex="-1">Recap <a class="header-anchor" href="https://bitsofco.de/when-do-the-hover-focus-and-active-pseudo-classes-apply/">#</a></h2>
<p>The <code>:hover</code>, <code>:focus</code>, and <code>:active</code> pseudo-classes allow us to style elements in response to how a user interacts with it. Depending on the device being used, these pseudo-classes become active at different points (or not at all).</p>
<table>
<thead>
<tr>
<th> </th>
<th><code>:hover</code></th>
<th><code>:focus</code></th>
<th><code>:active</code></th>
</tr>
</thead>
<tbody>
<tr>
<td>Mouse 🐭</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
</tr>
<tr>
<td>Keyboard ⌨️</td>
<td>No</td>
<td>Yes</td>
<td>Yes</td>
</tr>
<tr>
<td>Touchscreen 📱</td>
<td>Yes (on click)</td>
<td>Yes* (on click)</td>
<td>Yes* (on click)</td>
</tr>
</tbody>
</table>
<p><small>* Will not apply on mobile (iOS) Safari</small></p>
What is the Shadow DOM?2018-12-10T00:00:00Zhttps://bitsofco.de/what-is-the-shadow-dom/<p>A couple of weeks ago, I wrote an article on <a href="https://bitsofco.de/what-exactly-is-the-dom">what exactly the DOM is</a>. To recap, the Document Object Model is a representation of an HTML document. It is used by browsers to determine what to render on the page, and by Javascript programs to modify the content, structure, or styling of the page.</p>
<p>For example, let’s take the following HTML document:</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token doctype"><span class="token punctuation"><!</span><span class="token doctype-tag">doctype</span> <span class="token name">html</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>html</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>en<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>head</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>title</span><span class="token punctuation">></span></span>My first web page<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>title</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>head</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>body</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span><span class="token punctuation">></span></span>Hello, world!<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>How are you?<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>body</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>html</span><span class="token punctuation">></span></span></code></pre>
<p>The above HTML document will result in the following DOM tree.</p>
<ul>
<li>
<p>html</p>
<ul>
<li>
<p>head</p>
<ul>
<li>
<p>title</p>
<ul>
<li>My first web page</li>
</ul>
</li>
</ul>
</li>
<li>
<p>body</p>
<ul>
<li>
<p>h1</p>
<ul>
<li>Hello, world!</li>
</ul>
</li>
<li>
<p>p</p>
<ul>
<li>How are you?</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>In the past few years, you may have heard of terms like “Shadow DOM” and “Virtual DOM”. These, although of course related to the original DOM, refer to very different concepts. In this article, I will cover what, exactly, the shadow DOM is and how it differs from the original DOM. In a future article, I’ll do the same for the virtual DOM.</p>
<h2 id="everything-is-global-wait-everything-is-global" tabindex="-1">Everything is global 👍🏾! Wait, everything is global 👎🏾 <a class="header-anchor" href="https://bitsofco.de/what-is-the-shadow-dom/">#</a></h2>
<p>All elements and styles within an HTML document, and therefore the DOM, are in one big global scope. Any element on the page can be accessed by the <code>document.querySelector()</code> method, regardless of how deeply nested it is in the document or where it is placed. Similarly, CSS applied to the document can select any element, regardless of where it is.</p>
<p>This behaviour can be really great when we want to apply styles to the entire document. It’s incredibly useful to be able to select every single element on a page and set, for example, their box-sizing, all in a single line.</p>
<pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">*</span> <span class="token punctuation">{</span> <span class="token property">box-sizing</span><span class="token punctuation">:</span> border-box <span class="token punctuation">}</span></code></pre>
<p>On the other hand, there are times where an element requires complete encapsulation and we don’t want it to be affected by even global styles. A good example of this is third-party widgets, such as the “follow” button for Twitter. Here’s an example of of what that widget looks like:</p>
<p><a href="https://twitter.com/ireaderinokun">Follow @ireaderinokun</a>
<picture><source type="image/avif" srcset="https://bitsofco.de/img/kId0us-kyV-402.avif 402w"><source type="image/webp" srcset="https://bitsofco.de/img/kId0us-kyV-402.webp 402w"><img alt="Twitter follow @ireaderinokun" loading="lazy" decoding="async" src="https://bitsofco.de/img/kId0us-kyV-402.png" width="402" height="108"></picture></p>
<p>Assuming you have Javascript enabled and you inspect the element, you’ll notice that the button is an <code><iframe></code> element, which loads a small document with the styled button you actually see.</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/e2-5YxWK1N-1302.avif 1302w"><source type="image/webp" srcset="https://bitsofco.de/img/e2-5YxWK1N-1302.webp 1302w"><img alt="Follow-button-widget-iframe" loading="lazy" decoding="async" src="https://bitsofco.de/img/e2-5YxWK1N-1302.png" width="1302" height="1072"></picture></p>
<p>This is the only way that Twitter can ensure the intended styling of their widget will remain unaffected by any CSS in the hosting document. Although there are ways to use the cascade to try and achieve the same result, no other method will give the same guarantee that an <code><iframe></code> would, and that’s not ideal.</p>
<p>Shadow DOM was created to allow encapsulation and componentisation natively on the web platform without having to rely on tools like <code><iframe></code>s, which really weren’t made for this purpose.</p>
<h2 id="a-dom-within-a-dom" tabindex="-1">A DOM within a DOM <a class="header-anchor" href="https://bitsofco.de/what-is-the-shadow-dom/">#</a></h2>
<p>You can think of the shadow DOM as a “DOM within a DOM”. It is its own isolated DOM tree with its own elements and styles, completely isolated from the original DOM.</p>
<p>Although only recently specified for use by web authors, the shadow DOM has been used by user agents for years to create and style complex components such as form elements. Let’s take the range input element, for example. To create one on the page, all we have to do is add the following element:</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>range<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></code></pre>
<p>That one element results in the following component:</p>
<p>If we dig deeper, we will see that this one <code><input></code> element is actually made up of several smaller <code><div></code> elements, controlling the track and the slider itself.</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/iHwLe5ulwL-932.avif 932w"><source type="image/webp" srcset="https://bitsofco.de/img/iHwLe5ulwL-932.webp 932w"><img alt="Range input shadow dom" loading="lazy" decoding="async" src="https://bitsofco.de/img/iHwLe5ulwL-932.png" width="932" height="458"></picture></p>
<p>This is achieved using the shadow DOM. The element that is exposed to the host HTML document the simple <code><input></code>, but underneath it there are elements and styles related to the component that do not form part of the DOM’s global scope.</p>
<h2 id="how-the-shadow-dom-works" tabindex="-1">How the shadow DOM works <a class="header-anchor" href="https://bitsofco.de/what-is-the-shadow-dom/">#</a></h2>
<p>To illustrate how the shadow DOM works, let’s recreate the Twitter “follow” button using the shadow DOM instead of an <code><iframe></code>.</p>
<p>First, we start with the <strong>shadow host</strong>. This is the regular HTML element within the original DOM that we want to attach the new shadow DOM to. For a component like the Follow button, it could also contain the fallback element that we would want displayed if Javascript was not enabled on the page or shadow DOM wasn't supported.</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>shadow-host<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://twitter.com/ireaderinokun<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br> Follow @ireaderinokun<br> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></code></pre>
<p>Note that we didn’t just use the <code><a></code> element as the shadow host, because certain elements, primarily interactive elements, can’t be shadow hosts.</p>
<p>To attach a shadow DOM to our host, we use the <code>attachShadow()</code> method.</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> shadowEl <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">".shadow-host"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token keyword">const</span> shadow <span class="token operator">=</span> shadowEl<span class="token punctuation">.</span><span class="token function">attachShadow</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token literal-property property">mode</span><span class="token operator">:</span> <span class="token string">'open'</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>This will create an empty <strong>shadow root</strong> as a child of our shadow host. The shadow root is the start of a new shadow DOM in the way that the <code><html></code> element is the start of the original DOM. We can see our shadow root in the devtools inspector by the <strong>#shadow-root</strong>.</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/vnDsikIGaj-1036.avif 1036w"><source type="image/webp" srcset="https://bitsofco.de/img/vnDsikIGaj-1036.webp 1036w"><img alt="Empty shadow root" loading="lazy" decoding="async" src="https://bitsofco.de/img/vnDsikIGaj-1036.png" width="1036" height="198"></picture></p>
<p>Although the regular HTML children are viewable in the inspector, they are no longer visible on the page as the shadow root takes over.</p>
<p>Next, we want to create the content to form our new <strong>shadow tree</strong>. The shadow tree is like a DOM tree, but for a shadow DOM instead of a regular DOM. To create our follow button, all we need is a new <code><a></code> element, which will be almost exactly the same as the fallback link we already have, but with an icon.</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> link <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">createElement</span><span class="token punctuation">(</span><span class="token string">"a"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br>link<span class="token punctuation">.</span>href <span class="token operator">=</span> shadowEl<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">"a"</span><span class="token punctuation">)</span><span class="token punctuation">.</span>href<span class="token punctuation">;</span><br>link<span class="token punctuation">.</span>innerHTML <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string"><br> <span aria-label="Twitter icon"></span><br> </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>shadowEl<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">"a"</span><span class="token punctuation">)</span><span class="token punctuation">.</span>textContent<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"><br></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span></code></pre>
<p>We add this new element to our shadow DOM the same way we add any element as a child to another, with the <code>appendChild()</code> method.</p>
<pre class="language-js" tabindex="0"><code class="language-js">shadow<span class="token punctuation">.</span><span class="token function">appendChild</span><span class="token punctuation">(</span>link<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>At this point, here's what our element looks like:</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/wBbVnYX5go-704.avif 704w"><source type="image/webp" srcset="https://bitsofco.de/img/wBbVnYX5go-704.webp 704w"><img alt="Plain text of "Follow-@ireaderinokun"" loading="lazy" decoding="async" src="https://bitsofco.de/img/wBbVnYX5go-704.png" width="704" height="146"></picture></p>
<p>Finally, we can add some styles by creating a <code><style></code> element and appending that to the shadow root too.</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> styles <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">createElement</span><span class="token punctuation">(</span><span class="token string">"style"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br>styles<span class="token punctuation">.</span>textContent <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string"><br>a, span {<br> vertical-align: top;<br> display: inline-block;<br> box-sizing: border-box;<br>}<br><br>a {<br> height: 20px;<br> padding: 1px 8px 1px 6px;<br> background-color: #1b95e0;<br> color: #fff;<br> border-radius: 3px;<br> font-weight: 500;<br> font-size: 11px;<br> font-family:'Helvetica Neue', Arial, sans-serif;<br> line-height: 18px;<br> text-decoration: none;<br>}<br><br>a:hover { background-color: #0c7abf; }<br><br>span {<br> position: relative;<br> top: 2px;<br> width: 14px;<br> height: 14px;<br> margin-right: 3px;<br> background: transparent 0 0 no-repeat;<br> background-image: url(data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2072%2072%22%3E%3Cpath%20fill%3D%22none%22%20d%3D%22M0%200h72v72H0z%22%2F%3E%3Cpath%20class%3D%22icon%22%20fill%3D%22%23fff%22%20d%3D%22M68.812%2015.14c-2.348%201.04-4.87%201.744-7.52%202.06%202.704-1.62%204.78-4.186%205.757-7.243-2.53%201.5-5.33%202.592-8.314%203.176C56.35%2010.59%2052.948%209%2049.182%209c-7.23%200-13.092%205.86-13.092%2013.093%200%201.026.118%202.02.338%202.98C25.543%2024.527%2015.9%2019.318%209.44%2011.396c-1.125%201.936-1.77%204.184-1.77%206.58%200%204.543%202.312%208.552%205.824%2010.9-2.146-.07-4.165-.658-5.93-1.64-.002.056-.002.11-.002.163%200%206.345%204.513%2011.638%2010.504%2012.84-1.1.298-2.256.457-3.45.457-.845%200-1.666-.078-2.464-.23%201.667%205.2%206.5%208.985%2012.23%209.09-4.482%203.51-10.13%205.605-16.26%205.605-1.055%200-2.096-.06-3.122-.184%205.794%203.717%2012.676%205.882%2020.067%205.882%2024.083%200%2037.25-19.95%2037.25-37.25%200-.565-.013-1.133-.038-1.693%202.558-1.847%204.778-4.15%206.532-6.774z%22%2F%3E%3C%2Fsvg%3E);<br>}<br></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span><br><br>shadow<span class="token punctuation">.</span><span class="token function">appendChild</span><span class="token punctuation">(</span>styles<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Here's our final element:
<a href="https://twitter.com/ireaderinokun">Follow @ireaderinokun</a> <picture><source type="image/avif" srcset="https://bitsofco.de/img/kId0us-kyV-402.avif 402w"><source type="image/webp" srcset="https://bitsofco.de/img/kId0us-kyV-402.webp 402w"><img alt="Twitter follow @ireaderinokun" loading="lazy" decoding="async" src="https://bitsofco.de/img/kId0us-kyV-402.png" width="402" height="108"></picture></p>
<h2 id="the-dom-vs-the-shadow-dom" tabindex="-1">The DOM vs the shadow DOM <a class="header-anchor" href="https://bitsofco.de/what-is-the-shadow-dom/">#</a></h2>
<p>In some ways, the shadow DOM is a "lite" version of the DOM. Like the DOM, it is a representation of HTML elements, used to determine what to render on the page and enables the modification of the elements. But unlike the DOM, the shadow DOM is not based on a full, standalone document. A shadow DOM, as it's name suggests, is always attached to an element within a regular DOM. Without the DOM, a shadow DOM doesn't exist.</p>
Going Offline First (Video Series)2018-12-05T00:00:00Zhttps://bitsofco.de/going-offline-first/<p>You may have noticed that I recently updated this blog to allow for users like yourself to save an article to view offline. You can do this by clicking the bookmark icon above.</p>
<p>If you are ever offline, you will still be able to view articles previously bookmarked. If you try to visit a page that isn't bookmarked, you'll be redirected to the offline page, which lists all the saved articles.</p>
<h2 id="the-video-series" tabindex="-1">The Video Series <a class="header-anchor" href="https://bitsofco.de/going-offline-first/">#</a></h2>
<p>In order to show how I did this, I created a 5-part video tutorial. You can watch it below or on my <a href="https://www.youtube.com/c/bitsofcode">YouTube channel</a>.</p>
<h3 id="part-1-introduction-and-ghost-theme-setup" tabindex="-1">Part 1 - Introduction & Ghost Theme Setup <a class="header-anchor" href="https://bitsofco.de/going-offline-first/">#</a></h3>
<p><a href="https://www.youtube.com/watch?v=d39LNcggR0I"><img src="https://user-images.githubusercontent.com/8677283/49376942-b98ebc00-f700-11e8-8534-9164c5c655a2.png" alt="Part 1 - Introduction & Ghost Theme Setup"></a></p>
<h3 id="part-2-saving-pages-to-cache-storage" tabindex="-1">Part 2 - Saving Pages to Cache Storage <a class="header-anchor" href="https://bitsofco.de/going-offline-first/">#</a></h3>
<p><a href="https://www.youtube.com/watch?v=pJRdTYZLKvo"><img src="https://user-images.githubusercontent.com/8677283/49376941-b98ebc00-f700-11e8-9279-75f3313bae40.png" alt="Part 2 - Saving Pages to Cache Storage"></a></p>
<h3 id="part-3-creating-an-offline-page" tabindex="-1">Part 3 - Creating an Offline Page <a class="header-anchor" href="https://bitsofco.de/going-offline-first/">#</a></h3>
<p><a href="https://www.youtube.com/watch?v=8F2xEpLDX3Q"><img src="https://user-images.githubusercontent.com/8677283/49376940-b98ebc00-f700-11e8-98c3-bb037f075761.png" alt="Part 3 - Creating an Offline Page"></a></p>
<h3 id="part-4-service-worker-and-serving-offline-pages" tabindex="-1">Part 4 - Service Worker & Serving Offline Pages <a class="header-anchor" href="https://bitsofco.de/going-offline-first/">#</a></h3>
<p><a href="https://www.youtube.com/watch?v=VEjRhRArVR4"><img src="https://user-images.githubusercontent.com/8677283/49376939-b8f62580-f700-11e8-8022-72242a120f70.png" alt="Part 4 - Service Worker & Serving Offline Pages"></a></p>
<h3 id="part-5-handling-assets-while-offline" tabindex="-1">Part 5 - Handling Assets while Offline <a class="header-anchor" href="https://bitsofco.de/going-offline-first/">#</a></h3>
<p><a href="https://www.youtube.com/watch?v=ARTKRkJX7iM"><img src="https://user-images.githubusercontent.com/8677283/49376938-b8f62580-f700-11e8-8730-8e5f6c55f946.png" alt="Part 5 - Handling Assets while Offline"></a></p>
A look at CSS Resets in 20182018-11-30T00:00:00Zhttps://bitsofco.de/a-look-at-css-resets-in-2018/<p>All browsers ship with a set of default styles that are applied to every web page in what is called the “user agent stylesheet”. Most of these stylesheets are open source so you can have a look through them:</p>
<ul>
<li><a href="https://chromium.googlesource.com/chromium/blink/+/master/Source/core/css/html.css">Chromium UA stylesheet</a> - Google Chrome & Opera</li>
<li><a href="https://dxr.mozilla.org/mozilla-central/source/layout/style/res/html.css">Mozilla UA stylesheet</a> - Firefox</li>
<li><a href="https://trac.webkit.org/browser/trunk/Source/WebCore/css/html.css">WebKit UA stylesheet</a> - Safari</li>
</ul>
<p>A lot of styles are consistent across all user agent stylesheets. For example, I used to think that the <code><head></code> element was not visible due to some special feature, but it is actually hidden like any other element on a page, with <code>display: none</code>! You can see this same style in <a href="https://trac.webkit.org/browser/trunk/Source/WebCore/css/html.css#L35">WebKit</a>, <a href="https://chromium.googlesource.com/chromium/blink/+/master/Source/core/css/html.css#32">Chromium</a>, and <a href="https://dxr.mozilla.org/mozilla-central/source/layout/style/res/html.css#720">Mozilla</a>.</p>
<p>A lot of styles, however, <em>are</em> inconsistent between the user agent stylesheets. For example, see how a search input is styled across the browsers.</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/MvV337z3fa-856.avif 856w"><source type="image/webp" srcset="https://bitsofco.de/img/MvV337z3fa-856.webp 856w"><img alt="Search input across browsers" loading="lazy" decoding="async" src="https://bitsofco.de/img/MvV337z3fa-856.png" width="856" height="198"></picture></p>
<h2 id="the-case-for-css-resets" tabindex="-1">The case for CSS resets <a class="header-anchor" href="https://bitsofco.de/a-look-at-css-resets-in-2018/">#</a></h2>
<p>In order to deal with the inconsistencies between user agent stylesheets, CSS resets were born. A CSS reset is a set of styles applied to a page before any other custom styles, with the purpose of creating a more standardised base between browsers.</p>
<p>Eric Meyer wrote an article on <a href="https://meyerweb.com/eric/thoughts/2007/04/18/reset-reasoning/">the reasoning behind CSS resets</a> in 2007, which I think still holds true:</p>
<blockquote>
<p>The basic reason is that all browsers have presentation defaults, but no browsers have the same defaults… We think of our CSS as modifying the default look of a document but with a “reset” style sheet, we can make that default look more consistent across browsers, and thus spend less time fighting with browser defaults.</p>
</blockquote>
<p>Although things are different than they were in 2007, I think resets can still be useful.</p>
<h3 id="aren-t-user-agent-stylesheets-less-inconsistent-nowadays" tabindex="-1">Aren’t user agent stylesheets less inconsistent nowadays? <a class="header-anchor" href="https://bitsofco.de/a-look-at-css-resets-in-2018/">#</a></h3>
<p>Yes, a lot of default styles between user agent stylesheets are now very similar. For example, most browsers apply the same <code>0.67em</code> horizontal margin and <code>2em</code> font size to the <code><h1></code> element. However, this consistency is relatively new and we still need to consider support for the older browsers that have less consistent default styling.</p>
<p>Additionally, depending on the type of reset we want to apply (as we will see below), we may still want a reset even if only the modern, more consistent, browsers were in use.</p>
<h3 id="don-t-we-apply-our-own-styles-to-override-reset-anyway" tabindex="-1">Don’t we apply our own styles to override reset anyway? <a class="header-anchor" href="https://bitsofco.de/a-look-at-css-resets-in-2018/">#</a></h3>
<p>One of the <a href="https://meiert.com/en/blog/reset-style-sheets-are-bad/">major arguments against using CSS resets</a> is that a lot of the styles are eventually overridden by our main stylesheet, which means that the reset styles unnecessarily add to page load time. Although this is definitely true, it isn’t a good enough argument for me to stop using them for two reasons.</p>
<p>The first is that I think resets allow us to write cleaner styles. We can separate my styles that are targeted to specific browsers and my styles for the actual design of the website. Instead of having browser-specific corrections in the same place as my design-specific styles, we can separate them.</p>
<p>Second, resets are (typically) very small and should have a negligible effect on load times. We frequently use the cascade to override and modify element styling, and although there may be some performance gains to never doing this, it may not be worth the time.</p>
<h2 id="types-of-css-resets" tabindex="-1">Types of CSS resets <a class="header-anchor" href="https://bitsofco.de/a-look-at-css-resets-in-2018/">#</a></h2>
<p>There are many, many different CSS resets. Since the first in 2004, <a href="https://tantek.com/log/2004/undohtml.css">undohtml</a>, there has been a plethora of resets ranging in complexity and specificity.</p>
<p>Broadly, there are three goals that a CSS reset tries to achieve. Some reset stylesheets combine these three goals, whereas others only try to solve one.</p>
<ol>
<li>Correct user agent styling errors</li>
<li>Undo any "opinionated" user agent styling</li>
<li>Create a consistent, largely opinionated, style base</li>
</ol>
<h3 id="corrective-resets" tabindex="-1">Corrective resets <a class="header-anchor" href="https://bitsofco.de/a-look-at-css-resets-in-2018/">#</a></h3>
<p>One goal of CSS resets is to correct errors in user agent stylesheets. As you would imagine, this is more for older browsers. A good example of this is when HTML5 was introduced and browsers such as Internet Explorer 9 didn’t apply the correct <code>display</code> type to the new elements.</p>
<p>In resets like normalize.css, these errors are specifically targeted and fixed:</p>
<div class="code-heading">
<a href="https://github.com/csstools/normalize.css/blob/master/normalize.css">Normalize.css</a>
</div>
<pre class="language-css" tabindex="0"><code class="language-css"><span class="token comment">/**<br> * Add the correct display in IE.<br> */</span><br><br><span class="token selector">main</span> <span class="token punctuation">{</span><br> <span class="token property">display</span><span class="token punctuation">:</span> block<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre>
<p>Since these styles only apply to older browsers, we can also use a PostCSS processor such as <a href="https://github.com/csstools/postcss-normalize">PostCSS Normalize</a>, along with browserlist. This allows us to conditionally add these types of styles only if we are supporting that particular browser.</p>
<p>Another example, which applies to some modern browsers, is correcting the <code>display</code> on elements with the <code>hidden</code> attribute.</p>
<div class="code-heading">
<a href="https://purecss.io/base/">Pure CSS</a>
</div>
<pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">[hidden]</span> <span class="token punctuation">{</span><br> <span class="token property">display</span><span class="token punctuation">:</span> none <span class="token important">!important</span><span class="token punctuation">;</span> // One of the good use cases of <span class="token important">!important</span><br><span class="token punctuation">}</span></code></pre>
<h3 id="undoing-opinionated-user-agent-styling" tabindex="-1">Undoing opinionated user agent styling <a class="header-anchor" href="https://bitsofco.de/a-look-at-css-resets-in-2018/">#</a></h3>
<p>Another goal of CSS resets was to undo any opinionated styles from user agents.</p>
<p>There are many opinions for what an opinionated style is. My definition of an opinionated style is anything that isn’t the, for lack of a better word, “bare” version. For example, I would consider adding a <code>2em</code> margin on headings, as most user agent stylesheets do, an opinionated style because why <code>2em</code>? Why not <code>3em</code>? Or <code>4em</code>? An “unopinionated” style in my opinion would just be a margin of <code>0</code> instead. Okay, back to the resets.</p>
<p>A simple version of this is the “Universal Reset”, which removes all margin and padding from all elements:</p>
<pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">*</span> <span class="token punctuation">{</span><br> <span class="token property">margin</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span><br> <span class="token property">padding</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre>
<p>This reset is probably too heavy-handed, as it removes padding from elements you may not expect, such as <code><input></code> elements. An alternative, more commonly used reset is Eric Meyer’s CSS Reset. It returns margin, padding, borders, and font size on specific elements to <code>0</code> or <code>none</code>. And, it doesn't include elements where you would expect a padding.</p>
<div class="code-heading">
<a href="https://meyerweb.com/eric/tools/css/reset/index.html">Meyer's CSS Rest</a>
</div>
<pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">html, body, div, span, applet, object, iframe,<br>h1, h2, h3, h4, h5, h6, p, blockquote, pre,<br>a, abbr, acronym, address, big, cite, code,<br>del, dfn, em, img, ins, kbd, q, s, samp,<br>small, strike, strong, sub, sup, tt, var,<br>b, u, i, center,<br>dl, dt, dd, ol, ul, li,<br>fieldset, form, label, legend,<br>table, caption, tbody, tfoot, thead, tr, th, td,<br>article, aside, canvas, details, embed,<br>figure, figcaption, footer, header, hgroup,<br>menu, nav, output, ruby, section, summary,<br>time, mark, audio, video</span> <span class="token punctuation">{</span><br> <span class="token property">margin</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span><br> <span class="token property">padding</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span><br> <span class="token property">border</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span><br> <span class="token property">font-size</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span><br> <span class="token property">font</span><span class="token punctuation">:</span> inherit<span class="token punctuation">;</span><br> <span class="token property">vertical-align</span><span class="token punctuation">:</span> baseline<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre>
<h3 id="resets-for-consistent-opinionated-styles" tabindex="-1">Resets for consistent, opinionated, styles <a class="header-anchor" href="https://bitsofco.de/a-look-at-css-resets-in-2018/">#</a></h3>
<p>Where the previous category of resets attempted to correct opinionated user agent styles by undoing them entirely, this category of resets does this by setting its own opinionated styles.</p>
<p>For example, instead of setting all font sizes to <code>1em</code> or margins to <code>0</code>, this type of reset will set an opinionated style.</p>
<div class="code-heading">
<a href="https://github.com/csstools/sanitize.css/blob/master/sanitize.css">Sanitize.css</a>
</div>
<pre class="language-css" tabindex="0"><code class="language-css"><span class="token comment">/**<br> * Correct the font size and margin on `h1` elements within `section` and<br> * `article` contexts in Chrome, Firefox, and Safari.<br> */</span><br><br><span class="token selector">h1</span> <span class="token punctuation">{</span><br> <span class="token property">font-size</span><span class="token punctuation">:</span> 2em<span class="token punctuation">;</span><br> <span class="token property">margin</span><span class="token punctuation">:</span> 0.67em 0<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre>
<p><a href="https://getbootstrap.com/docs/4.0/content/reboot">Bootstrap’s Reboot</a> is another example of this. They take things a bit further, even declaring font families and background colours for the <code><body></code> element.</p>
<div class="code-heading">
<a href="https://github.com/twbs/bootstrap/blob/v4-dev/scss/_reboot.scss">Reboot.css</a>
</div>
<pre class="language-sass" tabindex="0"><code class="language-sass"><span class="token comment">// Body</span><br><span class="token comment">//</span><br><span class="token comment">// 1. Remove the margin in all browsers.</span><br><span class="token comment">// 2. As a best practice, apply a default `background-color`.</span><br><span class="token comment">// 3. Set an explicit initial text-align value so that we can later use the</span><br><span class="token comment">// the `inherit` value on things like `<th>` elements.</span><br><br><span class="token selector">body {</span><br><span class="token property-line"> <span class="token property">margin</span><span class="token punctuation">:</span> 0; <span class="token operator">/</span><span class="token operator">/</span> 1</span><br><span class="token property-line"> <span class="token property">font-family</span><span class="token punctuation">:</span> <span class="token variable">$font-family-base</span>;</span><br><span class="token property-line"> <span class="token property">font-size</span><span class="token punctuation">:</span> <span class="token variable">$font-size-base</span>;</span><br><span class="token property-line"> <span class="token property">font-weight</span><span class="token punctuation">:</span> <span class="token variable">$font-weight-base</span>;</span><br><span class="token property-line"> <span class="token property">line-height</span><span class="token punctuation">:</span> <span class="token variable">$line-height-base</span>;</span><br><span class="token property-line"> <span class="token property">color</span><span class="token punctuation">:</span> <span class="token variable">$body-color</span>;</span><br><span class="token property-line"> <span class="token property">text-align</span><span class="token punctuation">:</span> left; <span class="token operator">/</span><span class="token operator">/</span> 3</span><br><span class="token property-line"> <span class="token property">background-color</span><span class="token punctuation">:</span> <span class="token variable">$body-bg</span>; <span class="token operator">/</span><span class="token operator">/</span> 2</span><br><span class="token selector">}</span></code></pre>
<h2 id="how-are-we-using-css-resets-today" tabindex="-1">How are we using CSS resets today? <a class="header-anchor" href="https://bitsofco.de/a-look-at-css-resets-in-2018/">#</a></h2>
<p>I recently ran a poll on Twitter asking people what CSS resets they currently use.</p>
<blockquote>
<p>Thinking of writing about CSS resets and how they're used today. Do you use a CSS reset/normaliser? Which one?
— Ire Aderinokun (@ireaderinokun) <a href="https://twitter.com/ireaderinokun/status/1066985519425486848?ref_src=twsrc%5Etfw">November 26, 2018</a></p>
</blockquote>
<p>Most people use resets like Normalize.css, which is a combination of correcting browser errors and setting consistent, opinionated, styles. A good amount of people reported their own customised resets, which generally fell into the third category as well.</p>
<h3 id="my-opinion" tabindex="-1">My opinion <a class="header-anchor" href="https://bitsofco.de/a-look-at-css-resets-in-2018/">#</a></h3>
<p>I have always used a slightly-modified version of Eric Meyer’s CSS reset for two reasons.</p>
<p>The first is that I generally prefer to work with as blank a slate as possible, adding styles as I see fit. This is just a personal preference for the way I work.</p>
<p>The second is that I’m a strong believer in <a href="https://csswizardry.com/2012/06/single-direction-margin-declarations/">only applying margins in one direction</a> (usually down) and most opinionated resets, like most user agent stylesheets, don’t follow this rule. Therefore, I would rather start with zero margin and padding anywhere, and add it as I go.</p>
<p>Nowadays, I’ve been tweaking my reset file a lot more, even adding utilities that I want in every project I work on, such as a <code>.sr-only</code> class. This has made me think that it isn’t really a reset anymore and is more of a “base”. With new resets coming out like Reboot, it seems like this is the general theme of where things are going.</p>
<p>I'm really interested to hear what everyone uses. Do leave a comment with what you think about resets today!</p>
How and when to use the tabindex attribute2018-11-29T00:00:00Zhttps://bitsofco.de/how-and-when-to-use-the-tabindex-attribute/<p><code>tabindex</code> is a global attribute that can be applied to most HTML elements with content. It controls two things:</p>
<ol>
<li><strong>If an element is focusable</strong>, either by an input method such as the keyboard, or programatically such as with the <code>focus()</code> method; and</li>
<li><strong>At what point an element becomes focusable</strong> when a user is interacting with the page via a keyboard</li>
</ol>
<h2 id="focus-order" tabindex="-1">Focus order <a class="header-anchor" href="https://bitsofco.de/how-and-when-to-use-the-tabindex-attribute/">#</a></h2>
<p>To understand how the <code>tabindex</code> attribute works, we first need to understand how focus is controlled on a page.</p>
<p>There are six <sup><a href="https://bitsofco.de/how-and-when-to-use-the-tabindex-attribute/">1</a></sup> HTML elements which, by default, are focusable:</p>
<ul>
<li><code><a></code> elements with an <code>href</code> attribute</li>
<li><code><link></code> elements with an <code>href</code> attribute</li>
<li><code><button></code> elements</li>
<li><code><input></code> elements that are not <code>type="hidden"</code></li>
<li><code><select></code> elements</li>
<li><code><textarea></code> elements</li>
</ul>
<p>By default, only these elements can be brought into focus either with a user tabbing around the page with a keyboard or via the <code>focus()</code> method in Javascript.</p>
<pre class="language-js" tabindex="0"><code class="language-js">document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">"a"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">focus</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Without any intervention, calling <code>focus()</code> on any other element simply will not work.</p>
<pre class="language-js" tabindex="0"><code class="language-js">document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">"div"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">focus</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Will not work</p>
<p>The order in which these elements are placed in the source HTML document controls the order in which they become focusable when navigating with a keyboard.</p>
<p>Although this default behaviour covers most of the interaction we would need, there may be cases where we want to remove, add, or re-arrange items in the focus order. This is where the <code>tabindex</code> attribute becomes handy.</p>
<h2 id="how-to-use-tabindex" tabindex="-1">How to use <code>tabindex</code> <a class="header-anchor" href="https://bitsofco.de/how-and-when-to-use-the-tabindex-attribute/">#</a></h2>
<p>The <code>tabindex</code> attribute can be applied to almost any element, whether it is by default focusable or not. The value of the attribute must be a valid integer and can either be negative, positive, or exactly zero.</p>
<h3 id="negative-tabindex-values" tabindex="-1">Negative <code>tabindex</code> values <a class="header-anchor" href="https://bitsofco.de/how-and-when-to-use-the-tabindex-attribute/">#</a></h3>
<p>A negative <code>tabindex</code>, for example <code>-1</code>, will omit the element from the focus order entirely. A user navigating the page using a keyboard will not be able to access the element at all.</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>button<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Click me to start, then press the "tab" key<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>button<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>I’ll be in focus first<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>button<span class="token punctuation">"</span></span> <span class="token attr-name">tabindex</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>-1<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>I won’t be in focus :(<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>button<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>I’ll be in focus last<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></code></pre>
<p><button type="button">Click me to start, then press the "tab" key</button>
<button type="button">I’ll be in focus first</button>
<button type="button" tabindex="-1">I won’t be in focus :(</button>
<button type="button">I’ll be in focus last</button></p>
<p>The exact negative value chosen doesn’t actually matter. Since any negative value will remove the element from focus, there is no difference if the value is <code>-1</code> or <code>-99999</code>. Therefore, for readability and consistency, it is best to stick with <code>-1</code>.</p>
<h3 id="a-tabindex-value-of-zero" tabindex="-1">A <code>tabindex</code> value of zero <a class="header-anchor" href="https://bitsofco.de/how-and-when-to-use-the-tabindex-attribute/">#</a></h3>
<p>A <code>tabindex</code> equals to exactly zero will place the element in the default focus order, determined by its position in the source HTML. It can be applied to elements that are not typically focusable, and will add them to the natural focus order as if they were.</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>button<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Click me to start, then press the "tab" key<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>button<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>I’ll be in focus first<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">tabindex</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>I’m a DIV and will be in focus second<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>button<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>I’ll be in focus last<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></code></pre>
<p><button type="button">Click me to start, then press the "tab" key</button>
<button type="button">I’ll be in focus first</button></p>
<div tabindex="0">I’m a DIV and will be in focus second</div>
<button type="button">I’ll be in focus last</button>
<h3 id="positive-tabindex-values" tabindex="-1">Positive <code>tabindex</code> values <a class="header-anchor" href="https://bitsofco.de/how-and-when-to-use-the-tabindex-attribute/">#</a></h3>
<p>Finally, a positive <code>tabindex</code> will place the element in the focus order, but it’s position will be determined by the specific number, starting from 1 and going up. Elements with a positive <code>tabindex</code> will also be placed in front of elements that don’t have a <code>tabindex</code> attribute.</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>button<span class="token punctuation">"</span></span> <span class="token attr-name">tabindex</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>1<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>I’m the first focusable item on the page<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>button<span class="token punctuation">"</span></span> <span class="token attr-name">tabindex</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>500<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>I’m the second<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></code></pre>
<p>To test this out, click on the URL bar of your browser, then hit tab. You'll see that the first two focusable items on this page are the buttons below, even though they are in the middle of the source HTML.</p>
<p><button type="button" tabindex="1">I’m the first focusable item on the page</button>
<button type="button" tabindex="500">I’m the second</button></p>
<h2 id="programmatic-focus-and-tabindex" tabindex="-1">Programmatic focus and <code>tabindex</code> <a class="header-anchor" href="https://bitsofco.de/how-and-when-to-use-the-tabindex-attribute/">#</a></h2>
<p>Besides controlling which elements can be focusable via the keyboard and focus order, the <code>tabindex</code> attribute also controls which elements can be focusable via Javascript.</p>
<p>Adding the <code>tabindex</code> attribute to an element, regardless of the value, makes it focusable via Javascript. This means that we can make elements that are previously unfocusable to be focusable via Javascript, without also making them able to be focused via the user tabbing around using their keyboard.</p>
<p>Let’s take this <code><div></code>, for example, with a negative <code>tabindex</code>.</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">tabindex</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>-1<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>I'm a div<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></code></pre>
<p>If we were to navigate using the keyboard, we will see that it is not able to grab focus.</p>
<p><button>Click me to start, then press the "tab" key</button></p>
<div tabindex="-1">I'm a div</div>
<button>I’ll be in focus first & last</button>
<p>However, we can bring focus to it using the <code>focus()</code> method.</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>button<span class="token punctuation">"</span></span><br> <span class="token special-attr"><span class="token attr-name">onclick</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value javascript language-javascript">document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span><span class="token string">'demo-div'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">focus</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span><br> Click me to focus the DIV<br><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>demo-div<span class="token punctuation">"</span></span> <span class="token attr-name">tabindex</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>-1<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>I'm a div<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></code></pre>
<p><button type="button" onclick="document.getElementById('demo-div').focus();">
Click me to focus the DIV
</button></p>
<div id="demo-div" tabindex="-1">I'm a div</div>
<p>Next, we’ll see how this difference between tab and programmatic focus makes the <code>tabindex</code> attribute useful.</p>
<h2 id="when-to-use-tabindex" tabindex="-1">When to use <code>tabindex</code> <a class="header-anchor" href="https://bitsofco.de/how-and-when-to-use-the-tabindex-attribute/">#</a></h2>
<p>The <code>tabindex</code> attribute can be very useful, but has potentially destructive consequences if not used correctly. Each category of <code>tabindex</code> value should be used in different circumstances.</p>
<h3 id="when-to-use-a-negative-tabindex-value" tabindex="-1">When to use a negative <code>tabindex</code> value <a class="header-anchor" href="https://bitsofco.de/how-and-when-to-use-the-tabindex-attribute/">#</a></h3>
<p>As we have covered, a negative <code>tabindex</code> value will remove the element from tab focus, but can add elements to programmatic focus.</p>
<p>A great example of when this is useful is modals. Modal containers are typically unfocusable elements like <code><div></code> or <code><section></code>. When a modal window is open, we may want to move focus to it so that screen readers will read out its content. But, we don’t want the modal container itself to be able to receive tab focus. This can be achieved using a negative <code>tabindex</code>.</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>modal<span class="token punctuation">"</span></span> <span class="token attr-name">tabindex</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>-1<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br> <span class="token comment"><!-- Modal content --></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span><br><br><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><br><span class="token keyword">function</span> <span class="token function">openModal</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span><span class="token string">"modal"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">focus</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token comment">// Other stuff to show modal</span><br><span class="token punctuation">}</span><br></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></code></pre>
<h3 id="when-to-use-a-tabindex-value-of-zero" tabindex="-1">When to use a <code>tabindex</code> value of zero <a class="header-anchor" href="https://bitsofco.de/how-and-when-to-use-the-tabindex-attribute/">#</a></h3>
<p>A <code>tabindex</code> of zero is typically used either to add focus back to elements that it was programatically removed from.</p>
<p>Another good use case for this value is for custom elements. For example, we may need to create a custom button element, which will not be focusable by default because it's not actually a <code><button></code>. We can add this functionality back with the <code>tabindex</code> attribute, and specify its focus order as any regular <code>button</code> element would have.</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>my-custom-button</span> <span class="token attr-name">tabindex</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Click me!<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>my-custom-button</span><span class="token punctuation">></span></span></code></pre>
<h3 id="when-to-use-a-positive-tabindex-value" tabindex="-1">When to use a positive <code>tabindex</code> value <a class="header-anchor" href="https://bitsofco.de/how-and-when-to-use-the-tabindex-attribute/">#</a></h3>
<p>There is almost no reason to ever use a positive value to <code>tabindex</code>, and it is actually considered an anti-pattern. If you’re finding the need to use this value to change the order in which elements become focusable, it is likely that what you actually need to do is change the source order of the HTML elements.</p>
<p>#1 - I excluded the <code>menuitem</code> element as it’s no longer valid HTML</p>
Everything about CSS environment variables2018-11-28T00:00:00Zhttps://bitsofco.de/css-environment-variables/<p>When the iPhone X came out with the infamous notch, you may have heard of the new <code>safe-area-inset</code> values, which allowed browsers to detect the area of the screen that was covered by the notch and move content around appropriately.</p>
<p>Recently, these values have been formalised as part of a new specification for <a href="https://drafts.csswg.org/css-env-1/">CSS Environment Variables</a>. This specification is still in the stage of Editor’s Draft which means that, although some browsers may begin implementation, it is not yet an official part of the specification. However, it does have good support with browsers that need it, e.g. iOS Safari on the iPhone X, and can be used in a progressive way.</p>
<h2 id="we-already-have-css-variables-don-t-we" tabindex="-1">We already have CSS variables, don’t we? <a class="header-anchor" href="https://bitsofco.de/css-environment-variables/">#</a></h2>
<p><a href="https://www.w3.org/TR/css-variables-1/">CSS Custom Properties</a>, better known as CSS Variables, officially became a W3C recommendation in 2015, making it safe for web page authors to use (with appropriate fallbacks for non-supporting browsers).</p>
<p>For the first time in CSS, these variables allowed us to create references to frequently-used values and use those references around our stylesheet instead. The references, or “variables”, were scoped to an element and created in a similar way to how we define normal property-value declarations.</p>
<pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">button</span> <span class="token punctuation">{</span><br> <span class="token property">--padding</span><span class="token punctuation">:</span> 10px<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre>
<p>These variables could then be accessed using the <code>var()</code> notation.</p>
<pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">button</span> <span class="token punctuation">{</span><br> <span class="token property">padding-top</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--padding<span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre>
<p>Although these type of variables solved a lot of problems, they also had a few limitations.</p>
<p>Because CSS variables are scoped to an element, <strong>they are only accessible to that specific element, not globally</strong>. In order to make variables accessible to our whole stylesheet, we would have to apply the variables to the <code>:root</code> element. Since these variables are cascading, they will be available to all child elements.</p>
<pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">:root</span> <span class="token punctuation">{</span><br> <span class="token property">--colour-primary</span><span class="token punctuation">:</span> #bae<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token selector">button</span> <span class="token punctuation">{</span><br> <span class="token property">background-color</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--colour-primary<span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token selector">h1</span> <span class="token punctuation">{</span><br> <span class="token property">color</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--colour-primary<span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre>
<p>Another side-effect of the variables being scoped to an element is that <strong>they can’t be used outside CSS declarations</strong>. CSS variables can’t be used in other places such as with <code>@media</code> rules.</p>
<pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">:root</span> <span class="token punctuation">{</span><br> <span class="token property">--breakpoint</span><span class="token punctuation">:</span> 600px<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token atrule"><span class="token rule">@media</span> <span class="token punctuation">(</span><span class="token property">min-width</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--breakpoint<span class="token punctuation">)</span><span class="token punctuation">)</span></span> <span class="token punctuation">{</span><br> <span class="token comment">/* Doesn't work :( */</span><br><span class="token punctuation">}</span></code></pre>
<p>Will not work</p>
<p>Finally, because CSS variables are cascading, <strong>the browser needs to be prepared for their value to change at any moment</strong>. This means that, even if they aren’t being changed, the browser will not be able to perform any optimisations to store the value in a permanent way.</p>
<h2 id="enter-environment-variables" tabindex="-1">Enter environment variables <a class="header-anchor" href="https://bitsofco.de/css-environment-variables/">#</a></h2>
<p>CSS environment variables were created as a solution to the constraints of the custom properties, also called “cascading variables”. They have three key differences to the cascading variables:</p>
<ol>
<li>They are defined once, globally, and can never be changed</li>
<li>They can be used as any value to any property or <code>@</code> rule, e.g. media queries</li>
<li>They can be defined by either the user agent or the stylesheet author</li>
</ol>
<h2 id="how-environment-variables-are-created" tabindex="-1">How environment variables are created <a class="header-anchor" href="https://bitsofco.de/css-environment-variables/">#</a></h2>
<p>CSS environment variables can either be created by the user-agent, or by the author of the stylesheet.</p>
<h3 id="user-agent-environment-variables" tabindex="-1">User-agent environment variables <a class="header-anchor" href="https://bitsofco.de/css-environment-variables/">#</a></h3>
<p>User agents, e.g. browsers, will be able to set their own environment variables. Currently, the only ones that exist are the safe area inset variables.</p>
<p>The safe area insets are four length variables, which create a rectangle on the screen, outlining the area that is safe for content to be placed within.</p>
<table>
<thead>
<tr>
<th>Variable Name</th>
<th>Value type</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>safe-area-inset-top</code></td>
<td>length</td>
</tr>
<tr>
<td><code>safe-area-inset-right</code></td>
<td>length</td>
</tr>
<tr>
<td><code>safe-area-inset-bottom</code></td>
<td>length</td>
</tr>
<tr>
<td><code>safe-area-inset-left</code></td>
<td>length</td>
</tr>
</tbody>
</table>
<h3 id="author-environment-variables" tabindex="-1">Author environment variables <a class="header-anchor" href="https://bitsofco.de/css-environment-variables/">#</a></h3>
<p>Although the specification says that stylesheet authors, i.e. web developers, will be able to create environment variables, how this will be done has not been decided on yet.</p>
<h2 id="using-environment-variables-today" tabindex="-1">Using environment variables (today) <a class="header-anchor" href="https://bitsofco.de/css-environment-variables/">#</a></h2>
<p>A CSS environment variable is accessed using the <code>env()</code> notation. This notation accepts two values - the variable name, and a fallback value for if the variable wasn’t found.</p>
<pre><code>env('VARIABLE_NME', FALLBACK_VALUE);
</code></pre>
<p>For example, we can use the safe area inset variables to apply padding to the <code>body</code> element and ensure all content is within a safe area.</p>
<pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">body</span> <span class="token punctuation">{</span><br> <span class="token property">padding-top</span><span class="token punctuation">:</span> <span class="token function">env</span><span class="token punctuation">(</span>safe-area-inset-top<span class="token punctuation">,</span> 12px<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token property">padding-right</span><span class="token punctuation">:</span> <span class="token function">env</span><span class="token punctuation">(</span>safe-area-inset-right<span class="token punctuation">,</span> 12px<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token property">padding-bottom</span><span class="token punctuation">:</span> <span class="token function">env</span><span class="token punctuation">(</span>safe-area-inset-bottom<span class="token punctuation">,</span> 12px<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token property">padding-left</span><span class="token punctuation">:</span> <span class="token function">env</span><span class="token punctuation">(</span>safe-area-inset-left<span class="token punctuation">,</span> 12px<span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre>
<h3 id="support-and-progressive-enhancement" tabindex="-1">Support and progressive enhancement <a class="header-anchor" href="https://bitsofco.de/css-environment-variables/">#</a></h3>
<p>As I mentioned, the specification for CSS environment variables is still in Editor’s Draft, which means things will probably change. Despite this, the support for this feature is already at 70% and therefore can be used.</p>
<p class="ciu_embed" data-feature="css-env-function" data-periods="future_1,current,past_1,past_2" data-accessible-colours="false">
<picture>
<source type="image/webp" srcset="https://caniuse.bitsofco.de/image/css-env-function.webp">
<source type="image/png" srcset="https://caniuse.bitsofco.de/image/css-env-function.png">
<img src="https://caniuse.bitsofco.de/image/css-env-function.jpg" alt="Data on support for the css-env-function feature across the major browsers from caniuse.com">
</picture>
</p>
<p>A good way to use this feature today is to rely on the cascade and use appropriate fallbacks.</p>
<pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">body</span> <span class="token punctuation">{</span><br> <span class="token comment">/* No variables */</span><br> <span class="token property">padding-top</span><span class="token punctuation">:</span> 12px<span class="token punctuation">;</span><br><br> <span class="token comment">/* iOS Safari 11.2, Safari 11 */</span><br> <span class="token property">padding-top</span><span class="token punctuation">:</span> <span class="token function">constant</span><span class="token punctuation">(</span>safe-area-inset-top<span class="token punctuation">,</span> 12px<span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token comment">/* iOS Safari 11.4+, Safari 11.1+, Chrome 69+, Opera 56+ */</span><br> <span class="token property">padding-top</span><span class="token punctuation">:</span> <span class="token function">env</span><span class="token punctuation">(</span>safe-area-inset-top<span class="token punctuation">,</span> 12px<span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre>
Git aliases for lazy developers2018-11-27T00:00:00Zhttps://bitsofco.de/git-aliases-for-lazy-developers/<p>I prefer to interface with git via the command line, at least for the simple commands. However, I’m a bit lazy, and don’t like having to repeatedly type out the same long commands multiple times a day. Over the years, I have created some short aliases that allow me to more quickly use git via the command line.</p>
<h2 id="how-to-create-an-alias" tabindex="-1">How to create an alias <a class="header-anchor" href="https://bitsofco.de/git-aliases-for-lazy-developers/">#</a></h2>
<p>Bash aliases allow us to set keywords that can trigger longer commands. They are defined in a <code>.bash_profile</code> or <code>. bashrc</code>, typically in the home directory for the user on the machine. For example, my <code>.bash_profile</code> lives in <code>Macintosh HD > Users > ireaderinokun > .bash_profile</code> .</p>
<p>A bash alias looks like this:</p>
<pre class="language-bash" tabindex="0"><code class="language-bash"><span class="token builtin class-name">alias</span> <span class="token assign-left variable">ALIAS_NAME</span><span class="token operator">=</span><span class="token string">"ALIAS_COMMAND"</span></code></pre>
<p>The name can be whatever we want, as long as it isn’t already mapped to some other command. The command is what we want the alias to map to. For example, an alias to open up this website in Chrome could look like this:</p>
<pre class="language-bash" tabindex="0"><code class="language-bash"><span class="token builtin class-name">alias</span> <span class="token assign-left variable">bitsofcode</span><span class="token operator">=</span><span class="token string">"chrome https://bitsofco.de"</span></code></pre>
<p>Then, in a new terminal window, if I type in <code>bitsofcode</code>, the URL will open in Chrome.</p>
<h2 id="lazy-git-aliases" tabindex="-1">Lazy git aliases <a class="header-anchor" href="https://bitsofco.de/git-aliases-for-lazy-developers/">#</a></h2>
<p>Now onto the aliases.</p>
<h3 id="commits" tabindex="-1">Commits <a class="header-anchor" href="https://bitsofco.de/git-aliases-for-lazy-developers/">#</a></h3>
<p>The first alias I ever created was to shorten the process of adding all the unstaged changes and committing them with a particular message.</p>
<pre class="language-bash" tabindex="0"><code class="language-bash"><span class="token builtin class-name">alias</span> <span class="token assign-left variable">gac</span><span class="token operator">=</span><span class="token string">"git add . && git commit -m"</span> <span class="token comment"># + commit message</span></code></pre>
<p>To use this, I would type out the alias, plus the commit message I wanted.</p>
<pre class="language-bash" tabindex="0"><code class="language-bash">gac <span class="token string">"My commit message"</span></code></pre>
<h3 id="initialising" tabindex="-1">Initialising <a class="header-anchor" href="https://bitsofco.de/git-aliases-for-lazy-developers/">#</a></h3>
<p>Another useful alias is initialising a repo with the <code>"Initial commit"</code> first commit message.</p>
<pre class="language-bash" tabindex="0"><code class="language-bash"><span class="token builtin class-name">alias</span> <span class="token assign-left variable">gi</span><span class="token operator">=</span><span class="token string">"git init && gac 'Initial commit'"</span></code></pre>
<p>As you can see here, we can use other aliases within the commands for new aliases.</p>
<h3 id="pushing-and-pulling" tabindex="-1">Pushing & pulling <a class="header-anchor" href="https://bitsofco.de/git-aliases-for-lazy-developers/">#</a></h3>
<p>Pushing to remotes and pulling from remotes is another common command.</p>
<pre class="language-bash" tabindex="0"><code class="language-bash"><span class="token builtin class-name">alias</span> <span class="token assign-left variable">gp</span><span class="token operator">=</span><span class="token string">"git push"</span> <span class="token comment"># + remote & branch names</span><br><span class="token builtin class-name">alias</span> <span class="token assign-left variable">gl</span><span class="token operator">=</span><span class="token string">"git pull"</span> <span class="token comment"># + remote & branch names</span></code></pre>
<p>To get even lazier, I created aliases for pushing from and pulling to the common <code>origin</code> remote and/or the common <code>master</code> branch.</p>
<pre class="language-bash" tabindex="0"><code class="language-bash"><span class="token comment"># Pushing/pulling to origin remote</span><br><span class="token builtin class-name">alias</span> <span class="token assign-left variable">gpo</span><span class="token operator">=</span><span class="token string">"git push origin"</span> <span class="token comment"># + branch name</span><br><span class="token builtin class-name">alias</span> <span class="token assign-left variable">glo</span><span class="token operator">=</span><span class="token string">"git pull origin"</span> <span class="token comment"># + branch name</span><br><br><span class="token comment"># Pushing/pulling to origin remote, master branch</span><br><span class="token builtin class-name">alias</span> <span class="token assign-left variable">gpom</span><span class="token operator">=</span><span class="token string">"git push origin master"</span><br><span class="token builtin class-name">alias</span> <span class="token assign-left variable">glom</span><span class="token operator">=</span><span class="token string">"git pull origin master"</span></code></pre>
<h3 id="branches" tabindex="-1">Branches <a class="header-anchor" href="https://bitsofco.de/git-aliases-for-lazy-developers/">#</a></h3>
<p>Finally, I have a few aliases for working with branches. To create create a new branch or checkout into an existing branch, I have the following aliases:</p>
<pre class="language-bash" tabindex="0"><code class="language-bash"><span class="token builtin class-name">alias</span> <span class="token assign-left variable">gb</span><span class="token operator">=</span><span class="token string">"git branch"</span> <span class="token comment"># + branch name</span><br><span class="token builtin class-name">alias</span> <span class="token assign-left variable">gc</span><span class="token operator">=</span><span class="token string">"git checkout"</span> <span class="token comment"># + branch name</span></code></pre>
<p>To create a new branch <em>and</em> checkout into it, I have the following alias:</p>
<pre class="language-bash" tabindex="0"><code class="language-bash"><span class="token builtin class-name">alias</span> <span class="token assign-left variable">gcb</span><span class="token operator">=</span><span class="token string">"git checkout -b"</span> <span class="token comment"># + branch name</span></code></pre>
<p>You can view my <code>.bash_profile</code> on my Github <a href="https://github.com/ireade/dotfiles">dotfiles repository</a>. What aliases do you have?</p>
What, exactly, is the DOM?2018-11-26T00:00:00Zhttps://bitsofco.de/what-exactly-is-the-dom/<p>The Document Object Model, or the “DOM”, is an interface to web pages. It is essentially an API to the page, allowing programs to read and manipulate the page’s content, structure, and styles. Let’s break this down.</p>
<h2 id="how-is-a-web-page-built" tabindex="-1">How is a web page built? <a class="header-anchor" href="https://bitsofco.de/what-exactly-is-the-dom/">#</a></h2>
<p>How a browser goes from a source HTML document to displaying a styled and interactive page in the viewport is called the “Critical Rendering Path”. Although this process can be broken down into several steps, as I cover in my article on <a href="https://bitsofco.de/understanding-the-critical-rendering-path">Understanding the Critical Rendering Path</a>, these steps can be roughly grouped into two stages. The first stage involves the browser parsing the document to determine what will ultimately be rendered on the page, and the second stage involves the browser performing the render.</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/_nbMsSP-5v-828.avif 828w"><source type="image/webp" srcset="https://bitsofco.de/img/_nbMsSP-5v-828.webp 828w"><img alt="HTML-to-Render-Tree-to-Final" loading="lazy" decoding="async" src="https://bitsofco.de/img/_nbMsSP-5v-828.png" width="828" height="442"></picture></p>
<p>The result of the first stage is what is called a “render tree”. The render tree is a representation of the HTML elements that will be rendered on the page and their related styles. In order to build this tree, the browser needs two things:</p>
<ol>
<li>The CSSOM, a representation of the styles associated with elements</li>
<li>The DOM, a representation of the elements</li>
</ol>
<h2 id="how-is-the-dom-created-and-what-does-it-look-like" tabindex="-1">How is the DOM created (and what does it look like)? <a class="header-anchor" href="https://bitsofco.de/what-exactly-is-the-dom/">#</a></h2>
<p>The DOM is an object-based representation of the source HTML document. It has some differences, as we will see below, but it is essentially an attempt to convert the structure and content of the HTML document into an object model that can be used by various programs.</p>
<p>The object structure of the DOM is represented by what is called a “node tree”. It is so called because it can be thought of as a tree with a single parent stem that branches out into several child branches, each which may have leaves. In this case, the parent “stem” is the root <code><html></code> element, the child “branches” are the nested elements, and the “leaves” are the content within the elements.</p>
<p>Let’s take this HTML document as an example:</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token doctype"><span class="token punctuation"><!</span><span class="token doctype-tag">doctype</span> <span class="token name">html</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>html</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>en<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>head</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>title</span><span class="token punctuation">></span></span>My first web page<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>title</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>head</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>body</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span><span class="token punctuation">></span></span>Hello, world!<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>How are you?<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>body</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>html</span><span class="token punctuation">></span></span></code></pre>
<p>This document can be represented as the following node tree:</p>
<ul>
<li>
<p>html</p>
<ul>
<li>
<p>head</p>
<ul>
<li>
<p>title</p>
<ul>
<li>My first web page</li>
</ul>
</li>
</ul>
</li>
<li>
<p>body</p>
<ul>
<li>
<p>h1</p>
<ul>
<li>Hello, world!</li>
</ul>
</li>
<li>
<p>p</p>
<ul>
<li>How are you?</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 id="what-the-dom-is-not" tabindex="-1">What the DOM is <em>not</em> <a class="header-anchor" href="https://bitsofco.de/what-exactly-is-the-dom/">#</a></h2>
<p>In the example I gave above, it seems like the DOM is a 1-to-1 mapping of the source HTML document or what you see your DevTools. However, as I mentioned, there are differences. In order to fully understand what the DOM <em>is</em>, we need to look at what it is <em>not</em>.</p>
<h3 id="the-dom-is-not-your-source-html" tabindex="-1">The DOM is <em>not</em> your source HTML <a class="header-anchor" href="https://bitsofco.de/what-exactly-is-the-dom/">#</a></h3>
<p>Although the DOM is created from the source HTML document, it is not always exactly the same. There are two instances in which the DOM can be different from the source HTML.</p>
<h4 id="1-when-the-html-is-not-valid" tabindex="-1">1. When the HTML is not valid <a class="header-anchor" href="https://bitsofco.de/what-exactly-is-the-dom/">#</a></h4>
<p>The DOM is an interface for <strong>valid</strong> HTML documents. During the process of creating the DOM, the browser may correct some invalidities in the HTML code.</p>
<p>Let’s take this HTML document for example:</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token doctype"><span class="token punctuation"><!</span><span class="token doctype-tag">doctype</span> <span class="token name">html</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>html</span><span class="token punctuation">></span></span><br>Hello, world!<br><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>html</span><span class="token punctuation">></span></span></code></pre>
<p>The document is missing a <code><head></code> and <code><body></code> element, which is a requirement for valid HTML. If we look at the resulting DOM tree, we will see that this has been corrected:</p>
<ul>
<li>
<p>html</p>
<ul>
<li>
<p>head</p>
</li>
<li>
<p>body</p>
<ul>
<li>Hello, world!</li>
</ul>
</li>
</ul>
</li>
</ul>
<h4 id="2-when-the-dom-is-modified-by-javascript" tabindex="-1">2. When the DOM is modified by Javascript <a class="header-anchor" href="https://bitsofco.de/what-exactly-is-the-dom/">#</a></h4>
<p>Besides being an interface to viewing the content of an HTML document, the DOM can also be modified, making it a living resource.</p>
<p>We can, for example, create additional nodes to the DOM using Javascript.</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">var</span> newParagraph <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">createElement</span><span class="token punctuation">(</span><span class="token string">"p"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token keyword">var</span> paragraphContent <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">createTextNode</span><span class="token punctuation">(</span><span class="token string">"I'm new!"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br>newParagraph<span class="token punctuation">.</span><span class="token function">appendChild</span><span class="token punctuation">(</span>paragraphContent<span class="token punctuation">)</span><span class="token punctuation">;</span><br>document<span class="token punctuation">.</span>body<span class="token punctuation">.</span><span class="token function">appendChild</span><span class="token punctuation">(</span>newParagraph<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>This will update the DOM, but of course not our HTML document.</p>
<h3 id="the-dom-is-not-what-you-see-in-the-browser-i-e-the-render-tree" tabindex="-1">The DOM is <em>not</em> what you see in the browser (i.e., the render tree) <a class="header-anchor" href="https://bitsofco.de/what-exactly-is-the-dom/">#</a></h3>
<p>What you see in the browser viewport is the render tree which, as I mentioned, is a combination of the DOM and the CSSOM. What really separates the DOM from the render tree, is that the latter only consists of what will eventually be painted on the screen.</p>
<p>Because the render tree is only concerned with what is rendered, it excludes elements that are visually hidden. For example, elements that have <code>display: none</code> styles associated to them.</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token doctype"><span class="token punctuation"><!</span><span class="token doctype-tag">doctype</span> <span class="token name">html</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>html</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>en<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>head</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>head</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>body</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span><span class="token punctuation">></span></span>Hello, world!<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">display</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span>How are you?<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>body</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>html</span><span class="token punctuation">></span></span></code></pre>
<p>The DOM will include the <code><p></code> element:</p>
<ul>
<li>
<p>html</p>
<ul>
<li>
<p>head</p>
</li>
<li>
<p>body</p>
<ul>
<li>
<p>h1</p>
<ul>
<li>Hello, world!</li>
</ul>
</li>
<li>
<p>p</p>
<ul>
<li>How are you?</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>However, the render tree, and therefore what is seen in the viewport, will not include that element.</p>
<ul>
<li>
<p>html</p>
<ul>
<li>
<p>body</p>
<ul>
<li>
<p>h1</p>
<ul>
<li>Hello, world!</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="the-dom-is-not-what-is-in-devtools" tabindex="-1">The DOM is <em>not</em> what is in DevTools <a class="header-anchor" href="https://bitsofco.de/what-exactly-is-the-dom/">#</a></h3>
<p>This difference is a bit more minuscule because the DevTools element inspector provides the closest approximation to the DOM that we have in the browser. However, the DevTools inspector includes additional information that isn’t in the DOM.</p>
<p>The best example of this is CSS pseudo-elements. Pseudo-elements created using the <code>::before</code> and <code>::after</code> selectors form part of the CSSOM and render tree, but are not technically part of the DOM. This is because the DOM is built from the source HTML document alone, not including the styles applied to the element.</p>
<p>Despite the fact that pseudo-elements are not part of the DOM, they are in our devtools element inspector.</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/WKdXRnQ2pN-1522.avif 1522w"><source type="image/webp" srcset="https://bitsofco.de/img/WKdXRnQ2pN-1522.webp 1522w"><img alt="Pseudo-element-in-devtools-inspector" loading="lazy" decoding="async" src="https://bitsofco.de/img/WKdXRnQ2pN-1522.png" width="1522" height="976"></picture></p>
<p>This is why pseudo-elements cannot be targetted by Javascript, because they are not part of the DOM.</p>
<h2 id="recap" tabindex="-1">Recap <a class="header-anchor" href="https://bitsofco.de/what-exactly-is-the-dom/">#</a></h2>
<p>The DOM is an interface to an HTML document. It is used by browsers as a first step towards determining what to render in the viewport, and by Javascript programs to modify the content, structure, or styling of the page.</p>
<p>Although similar to other forms of the source HTML document, the DOM is different in a number of ways:</p>
<ul>
<li>It is always valid HTML</li>
<li>It is a living model that can be modifed by Javascript</li>
<li>It doesn't include pseudo-elements (e.g. <code>::after</code>)</li>
<li>It does include hidden elements (e.g. with <code>display: none</code>)</li>
</ul>
How to upload a screenshot from Puppeteer to Cloudinary2018-11-23T00:00:00Zhttps://bitsofco.de/how-to-upload-a-screenshot-from-puppeteer-to-cloudinary/<p>In <a href="https://bitsofco.de/using-a-headless-browser-to-capture-page-screenshots">my article yesterday</a>, I wrote about the problem I was trying to solve with my <a href="https://caniuse.bitsofco.de">caniuse embed</a>. To recap, I was trying to create a fallback image for the live embed and I did this by taking a screenshot of the page using puppeteer.</p>
<p>Yesterday, I covered how to use headless Chrome, via Puppeteer, to capture that screenshot. The next thing I needed to do was upload the screenshot to my <a href="https://cloudinary.com/?ap=irebc">Cloudinary</a> (affiliate link) account in order to have a hosted image that could be referenced.</p>
<p>This is quite a specific problem I had to solve, but I couldn’t find any examples or documentation of how to do it, so I thought it would be useful to share the process.</p>
<h2 id="save-the-screenshot-as-a-buffer" tabindex="-1">Save the screenshot as a buffer <a class="header-anchor" href="https://bitsofco.de/how-to-upload-a-screenshot-from-puppeteer-to-cloudinary/">#</a></h2>
<p>Using Puppeteer, we can capture a screenshot and save it to disk using the <code>page.screenshot()</code> method. However, in my use case, I didn’t need to save the image to the file system. Instead, I wanted to capture it as a buffer, which could then be uploaded to Cloudinary.</p>
<p>The <code>page.screenshot()</code> method returns a buffer, so all we need to do is save it.</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> screenshotBuffer <span class="token operator">=</span> <span class="token keyword">await</span> page<span class="token punctuation">.</span><span class="token function">screenshot</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br> <span class="token literal-property property">encoding</span><span class="token operator">:</span> <span class="token string">'binary'</span><span class="token punctuation">,</span><br> <span class="token comment">// other options...</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br><span class="token comment">// Do something with screenshotBuffer</span></code></pre>
<h2 id="upload-the-stream-to-cloudinary" tabindex="-1">Upload the stream to Cloudinary <a class="header-anchor" href="https://bitsofco.de/how-to-upload-a-screenshot-from-puppeteer-to-cloudinary/">#</a></h2>
<p>Next, we need to upload the buffer to Cloudinary. The <a href="https://github.com/cloudinary/cloudinary_npm">Cloudinary node module</a> has a specific method for uploading images as streams, the <code>upload_stream</code> method.</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> cloudinary <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'cloudinary'</span><span class="token punctuation">)</span><span class="token punctuation">.</span>v2<span class="token punctuation">;</span> <span class="token comment">// Make sure to use v2</span><br><br>cloudinary<span class="token punctuation">.</span><span class="token function">config</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br> <span class="token literal-property property">cloud_name</span><span class="token operator">:</span> <span class="token constant">YOUR_CLOUDINARY_CLOUD_NAME</span><span class="token punctuation">,</span><br> <span class="token literal-property property">api_key</span><span class="token operator">:</span> <span class="token constant">YOUR_CLOUDINARY_API_KEY</span><span class="token punctuation">,</span><br> <span class="token literal-property property">api_secret</span><span class="token operator">:</span> <span class="token constant">YOUR_CLOUDINARY_API_SECRET</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br>cloudinary<span class="token punctuation">.</span>uploader<span class="token punctuation">.</span><span class="token function">upload_stream</span><span class="token punctuation">(</span><br> uploadOptions<span class="token punctuation">,</span><br> <span class="token punctuation">(</span><span class="token parameter">error<span class="token punctuation">,</span> result</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token comment">/* Do something with result! */</span> <span class="token punctuation">}</span><br><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">end</span><span class="token punctuation">(</span>screenshotBuffer<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>If successful, the <code>result</code> will be an object with information about the image, such as the <code>url</code>!</p>
<h2 id="putting-it-all-together-in-an-api" tabindex="-1">Putting it all together in an API <a class="header-anchor" href="https://bitsofco.de/how-to-upload-a-screenshot-from-puppeteer-to-cloudinary/">#</a></h2>
<p>Since we are working with private keys and API secrets, all of this should ideally be done server-side. I created a simple express application to handle everything from taking the screenshot to uploading the image, then returning the resulting image data from Cloudinary.</p>
<p>You can view the full API on my GitHub repository, <a href="https://github.com/ireade/caniuse-embed-screenshot-api">caniuse-embed-screenshot-api</a>, but here's a stripped-down version of the basic functionality:</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token comment">/* main.js */</span><br><span class="token keyword">const</span> express <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">"express"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token keyword">const</span> app <span class="token operator">=</span> <span class="token function">express</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br><span class="token keyword">const</span> puppeteer <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'puppeteer'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token keyword">const</span> cloudinary <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'cloudinary'</span><span class="token punctuation">)</span><span class="token punctuation">.</span>v2<span class="token punctuation">;</span><br>cloudinary<span class="token punctuation">.</span><span class="token function">config</span><span class="token punctuation">(</span><span class="token comment">/* config here */</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br><span class="token comment">// Create an express route</span><br>app<span class="token punctuation">.</span><span class="token function">post</span><span class="token punctuation">(</span><span class="token string">"/upload"</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">req<span class="token punctuation">,</span> res</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> <span class="token function">takeScreenshot</span><span class="token punctuation">(</span><span class="token punctuation">)</span><br> <span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">screenshot</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token function">uploadScreenshot</span><span class="token punctuation">(</span>screenshot<span class="token punctuation">)</span><span class="token punctuation">)</span><br> <span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">result</span><span class="token punctuation">)</span> <span class="token operator">=></span> res<span class="token punctuation">.</span><span class="token function">status</span><span class="token punctuation">(</span><span class="token number">200</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">json</span><span class="token punctuation">(</span>result<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br><span class="token comment">// See https://bitsofco.de/using-a-headless-browser-to-capture-page-screenshots</span><br><span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">takeScreenshot</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">const</span> browser <span class="token operator">=</span> <span class="token keyword">await</span> puppeteer<span class="token punctuation">.</span><span class="token function">launch</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br> <span class="token literal-property property">defaultViewport</span><span class="token operator">:</span> <span class="token punctuation">{</span><br> <span class="token literal-property property">width</span><span class="token operator">:</span> <span class="token number">800</span><span class="token punctuation">,</span><br> <span class="token literal-property property">height</span><span class="token operator">:</span> <span class="token number">800</span><span class="token punctuation">,</span><br> <span class="token literal-property property">isLandscape</span><span class="token operator">:</span> <span class="token boolean">true</span><br> <span class="token punctuation">}</span><br> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token keyword">const</span> page <span class="token operator">=</span> <span class="token keyword">await</span> browser<span class="token punctuation">.</span><span class="token function">newPage</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token keyword">await</span> page<span class="token punctuation">.</span><span class="token function">goto</span><span class="token punctuation">(</span><br> embedUrl<span class="token punctuation">,</span><br> <span class="token punctuation">{</span> <span class="token literal-property property">waitUntil</span><span class="token operator">:</span> <span class="token string">'networkidle2'</span> <span class="token punctuation">}</span><br> <span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token keyword">const</span> screenshot <span class="token operator">=</span> <span class="token keyword">await</span> page<span class="token punctuation">.</span><span class="token function">screenshot</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br> <span class="token literal-property property">omitBackground</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span><br> <span class="token literal-property property">encoding</span><span class="token operator">:</span> <span class="token string">'binary'</span><br> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token keyword">await</span> browser<span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token keyword">return</span> screenshot<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token keyword">function</span> <span class="token function">uploadScreenshot</span><span class="token punctuation">(</span><span class="token parameter">screenshot</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">Promise</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">resolve<span class="token punctuation">,</span> reject</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> <span class="token keyword">const</span> uploadOptions <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span><br> cloudinary<span class="token punctuation">.</span>uploader<span class="token punctuation">.</span><span class="token function">upload_stream</span><span class="token punctuation">(</span>uploadOptions<span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">error<span class="token punctuation">,</span> result</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span>error<span class="token punctuation">)</span> <span class="token function">reject</span><span class="token punctuation">(</span>error<span class="token punctuation">)</span><br> <span class="token keyword">else</span> <span class="token function">resolve</span><span class="token punctuation">(</span>result<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">end</span><span class="token punctuation">(</span>screenshot<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre>
Using a headless browser to capture page screenshots2018-11-22T00:00:00Zhttps://bitsofco.de/using-a-headless-browser-to-capture-page-screenshots/<p>A headless browser is a browser without the graphical user interface. It is a way to navigate the web via the command line.</p>
<p><img src="https://res.cloudinary.com/ireaderinokun/image/upload/v1542872853/headful-vs-headless_n2yodt.jpg" alt="Headful vs Headless Browser"></p>
<p>Headless browsers are useful for automating tests of websites as it allows us to navigate to a page and perform actions without having to manually open up a browser and click around. Another great use of headless browsers is to take screenshots of web pages dynamically.</p>
<h2 id="what-am-i-trying-to-achieve" tabindex="-1">What am I trying to achieve? <a class="header-anchor" href="https://bitsofco.de/using-a-headless-browser-to-capture-page-screenshots/">#</a></h2>
<p>A couple of years ago, I created an embed for caniuse data of any particular feature. Here’s an example of how this looks:</p>
<p class="ciu_embed" data-feature="css-grid" data-periods="future_1,current,past_1,past_2" data-accessible-colours="false">
<picture>
<source type="image/webp" srcset="https://caniuse.bitsofco.de/image/css-grid.webp">
<source type="image/png" srcset="https://caniuse.bitsofco.de/image/css-grid.png">
<img src="https://caniuse.bitsofco.de/image/css-grid.jpg" alt="Data on support for the css-grid feature across the major browsers from caniuse.com">
</picture>
</p>
<p>In case you are interested, I wrote about <a href="https://bitsofco.de/caniuse-embed">how I created the embed</a>. One thing that always bothered me about it is that was dependent on Javascript to display any data. If for whatever reason Javascript was disabled, or even if the network was slow, this is what would be displayed:</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/IWPYEkxGV5-1458.avif 1458w"><source type="image/webp" srcset="https://bitsofco.de/img/IWPYEkxGV5-1458.webp 1458w"><img alt="Plain text that says "Can I Use css-grid? Data on support for the css-grid feature across the major browsers from caniuse.com."" loading="lazy" decoding="async" src="https://bitsofco.de/img/IWPYEkxGV5-1458.png" width="1458" height="376"></picture></p>
<p>This is because, until the script generates the iframe, the embed code looks like this:</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>ciu_embed<span class="token punctuation">"</span></span> <span class="token attr-name">data-feature</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>css-grid<span class="token punctuation">"</span></span> <span class="token attr-name">data-periods</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>future_1,current,past_1,past_2<span class="token punctuation">"</span></span> <span class="token attr-name">data-accessible-colours</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>false<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://caniuse.com/#feat=css-grid<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Can I Use css-grid?<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span><br> Data on support for the css-grid feature across the major browsers from caniuse.com.<br><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span></code></pre>
<p>I had been thinking about how to solve this problem for a while, and finally settled that the best solution would be to programmatically take a screenshot of the embed page using a headless browser and adding that to the fallback code.</p>
<p>In order to achieve this end result, I had to go through a few steps:</p>
<ol>
<li>Generate a screenshot of the embed page</li>
<li>Create an API to handle the image generation and upload the image to Cloudinary</li>
<li>Update the fallback code to include the screenshot</li>
</ol>
<p>In this article, I will cover the first part of this solution, which is using a headless browser to create a screenshot of the page.</p>
<h2 id="headless-chrome" tabindex="-1">Headless Chrome <a class="header-anchor" href="https://bitsofco.de/using-a-headless-browser-to-capture-page-screenshots/">#</a></h2>
<p>Since version 59, headless Chrome has been available via the <code>chrome</code> command (Note: <a href="https://developers.google.com/web/updates/2017/04/headless-chrome">you may need to add an alias to use the command</a>).</p>
<p>To get the DOM contents of a page, for example, we can use the <code>--dump-dom</code> flag.</p>
<pre class="language-bash" tabindex="0"><code class="language-bash">chrome <span class="token parameter variable">--headless</span> --disable-gpu --dump-dom https://bitsofco.de</code></pre>
<p>To take a screenshot, we can use the <code>--screenshot</code> flag instead.</p>
<pre class="language-bash" tabindex="0"><code class="language-bash">chrome <span class="token parameter variable">--headless</span> --disable-gpu <span class="token parameter variable">--screenshot</span> https://bitsofco.de</code></pre>
<h2 id="puppeteer-a-headless-chrome-node-api" tabindex="-1">Puppeteer - A headless Chrome node API <a class="header-anchor" href="https://bitsofco.de/using-a-headless-browser-to-capture-page-screenshots/">#</a></h2>
<p>Puppeteer brings the power of headless chrome to a simple node API, enabling us to use headless chrome almost anywhere.</p>
<p>To take a screenshot using Puppeteer, we have to go through four steps:</p>
<ol>
<li>Launch the browser</li>
<li>Open a new page</li>
<li>Navigate to the chosen URL</li>
<li>Take a screenshot</li>
</ol>
<p>Here is how that looks:</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> puppeteer <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'puppeteer'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br><span class="token punctuation">(</span><span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br><br> <span class="token comment">// 1. Launch the browser</span><br> <span class="token keyword">const</span> browser <span class="token operator">=</span> <span class="token keyword">await</span> puppeteer<span class="token punctuation">.</span><span class="token function">launch</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token comment">// 2. Open a new page</span><br> <span class="token keyword">const</span> page <span class="token operator">=</span> <span class="token keyword">await</span> browser<span class="token punctuation">.</span><span class="token function">newPage</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token comment">// 3. Navigate to URL</span><br> <span class="token keyword">await</span> page<span class="token punctuation">.</span><span class="token function">goto</span><span class="token punctuation">(</span><span class="token string">'https://bitsofco.de'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token comment">// 4. Take screenshot</span><br> <span class="token keyword">await</span> page<span class="token punctuation">.</span><span class="token function">screenshot</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token literal-property property">path</span><span class="token operator">:</span> <span class="token string">'screenshot.png'</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token keyword">await</span> browser<span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>This will take a screenshot of the page, <code>https://bitsofco.de</code>, and save a PNG file to the current directory with the name <code>screenshot.png</code>.</p>
<h2 id="taking-a-screenshot-for-the-embed" tabindex="-1">Taking a screenshot for the embed <a class="header-anchor" href="https://bitsofco.de/using-a-headless-browser-to-capture-page-screenshots/">#</a></h2>
<p>For my use case, I needed to take a screenshot of the embed and save it as binary data, to later be uploaded to <a href="https://cloudinary.com/?ap=irebc">Cloudinary</a> (affiliate link). To do that, I had to make a few modifications to the default example from above.</p>
<h3 id="defining-the-browser-viewport" tabindex="-1">Defining the browser viewport <a class="header-anchor" href="https://bitsofco.de/using-a-headless-browser-to-capture-page-screenshots/">#</a></h3>
<p>By default, Puppeteer will use a 800px by 600px viewport size for the browser. To change this, we can manually set the height and width of the viewport we prefer in options passed to <code>puppeteer.launch()</code>.</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> browser <span class="token operator">=</span> <span class="token keyword">await</span> puppeteer<span class="token punctuation">.</span><span class="token function">launch</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br> <span class="token literal-property property">defaultViewport</span><span class="token operator">:</span> <span class="token punctuation">{</span><br> <span class="token literal-property property">width</span><span class="token operator">:</span> <span class="token number">800</span><span class="token punctuation">,</span><br> <span class="token literal-property property">height</span><span class="token operator">:</span> <span class="token number">500</span><span class="token punctuation">,</span><br> <span class="token literal-property property">isLandscape</span><span class="token operator">:</span> <span class="token boolean">true</span><br> <span class="token punctuation">}</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>We can also set if the viewport is in landscape mode with the <code>isLandscape</code> boolean.</p>
<h3 id="waiting-for-page-load" tabindex="-1">Waiting for page load <a class="header-anchor" href="https://bitsofco.de/using-a-headless-browser-to-capture-page-screenshots/">#</a></h3>
<p>When the embed page is initially loaded, all that exists is some text that lets the user know that the data is being fetched. When we take the screenshot, we need to make sure that the page has fully loaded.</p>
<p>We can specify when puppeteer will take the screenshot with options passed to <code>page.goto()</code>.</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">await</span> page<span class="token punctuation">.</span><span class="token function">goto</span><span class="token punctuation">(</span><br> pageUrl<span class="token punctuation">,</span><br> <span class="token punctuation">{</span> <span class="token literal-property property">waitUntil</span><span class="token operator">:</span> <span class="token string">'networkidle2'</span> <span class="token punctuation">}</span><br><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>The <code>networkidle2</code> value means that puppeteer will consider the page fully loaded when there are no more than 2 network connections for at least 500ms.</p>
<h3 id="removing-the-background" tabindex="-1">Removing the background <a class="header-anchor" href="https://bitsofco.de/using-a-headless-browser-to-capture-page-screenshots/">#</a></h3>
<p>Finally, we only want to capture the embed on the page, omitting any white background. We can do this by passing an option to the <code>page.screenshot()</code> method.</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">await</span> page<span class="token punctuation">.</span><span class="token function">screenshot</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br> <span class="token literal-property property">omitBackground</span><span class="token operator">:</span> <span class="token boolean">true</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>In my article tomorrow, I will cover the next step in solving this problem - How to upload a screenshot from Puppeteer to Cloudinary.</p>
Why and how to use WebP images today2018-11-21T00:00:00Zhttps://bitsofco.de/why-and-how-to-use-webp-images-today/<p><a href="https://developers.google.com/speed/webp/">WebP</a> is an image format developed by Google in 2010. It was created to be an alternative to formats like PNG and JPG, producing much smaller file sizes while maintaining similar image qualities.</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/fbunGfsBDq-1138.avif 1138w"><source type="image/webp" srcset="https://bitsofco.de/img/fbunGfsBDq-1138.webp 1138w"><img alt="Comparison of JPG and WebP" loading="lazy" decoding="async" src="https://bitsofco.de/img/fbunGfsBDq-1138.png" width="1138" height="860"></picture></p>
<h2 id="why-use-webp" tabindex="-1">Why use WebP? <a class="header-anchor" href="https://bitsofco.de/why-and-how-to-use-webp-images-today/">#</a></h2>
<p>WebP is an incredibly useful format because it offers both performance and features. Unlike other formats, WebP supports both lossy and lossless compression, as well as transparency and animation.</p>
<table>
<thead>
<tr>
<th> </th>
<th>WebP</th>
<th>PNG</th>
<th>JPG</th>
<th>GIF</th>
</tr>
</thead>
<tbody>
<tr>
<td>Lossy compression</td>
<td>✓</td>
<td>✓</td>
<td>✓</td>
<td>✗</td>
</tr>
<tr>
<td>Lossless compression</td>
<td>✓</td>
<td>✓</td>
<td>✓</td>
<td>✓</td>
</tr>
<tr>
<td>Transparency</td>
<td>✓</td>
<td>✓</td>
<td>✗</td>
<td>✓</td>
</tr>
<tr>
<td>Animation</td>
<td>✓</td>
<td>✗</td>
<td>✗</td>
<td>✓</td>
</tr>
</tbody>
</table>
<p>Even with these features, WebP provides consistently smaller file sizes than its alternatives. In a <a href="https://developers.google.com/speed/webp/docs/c_study#results">comparative study of these image formats</a>, it was found that WebP lossy images were on average 30% smaller than JPGs and WebP lossless images were on average 25% smaller than PNG.</p>
<h2 id="how-to-covert-images-to-the-webp-format" tabindex="-1">How to covert images to the WebP format <a class="header-anchor" href="https://bitsofco.de/why-and-how-to-use-webp-images-today/">#</a></h2>
<p>There are several tools we can use to convert our JPGs, PNGs, and other file formats to WebP.</p>
<h3 id="online-tools" tabindex="-1">Online tools <a class="header-anchor" href="https://bitsofco.de/why-and-how-to-use-webp-images-today/">#</a></h3>
<ul>
<li><strong><a href="https://squoosh.app/">Squoosh</a></strong> - Online image compression and conversion</li>
<li><strong><a href="https://image.online-convert.com/convert-to-webp">Online-Convert.com</a></strong> - Online image conversion</li>
</ul>
<h3 id="command-line-tools" tabindex="-1">Command line tools <a class="header-anchor" href="https://bitsofco.de/why-and-how-to-use-webp-images-today/">#</a></h3>
<p><strong><a href="https://www.npmjs.com/package/cwebp">cwebp</a></strong> is the most popular command line tool for converting images to the WebP format. Once installed, we can use it to convert images by passing in, among other options, a quality, input file, and output file.</p>
<pre class="language-bash" tabindex="0"><code class="language-bash"><span class="token comment"># cwebp -q [quality] [input_file] -o [output_file]</span><br><br>cwebp <span class="token parameter variable">-q</span> <span class="token number">75</span> image.png <span class="token parameter variable">-o</span> image.webp</code></pre>
<h3 id="node-tools" tabindex="-1">Node tools <a class="header-anchor" href="https://bitsofco.de/why-and-how-to-use-webp-images-today/">#</a></h3>
<p><strong><a href="https://github.com/imagemin/imagemin">imagemin</a></strong>, and it's plugin <strong><a href="https://github.com/imagemin/imagemin-webp">imagemin-webp</a></strong>, is the most popular node library for converting images to the WebP format.</p>
<p>Below is an example script that will convert all PNG and JPG files in the directory to WebP.</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token comment">/* convert-to-webp.js */</span><br><br><span class="token keyword">const</span> imagemin <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">"imagemin"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token keyword">const</span> webp <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">"imagemin-webp"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br><span class="token function">imagemin</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string">"*.png"</span><span class="token punctuation">,</span> <span class="token string">"*.jpg"</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token string">"images"</span><span class="token punctuation">,</span> <span class="token punctuation">{</span><br> <span class="token literal-property property">use</span><span class="token operator">:</span> <span class="token punctuation">[</span><br> <span class="token function">webp</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token literal-property property">quality</span><span class="token operator">:</span> <span class="token number">75</span><span class="token punctuation">}</span><span class="token punctuation">)</span><br> <span class="token punctuation">]</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>We can then use this script via the command line, or via build tool</p>
<pre class="language-bash" tabindex="0"><code class="language-bash"><span class="token function">node</span> convert-to-webp.js</code></pre>
<h3 id="sketch" tabindex="-1">Sketch <a class="header-anchor" href="https://bitsofco.de/why-and-how-to-use-webp-images-today/">#</a></h3>
<p>In Sketch, we can export any slice in the WebP image format.</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/7Hl2opyT2x-470.avif 470w"><source type="image/webp" srcset="https://bitsofco.de/img/7Hl2opyT2x-470.webp 470w"><img alt="Screenshot of SKetch program showing export as WebP" loading="lazy" decoding="async" src="https://bitsofco.de/img/7Hl2opyT2x-470.png" width="470" height="533"></picture></p>
<h2 id="how-to-use-to-webp-on-the-web-today" tabindex="-1">How to use to WebP on the web today <a class="header-anchor" href="https://bitsofco.de/why-and-how-to-use-webp-images-today/">#</a></h2>
<p>At the time of writing, WebP is supported in 72% of browsers globally used.</p>
<p><a href="https://caniuse.com/#feat=webp"><picture><source type="image/avif" srcset="https://bitsofco.de/img/9CTFl69o9I-1290.avif 1290w"><source type="image/webp" srcset="https://bitsofco.de/img/9CTFl69o9I-1290.webp 1290w"><img alt="Can I Use webp? Data on support for the webp feature across the major browsers from caniuse.com." loading="lazy" decoding="async" src="https://bitsofco.de/img/9CTFl69o9I-1290.png" width="1290" height="936"></picture></a></p>
<p>Although this is good enough to make a compelling case to use WebP, it isn’t good enough to rely on the format without providing fallbacks. In browsers that don’t support this file format, the image will appear broken.</p>
<p>We can provide fallbacks to images using the <code><picture></code> element. This HTML5 element allows us to provide multiple sources for a single image.</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>picture</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>source</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>image/webp<span class="token punctuation">"</span></span> <span class="token attr-name">srcset</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>image.webp<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>source</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>image/jpeg<span class="token punctuation">"</span></span> <span class="token attr-name">srcset</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>image.jpg<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>img</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>image.jpg<span class="token punctuation">"</span></span> <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>My Image<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>picture</span><span class="token punctuation">></span></span></code></pre>
<p>To provide an alternate image source, we use the <code><source></code> element within the <code><picture></code> element. The <code><source></code> element has a number of attributes we can use to define the image and when it should be used:</p>
<ul>
<li><code>type</code>: The <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Complete_list_of_MIME_types">MIME type</a> of the source</li>
<li><code>srcset</code>: The path to the image file. Multiple files can be used to provide different image sizes/densities (see <a href="https://bitsofco.de/the-srcset-and-sizes-attributes">Responsive Images - The srcset and sizes Attributes</a>)</li>
<li><code>sizes</code>: A list of sizes of each source file (see article above)</li>
<li><code>media</code>: A media query that will determine when the image is used (see article above)</li>
</ul>
<p>In addition to the various <code><source></code>s, a regular <code><img></code> element must also be provided as a fallback for browsers that do not support multiple file formats via the <code><picture></code> element.</p>
Web workers vs Service workers vs Worklets2018-11-20T00:00:00Zhttps://bitsofco.de/web-workers-vs-service-workers-vs-worklets/<p>Web workers, service workers, and worklets. All of these are what I would call “Javascript Workers”, and although they do have some similarities in how they work, they have very little overlap in what they are used for.</p>
<p>Broadly speaking, a worker is a script that runs on a thread separate to the browser’s main thread. If you think about your typical Javascript file that is included in your HTML document via a <code><script></code> tag, that runs on the main thread. If there is too much activity on the main thread, it can slow down the site, making interactions jittery and unresponsive.</p>
<p>Web workers, service workers, and worklets are all scripts that run on a separate thread. So what are the differences between these three types of workers?</p>
<h2 id="web-workers" tabindex="-1">Web workers <a class="header-anchor" href="https://bitsofco.de/web-workers-vs-service-workers-vs-worklets/">#</a></h2>
<p>Web workers are the most general purpose type of worker. Unlike service workers and worklets as we will see below, they do not have a specific use case, other than the feature of being run separately to the main thread. As a result, web workers can be used to offload pretty much any heavy processing from the main thread.</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/AfltGEBT6F-566.avif 566w"><source type="image/webp" srcset="https://bitsofco.de/img/AfltGEBT6F-566.webp 566w"><img alt="Diagram of web worker as separate to main thread, with postMessage as communication" loading="lazy" decoding="async" src="https://bitsofco.de/img/AfltGEBT6F-566.jpeg" width="566" height="400"></picture></p>
<p>Web workers are created using the <code>Web Workers API</code>. After creating a dedicated Javascript file for our worker, we can add it as a new <code>Worker</code>.</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token comment">/* main.js */</span><br><br><span class="token keyword">const</span> myWorker <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Worker</span><span class="token punctuation">(</span><span class="token string">'worker.js'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>This will start to run whatever code we have in the <code>worker.js</code> file. As I mentioned, this can be almost anything, but web workers are most useful for offloading processes that might take a long time or are run in parallel to other processes. A great example is the image processing web application, <a href="https://squoosh.app">Squoosh</a>, which uses web workers to handle image manipulation tasks, leaving the main thread available for the user to interact with the application without interruption.</p>
<p>Like all workers, web workers do not have access to the DOM, which means that any information needed will have to be passed between the worker and the main script using <code>window.postMessage()</code>.</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token comment">/* main.js */</span><br><br><span class="token comment">// Create worker</span><br><span class="token keyword">const</span> myWorker <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Worker</span><span class="token punctuation">(</span><span class="token string">'worker.js'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br><span class="token comment">// Send message to worker</span><br>myWorker<span class="token punctuation">.</span><span class="token function">postMessage</span><span class="token punctuation">(</span><span class="token string">'Hello!'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br><span class="token comment">// Receive message from worker</span><br>myWorker<span class="token punctuation">.</span><span class="token function-variable function">onmessage</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">e</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>e<span class="token punctuation">.</span>data<span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre>
<p>In our worker script, we can listen for messages from the main script, and return a response.</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token comment">/* worker.js */</span><br><br><span class="token comment">// Receive message from main file</span><br>self<span class="token punctuation">.</span><span class="token function-variable function">onmessage</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">e</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>e<span class="token punctuation">.</span>data<span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token comment">// Send message to main file</span><br> self<span class="token punctuation">.</span><span class="token function">postMessage</span><span class="token punctuation">(</span>workerResult<span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre>
<h2 id="service-workers" tabindex="-1">Service workers <a class="header-anchor" href="https://bitsofco.de/web-workers-vs-service-workers-vs-worklets/">#</a></h2>
<p>Service workers are a type of worker that serve the explicit purpose of being a proxy between the browser and the network and/or cache.</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/lGb0ZjmwwL-772.avif 772w"><source type="image/webp" srcset="https://bitsofco.de/img/lGb0ZjmwwL-772.webp 772w"><img alt="Diagram of service worker as separate to main thread, with postMessage as communication. Service worker also communicated with server and cache." loading="lazy" decoding="async" src="https://bitsofco.de/img/lGb0ZjmwwL-772.jpeg" width="772" height="400"></picture></p>
<p>Like web workers, service workers are registered in the main javascript file, referencing a dedicated service worker file.</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token comment">/* main.js */</span><br><br>navigator<span class="token punctuation">.</span>serviceWorker<span class="token punctuation">.</span><span class="token function">register</span><span class="token punctuation">(</span><span class="token string">'/service-worker.js'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Unlike regular web workers, service workers have some extra features that allow them to fulfil their proxy purpose. Once they are installed and activated, service workers are able to intercept any network requests made from the main document.</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token comment">/* service-worker.js */</span><br><br><span class="token comment">// Install</span><br>self<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'install'</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">event</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token comment">// ...</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br><span class="token comment">// Activate</span><br>self<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'activate'</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">event</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token comment">// ...</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br><span class="token comment">// Listen for network requests from the main document</span><br>self<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'fetch'</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">event</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token comment">// ...</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Once intercepted, a service worker can, for example, respond by returning a document from the cache instead of going to the network, thereby allowing web applications to function offline!</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token comment">/* service-worker.js */</span><br><br>self<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'fetch'</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">event</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token comment">// Return data from cache</span><br> event<span class="token punctuation">.</span><span class="token function">respondWith</span><span class="token punctuation">(</span><br> caches<span class="token punctuation">.</span><span class="token function">match</span><span class="token punctuation">(</span>event<span class="token punctuation">.</span>request<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<h2 id="worklets" tabindex="-1">Worklets <a class="header-anchor" href="https://bitsofco.de/web-workers-vs-service-workers-vs-worklets/">#</a></h2>
<p>Worklets are a very lightweight, highly specific, worker. They enable us as developers to hook into various parts of the browser’s rendering process.</p>
<p>When a web page is being rendered, the browser goes through a number of steps. I cover this in more detail in my article on <a href="https://bitsofco.de/understanding-the-critical-rendering-path">Understanding the Critical Rendering Path</a>, but there are four steps we need to worry about here - Style, Layout, Paint, & Composite.</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/gds7g3Y5ky-1093.avif 1093w"><source type="image/webp" srcset="https://bitsofco.de/img/gds7g3Y5ky-1093.webp 1093w"><img alt="The browser pipeline - Javscript to Style to Layout to Paint to Composite" loading="lazy" decoding="async" src="https://bitsofco.de/img/gds7g3Y5ky-1093.jpeg" width="1093" height="167"></picture></p>
<p>Image credit: <a href="https://developers.google.com/web/fundamentals/performance/rendering/">Rendering Performance, by Paul Lewis</a></p>
<p>Let’s take the Paint stage. This is where the browser applies the styles to each element. The worklet that hooks into this stage of rendering is the <a href="https://www.w3.org/TR/css-paint-api-1/">Paint Worklet</a>.</p>
<p>The Paint Worklet allows us to create custom images that can be applied anywhere CSS expects an image, for example as a value to the <code>background-image</code> property.</p>
<p>To create a worklet, as with all workers, we register it in our main javascript file, referencing the dedicated worklet file.</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token comment">/* main.js */</span><br><br><span class="token constant">CSS</span><span class="token punctuation">.</span>paintWorklet<span class="token punctuation">.</span><span class="token function">addModule</span><span class="token punctuation">(</span><span class="token string">'myWorklet.js'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>In our worklet file, we can create the custom image. The <code>paint</code> method works very similarly to the Canvas API. Here’s an example of a simple black-to-white gradient.</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token comment">/* myWorklet.js */</span><br><br><span class="token function">registerPaint</span><span class="token punctuation">(</span><span class="token string">'myGradient'</span><span class="token punctuation">,</span> <span class="token keyword">class</span> <span class="token punctuation">{</span><br> <span class="token function">paint</span><span class="token punctuation">(</span><span class="token parameter">ctx<span class="token punctuation">,</span> size<span class="token punctuation">,</span> properties</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">var</span> gradient <span class="token operator">=</span> ctx<span class="token punctuation">.</span><span class="token function">createLinearGradient</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> size<span class="token punctuation">.</span>height <span class="token operator">/</span> <span class="token number">3</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> gradient<span class="token punctuation">.</span><span class="token function">addColorStop</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token string">"black"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> gradient<span class="token punctuation">.</span><span class="token function">addColorStop</span><span class="token punctuation">(</span><span class="token number">0.7</span><span class="token punctuation">,</span> <span class="token string">"rgb(210, 210, 210)"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> gradient<span class="token punctuation">.</span><span class="token function">addColorStop</span><span class="token punctuation">(</span><span class="token number">0.8</span><span class="token punctuation">,</span> <span class="token string">"rgb(230, 230, 230)"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> gradient<span class="token punctuation">.</span><span class="token function">addColorStop</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token string">"white"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> ctx<span class="token punctuation">.</span>fillStyle <span class="token operator">=</span> gradient<span class="token punctuation">;</span><br> ctx<span class="token punctuation">.</span><span class="token function">fillRect</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> size<span class="token punctuation">.</span>width<span class="token punctuation">,</span> size<span class="token punctuation">.</span>height <span class="token operator">/</span> <span class="token number">3</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Finally, we can use this new worklet in our CSS, and the custom image we created will be applied like any other background image.</p>
<pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">div</span> <span class="token punctuation">{</span><br> <span class="token property">background-image</span><span class="token punctuation">:</span> <span class="token function">paint</span><span class="token punctuation">(</span>myGradient<span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre>
<p>In addition to the Paint Worklet, there are other worklets that hook into other stages of the rendering process. The <a href="https://wicg.github.io/animation-worklet/">Animation Worklet</a> hooks into the Composite stage, and the <a href="https://drafts.css-houdini.org/css-layout-api-1/#layout-worklet">Layout Worklet</a> hooks in to the Layout stage.</p>
<h2 id="recap" tabindex="-1">Recap <a class="header-anchor" href="https://bitsofco.de/web-workers-vs-service-workers-vs-worklets/">#</a></h2>
<p>To recap, web workers, service workers, and worklets are all scripts that run on a separate thread to the browser’s main thread. Where they differ is in where they are used and what features they have to enable these use cases.</p>
<p><strong>Worklets</strong> are hooks into the browser’s rendering pipeline, enabling us to have low-level access to the browser’s rendering processes such as styling and layout.</p>
<p><strong>Service workers</strong> are a proxy between the browser and the network. By intercepting requests made by the document, service workers can redirect requests to a cache, enabling offline access.</p>
<p><strong>Web workers</strong> are general-purpose scripts that enable us to offload processor-intensive work from the main thread.</p>
You might not need a loop2018-11-19T00:00:00Zhttps://bitsofco.de/you-might-not-need-a-loop/<p>In JavaScript, like any other language, there are several ways to achieve the same result, some being more suited to certain situations than others. One of the first concepts I learned about when learning to code was loops, and it seemed like the answer to almost everything.</p>
<p>Although loops are great for a lot of use cases, the JavaScript language has provided a suite of methods that can be used to achieve similar results in a more targeted, and sometimes more performant, way.</p>
<p>In this article, I want to cover some examples of where Array methods can be used to replace <code>for</code> loops (and by extension the <code>forEach()</code> method).</p>
<h2 id="modifying-data-with-map" tabindex="-1">Modifying data with <code>map()</code> <a class="header-anchor" href="https://bitsofco.de/you-might-not-need-a-loop/">#</a></h2>
<p>If we want to perform an action on each item in an array and generate a new array with the resulting items, the <code>map()</code> is a great option.</p>
<p>The <code>map()</code> method is available on any Array and has three parameters - the current item in the array, the index (optional), and the original array (optional). Within the callback function, we can return whatever value we want to make up the new array that the <code>map()</code> function returns.</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> newArray <span class="token operator">=</span> myArray<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">current<span class="token punctuation">,</span> index<span class="token punctuation">,</span> array</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> <span class="token comment">// return item for newArray</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre>
<p>Let’s take the example of the following array of articles, each with a <code>title</code> and <code>rating</code> property:</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> articles <span class="token operator">=</span> <span class="token punctuation">[</span><br> <span class="token punctuation">{</span> <span class="token literal-property property">title</span><span class="token operator">:</span> <span class="token string">'You might not need a loop'</span><span class="token punctuation">,</span> <span class="token literal-property property">rating</span><span class="token operator">:</span> <span class="token number">4</span> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token operator">...</span><br><span class="token punctuation">]</span><span class="token punctuation">;</span></code></pre>
<p>Let’s say we wanted to create a new array, but with each of the articles rated as 5 stars. We could do this using a basic <code>for</code> loop, which could look something like this:</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> amazingArticles <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span><br><br><span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">let</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator"><</span> articles<span class="token punctuation">.</span>length<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">const</span> newArticle <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">assign</span><span class="token punctuation">(</span>articles<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">{</span><span class="token literal-property property">rating</span><span class="token operator">:</span> <span class="token number">5</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> amazingArticles<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>newArticle<span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre>
<p>Although this yields the result we want, this logic could be better served by using the <code>.map()</code> method instead.</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> amazingArticles <span class="token operator">=</span> articles<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">article</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> <span class="token keyword">return</span> Object<span class="token punctuation">.</span><span class="token function">assign</span><span class="token punctuation">(</span>article<span class="token punctuation">,</span> <span class="token punctuation">{</span><span class="token literal-property property">rating</span><span class="token operator">:</span> <span class="token number">5</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<h2 id="filtering-data-with-filter" tabindex="-1">Filtering data with <code>filter()</code> <a class="header-anchor" href="https://bitsofco.de/you-might-not-need-a-loop/">#</a></h2>
<p>Sometimes, we want to ascertain whether and which items within an array satisfy a particular condition. We can do this with the <code>filter()</code> method.</p>
<p>The <code>filter()</code> method, like <code>map()</code>, has three parameters - the current item in the array, the index (optional), and the original array (optional). Unlike <code>map()</code>, what is returned is a boolean which determines whether or not to keep the item in the new array the <code>filter()</code> method returns.</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> newArray <span class="token operator">=</span> myArray<span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">current<span class="token punctuation">,</span> index<span class="token punctuation">,</span> array</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> <span class="token comment">// return true or false</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Let’s take the example of the array of articles from before. What if we wanted to get all the articles in the array that are rated 3 and above? Again, we could do this with a <code>for</code> loop, but the <code>filter()</code> method is more suitable.</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> highlyRatedArticles <span class="token operator">=</span> articles<span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">article</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> <span class="token keyword">return</span> article<span class="token punctuation">.</span>rating <span class="token operator">>=</span> <span class="token number">3</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<h2 id="testing-conditions-with-every-and-some" tabindex="-1">Testing conditions with <code>every()</code> and <code>some()</code> <a class="header-anchor" href="https://bitsofco.de/you-might-not-need-a-loop/">#</a></h2>
<p>With the <code>filter()</code> method, we can determine which items within an array satisfy a particular condition. But sometimes, we don’t need to know which items do or don’t satisfy the condition, we may just need to know if all or any of the items satisfy the condition. This is where the <code>every()</code> and <code>some()</code> methods come into play.</p>
<p>The <code>every()</code> and <code>some()</code> methods function very similarly to <code>filter()</code> method. They have the same parameters, and their callback function expects a boolean to be returned to determine whether the item meets the condition.</p>
<pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> every <span class="token operator">=</span> myArray<span class="token punctuation">.</span><span class="token function">every</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">current<span class="token punctuation">,</span> index<span class="token punctuation">,</span> array</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> <span class="token comment">// return true or false</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br><span class="token keyword">const</span> some <span class="token operator">=</span> myArray<span class="token punctuation">.</span><span class="token function">some</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">current<span class="token punctuation">,</span> index<span class="token punctuation">,</span> array</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> <span class="token comment">// return true or false</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>The key difference between these methods and <code>filter()</code> are in what the methods themselves return, i.e. in the above example what the values of the <code>every</code> and <code>some</code> variables would be.</p>
<p>Where the <code>filter()</code> method returns a new array of elements that meets the condition, the <code>every()</code> and <code>some()</code> methods return booleans.</p>
<p>The <code>every()</code> method will return <code>true</code> is <strong>every item</strong> within the array satisfies the condition. If only one item does not satisfy the condition, it will instead return <code>false</code>. The <code>some()</code> method, on the other hand, will return <code>true</code> if at <strong>least one</strong> item within the array satisfies the condition.</p>
<h2 id="to-loop-or-not-to-loop" tabindex="-1">To loop or not to loop? <a class="header-anchor" href="https://bitsofco.de/you-might-not-need-a-loop/">#</a></h2>
<p>As I mentioned earlier, loops are a great tool for a lot of cases, and the existence of these new methods doesn't mean that loops shouldn't be used at all.</p>
<p>I think these methods are great because they provide code that is in a way self-documenting. When we use the <code>filter()</code> method instead of a <code>for</code> loop, it is easier to understand at first glance what the purpose of the logic is.</p>
<p>However, these methods have very specific use cases and may be overkill if their full value isn't being used. An example of this is the <code>map()</code> method, which can technically be used to replace almost any arbitray loop. If in our first example, we only wanted to modify the original <code>articles</code> array and not create a new, modified, <code>amazingArticles</code>, using this method would be unnecessary. It's important to use the method that suits each scenario, to make sure that we aren't over- or under-performing.</p>
Highlights from Chrome Dev Summit 20182018-11-16T00:00:00Zhttps://bitsofco.de/chrome-dev-summit-2018/<p>Earlier this week, I was able to attend Chrome Dev Summit in San Francisco. This was my second time attending the event, the first being in 2016 (you can read about my <a href="https://bitsofco.de/chrome-dev-summit-2016">highlights from that event</a> too). This year's event was one of the best tech conferences I’ve been to in a while, jam-packed with well-thought out sessions and amazing people.</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/N4GsvyioOy-2048.avif 2048w"><source type="image/webp" srcset="https://bitsofco.de/img/N4GsvyioOy-2048.webp 2048w"><img alt="Group photo of attendees at Chrome Dev Summit with a person in a dinosaur costume" loading="lazy" decoding="async" src="https://bitsofco.de/img/N4GsvyioOy-2048.jpeg" width="2048" height="1536"></picture></p>
<p>I learned a lot from the sessions and will likely write about some of the topics in more detail, but I wanted to share a few of the projects and topics I found the most interesting.</p>
<h2 id="navigation-transitions-again-but-this-time-with-portals" tabindex="-1">Navigation transitions (again, but this time with Portals) <a class="header-anchor" href="https://bitsofco.de/chrome-dev-summit-2018/">#</a></h2>
<p>In 2016, Jake Archibald previewed a <a href="https://bitsofco.de/chrome-dev-summit-2018/">highly speculative API</a> that could enable page transitions in the way native mobile applications do, with a new <code>navigate</code> event on the <code>window</code>. Although the specification as Jake presented it two years ago never came to fruition, the idea was continued and re-imaged.</p>
<p><a href="https://github.com/KenjiBaheux/portals">Portals</a> are the new proposal for seamless page transitions on the web. A <code><portal></code> element is similar to an <code><iframe></code>, but is always top-level and can be navigated into and out of. The proposal is still in its early stages and will likely change, but here’s how it could work.</p>
<p>First, we create a new portal element on the page. This can be created via the <code><portal></code> HTML element itself or via JavaScript.</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>portal</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>myPortal<span class="token punctuation">"</span></span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://bitsofco.de<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>portal</span><span class="token punctuation">></span></span></code></pre>
<p>Next, we can respond to a users click (or any) event by animating the portal window in any way we like. It wasn’t clear from the presentation or the documentation exactly how the animations would work, so hopefully that is something that will be cleared up soon.</p>
<pre class="language-js" tabindex="0"><code class="language-js">navigateButton<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token string">'click'</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> <span class="token comment">// insert fancy animations here</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Finally, we activate the new portal, replacing the current top-level browsing context with the portal.</p>
<pre class="language-js" tabindex="0"><code class="language-js">navigateButton<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token string">'click'</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> <span class="token comment">// ...</span><br><br> myPortal<span class="token punctuation">.</span><span class="token function">activate</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Like Jake's original proposal, Portals may not be the answer to our problem of navigating on the web, but I’m happy that there are some proposals being put forth to get us that much closer to the actual solution.</p>
<h2 id="squoosh-a-15kb-js-powered-web-application" tabindex="-1">Squoosh - A 15kb JS-powered web application <a class="header-anchor" href="https://bitsofco.de/chrome-dev-summit-2018/">#</a></h2>
<p>My favourite session of the conference was <a href="https://www.youtube.com/watch?v=ipNW6lJHVEs&t=0s&list=PLNYkxOF6rcIDjlCx1PcphPpmf43aKOAdF">Jake and Mariko’s talk</a> on how they built <a href="https://squoosh.app">Squoosh</a>, a web application for image compression. I would highly recommend you watch it yourself, as they go into great detail on all the steps they took and best practices they followed to ensure that the app was only 15kb on first load!</p>
<p>To do this, they leveraged on a number of tools, technologies, and best practices, including:</p>
<ul>
<li>Preact as a lightweight, 3kb framework</li>
<li>Webpack for code splitting</li>
<li>Web Workers for parallel tasks</li>
<li>Dynamically importing Javascript modules</li>
</ul>
<h2 id="unified-performance-analysis-with-pagespeed-and-lighthouse-apis" tabindex="-1">Unified performance analysis with PageSpeed and Lighthouse APIs <a class="header-anchor" href="https://bitsofco.de/chrome-dev-summit-2018/">#</a></h2>
<p>Historically, measuring the performance of a website was split among various tools that could (and probably would) give differing results. Going forward, all of Google’s tooling for measuring performance across all the various products will now be backed by the new PageSpeed API (version 5), which is essentially a Lighthouse API.</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/HLR_T2pt7e-4032.avif 4032w"><source type="image/webp" srcset="https://bitsofco.de/img/HLR_T2pt7e-4032.webp 4032w"><img alt="Slide from Chrome Dev Summit presentation showing PageSpeed Insights, Lighthouse CLI, and PageSpeed API being used at different stages of the development process" loading="lazy" decoding="async" src="https://bitsofco.de/img/HLR_T2pt7e-4032.jpeg" width="4032" height="3024"></picture></p>
<p>Although the various products could still be useful at different stages -PageSpeed Insights for Benchmarking, the Lighthouse CLI for development, the Search Console for SEO monitoring - they are now all powered by the same testing tool and will deliver the same reliable results.</p>
<h2 id="native-lazyloading-in-chrome" tabindex="-1">Native lazyloading in Chrome <a class="header-anchor" href="https://bitsofco.de/chrome-dev-summit-2018/">#</a></h2>
<p>Chrome will soon support a <code>lazyload</code> attribute on resources such as images! This attribute will tell the browser to only load the resource when it is needed, for example when it is scrolled into view.</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>img</span> <span class="token attr-name">lazyload</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>path/to/image.png<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></code></pre>
<p>This is already available in Internet Explorer and Edge, but it’s great to see it come to more browsers. Right now, this feature can be enabled in Chrome behind a flag (<code>chrome://flags/#enable-lazy-image-loading</code>)</p>
<p>There were many more talks with many more bits of useful information I didn’t mention here. As always, you can re-watch all the sessions on the <a href="https://www.youtube.com/playlist?list=PLNYkxOF6rcIDjlCx1PcphPpmf43aKOAdF">Chrome Developers YouTube Channel</a>.</p>
What is First Input Delay?2018-11-15T00:00:00Zhttps://bitsofco.de/what-is-first-input-delay/<p>There are a lot of metrics we can use to measure the performance of a website - FP, FMP, FCP, TTI, TTYL (JK :P). Although these metrics can be overwhelming at first, it isn’t necessary for every site to track every single one. Different metrics may be more important for different types of websites.</p>
<p>For a content-driven website such as this blog, First Contentful Paint & First Meaningful Paint are the primary metrics I track. FCP refers to the point at which the browser renders <em>any</em> information from the DOM. This could be an image, some text, anything. FMP, on the other hand, is about the point at which the first <em>useful</em> bit of content is rendered on the screen. This is usually the hero section of the page, for example on this blog it could be the title and first sentence or two of an article.</p>
<p>Other metrics could be more important for other types of sites. Take a form, for example, where the primary purpose of the site is for a user to fill out some information. A metric such as Time to Interactive would be crucial. TTI refers to the point at which a user can reliably interact with the content on the website, such as clicking links or entering text into input fields.</p>
<h2 id="first-input-delay" tabindex="-1">First Input Delay <a class="header-anchor" href="https://bitsofco.de/what-is-first-input-delay/">#</a></h2>
<p>FID is a metric that tracks the delay between the time a user can attempt to interact with a part of the site, and the time that the interface is able to respond to that interaction. We have all experienced situations where a web page has visibly loaded and we try to click around on buttons or any Javascript-powered interactive elements, but nothing responds. This delay is what the FID metric tracks.</p>
<p>The FID metric is in someways an intersection of the First Contentful Paint and Time to Interactive metrics. It lies in the middle of those two points, measuring the time between the FCP (and therefore when a first input can be made) and when the browser’s main thread is able to respond to any interactions.</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/np0Gf_uCgs-4032.avif 4032w"><source type="image/webp" srcset="https://bitsofco.de/img/np0Gf_uCgs-4032.webp 4032w"><img alt="Diagram showing FID between FCP and TTI" loading="lazy" decoding="async" src="https://bitsofco.de/img/np0Gf_uCgs-4032.jpeg" width="4032" height="3024"></picture></p>
<div class="img-subheading">Paul Irish at Chrome Dev Summit 2018</div>
<h2 id="what-causes-a-slow-first-input-delay" tabindex="-1">What causes a slow First Input Delay? <a class="header-anchor" href="https://bitsofco.de/what-is-first-input-delay/">#</a></h2>
<p>A slow(er) FID can be caused when the browser’s main thread is busy parsing and executing a significant amount of Javascript <em>after</em> the page content has already loaded. The key point here is that there is visible content on the page that the user may try to interact with, but the browser is not able to respond yet.</p>
<p>If we think of classic client-rendered single page applications, they typically wouldn’t suffer from this issue because nothing meaningful shows on the page until the entire application Javascript is loaded.</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/cQ4NwooH8w-2778.avif 2778w"><source type="image/webp" srcset="https://bitsofco.de/img/cQ4NwooH8w-2778.webp 2778w"><img alt="Timeline - FMP and TTI appear at the same time" loading="lazy" decoding="async" src="https://bitsofco.de/img/cQ4NwooH8w-2778.jpeg" width="2778" height="1600"></picture></p>
<p>In these cases, they have a poor First Meaning Paint, but a low or inexistent First Input Delay. Server-rendered single page applications, on the other hand, have the opposite problem. Because they are rendered by the server, they typically have a comparatively fast First Meaningful Paint. However, because they need the application Javascript to be loaded before the user can properly interact with the site, they are a prime candidate for slow First Input Delay.</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/8SOY0cOkej-2778.avif 2778w"><source type="image/webp" srcset="https://bitsofco.de/img/8SOY0cOkej-2778.webp 2778w"><img alt="Timeline - FMP appears two screens before TTI, and FID between" loading="lazy" decoding="async" src="https://bitsofco.de/img/8SOY0cOkej-2778.jpeg" width="2778" height="1550"></picture></p>
<h2 id="tracking-first-input-delay" tabindex="-1">Tracking First Input Delay <a class="header-anchor" href="https://bitsofco.de/what-is-first-input-delay/">#</a></h2>
<p>FID can be measured using <a href="https://github.com/GoogleChromeLabs/first-input-delay">Google’s tracking library</a>. This library exposes the <code>perfMetrics</code> object, which tracks FID with the <code>onFirstInputDelay()</code> method.</p>
<pre class="language-js" tabindex="0"><code class="language-js">perfMetrics<span class="token punctuation">.</span><span class="token function">onFirstInputDelay</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">delay<span class="token punctuation">,</span> evt</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>delay<span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>With the <code>delay</code> response, you can track it by sending it to tools like Google Analytics or whatever else you use.</p>
Learning to write again2018-11-14T00:00:00Zhttps://bitsofco.de/learning-to-write-again/<p>Last weekend, I attended the Google Developers Experts Summit in Sunnyvale, California. During the summit, some GDEs delivered quick, 5-minute lightning talks on interesting projects or experiences they had over the course of their career.</p>
<p>As you would expect, there were a lot of interesting people doing a lot of interesting things, but the talk that stood out to me the most was <a href="https://twitter.com/urishaked">Uri Shaked</a>’s talk on his <a href="https://medium.com/@urish/short-story-long-story-46979cb9559b">one-blog-a-day challenge</a>. For 30 days, he challenged himself to write a blog post on just about anything.</p>
<p>This challenge really spoke to me because, as I have mentioned a few times on social media, I’ve found it increasingly difficult to write as much as I would like and as much as I used to. There are two main reasons for this:</p>
<p>The first is that <em>I felt like I couldn’t write shorter, less deeply technical articles</em>. Since my blog grew and gained more readership, I felt like my readers would want the more long-form and very technical articles. I recently came across an <a href="https://www.sarasoueidan.com/desk/just-write/">article Sara Soueidan wrote</a>, in which she very aptly explained this feeling:</p>
<blockquote>
<p>With time, people got used to my writing style, and my lengthy, detailed deep dives that sort of became a distinguishing quality of my articles. So as the years went by, I started judging the quality of my articles by whether or not they match my previous style, and hence whether or not people would like them. And this is where I felt like I started losing my direction.</p>
</blockquote>
<p><a href="https://bitsofco.de/hello-world">When I first started this blog</a>, the aim was to share little “bits of code” (get it?) that I was learning. It was meant to be a resource for myself, as writing is a great way to consolidate knowledge, but also as a resource for other developers who were learning like me. Although writing the longer articles isn’t a negative progression, I started being afraid of doing the what I started out with. And this became a hinderance to me writing anything at all.</p>
<p>The second reason is that <em>I felt like I needed to stay in the “HTML, CSS, & JavaScript” box.</em> Although these are still my primary technologies, a large amount of my day job and hobbies include venturing outside these foundational languages. At BuyCoins, I work with the Ionic framework (and as a result Angular) and there are so many things I have learned from working with these technologies that I could share, but chose not to because I felt like my blog should maintain it’s topic. Even outside frontend development, I would like to explore other topics in technology, and experiences learning them.</p>
<h2 id="30-articles-in-30-days-sort-of" tabindex="-1">30 articles in 30 days (sort of) <a class="header-anchor" href="https://bitsofco.de/learning-to-write-again/">#</a></h2>
<p>In order to get out of this rut and force myself to just write again, I want to try Uri’s one-blog-a-day challenge. I’m going to challenge myself to write 5 articles every week for the next 30 days. Most of the articles will be short, the aim is less than 500 words each day. Some of the articles will be on topics I haven’t covered before.</p>
<p>If you want to join in with me, leave a comment below with your blog URL, I’d love to check out what you’re writing too!</p>
<p>I'm counting this article as the first of 30, so I'll see you here again tomorrow :)</p>
Understanding the difference between grid-template and grid-auto2018-10-09T00:00:00Zhttps://bitsofco.de/understanding-the-difference-between-grid-template-and-grid-auto/<p>With all the new properties related to CSS Grid Layout, one of the distinctions that always confused me was the difference between the <code>grid-template-*</code> and <code>grid-auto-*</code> properties. Specifically the difference between <code>grid-template-rows/columns</code> and <code>grid-auto-rows/columns</code>. Although I knew they were to do with the explicit and implicit grid systems, at face value they seemed to be doing almost the same thing. In actual fact, they have little in common and are used to achieve different things.</p>
<h2 id="what-are-explicit-and-implicit-grids" tabindex="-1">What are explicit and implicit grids? <a class="header-anchor" href="https://bitsofco.de/understanding-the-difference-between-grid-template-and-grid-auto/">#</a></h2>
<p>To understand the difference between the <code>grid-template-*</code> and <code>grid-auto-*</code> properties we first need to understand the difference between the explicit and implicit grids.</p>
<p>The definitions of explicit and implicit grids can be a bit circular. The explicit grid, for example, is defined by the property used to create the explicit grid - the <code>grid-template-*</code> properties. According to the <a href="https://www.w3.org/TR/css-grid-1/#explicit-grids">W3C Candidate Recommendation</a>:</p>
<blockquote>
<p>The three properties <code>grid-template-rows</code>, <code>grid-template-columns</code>, and <code>grid-template-areas</code> together define the <strong>explicit grid</strong>.</p>
</blockquote>
<p>The implicit grid, on the other hand, is essentially defined as "everything else" -</p>
<blockquote>
<p>When grid items are positioned outside of [the explicit grid], the grid container generates implicit grid tracks by adding implicit grid lines to the grid. These lines together with the explicit grid form the implicit grid.</p>
</blockquote>
<p>Although these definitions may be a bit difficult to conceptualise at first, it can be helpful to rephrase it.</p>
<p>Within a grid container, there are grid cells. Any cell positioned and sized using the <code>grid-template-*</code> properties forms part of the explicit grid. Any grid cell that is not positioned/sized using this property forms part of the implicit grid instead.</p>
<h2 id="how-do-the-grid-template-and-grid-auto-properties-differ" tabindex="-1">How do the grid-template and grid-auto properties differ? <a class="header-anchor" href="https://bitsofco.de/understanding-the-difference-between-grid-template-and-grid-auto/">#</a></h2>
<p>Because the structure and syntax of the <code>grid-template-*</code> and <code>grid-auto-*</code> property names have some similarities, it can be easy to think the values they accept and therefore what they achieve, are similar as well. On the contrary, these properties are doing very different things.</p>
<p>While the <code>grid-template-*</code> properties are used to define both the <strong>position and size</strong> of grid cells, the <code>grid-auto-*</code> properties are used only to define the <strong>size</strong> of grid cells.</p>
<p>This difference becomes apparent when you consider what these properties are for. The <code>grid-template-*</code> properties are used to <strong>create</strong> an explicit grid, whereas th <code>grid-auto-*</code> properties are used to <strong>size</strong> an implicit grid (which is automatically created).</p>
<h3 id="how-grid-template-works" tabindex="-1">How grid-template works <a class="header-anchor" href="https://bitsofco.de/understanding-the-difference-between-grid-template-and-grid-auto/">#</a></h3>
<p>There are four <code>grid-template-*</code> properties - <code>grid-template-rows</code>, <code>grid-template-columns</code>, <code>grid-template-areas</code>, and <code>grid-template</code> - the last being a shorthand for the first three. For the purpose of this article, I'm going to focus on the first two.</p>
<p>Let's take <code>grid-template-rows</code>. If we want to create a grid with one row of 100px height, we would use the following CSS -</p>
<pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.grid</span> <span class="token punctuation">{</span><br> <span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span><br> <span class="token property">grid-template-rows</span><span class="token punctuation">:</span> 100px<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre>
<p>If we want to add another row, we simply add another length value, space-separated to the first.</p>
<pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.grid</span> <span class="token punctuation">{</span><br> <span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span><br> <span class="token property">grid-template-rows</span><span class="token punctuation">:</span> 100px 100px<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre>
<p>We can do the same for columns using the <code>grid-template-columns</code> property.</p>
<p>Although there are several ways to define the height/width of each row/column using the <code>grid-template-*</code> properties, there is always a one-to-one match between a value and a <a href="https://bitsofco.de/css-grid-terminology#gridcolumnsgridrowsandgridtracks">track</a>. As I showed in the example above, each space-separated length value represents a single row. This is how the <code>grid-template-*</code> properties are used to, not only define sizing, but position and layout of the grid cells.</p>
<h3 id="how-grid-auto-works" tabindex="-1">How grid-auto works <a class="header-anchor" href="https://bitsofco.de/understanding-the-difference-between-grid-template-and-grid-auto/">#</a></h3>
<p>There are three <code>grid-auto-*</code> properties - <code>grid-auto-rows</code>, <code>grid-auto-columns</code>, and <code>grid-auto-flow</code>. For the purpose of this article, I'm going to focus on the first two.</p>
<p>The <code>grid-auto-rows</code> and <code>grid-auto-columns</code> properties accept a single length value, which is used to define the size of any implicit grid cells. For example, we can define that any implicit grid rows should have a height of 100px -</p>
<pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.grid</span> <span class="token punctuation">{</span><br> <span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span><br> <span class="token property">grid-auto-rows</span><span class="token punctuation">:</span> 100px<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre>
<p>Unlike the <code>grid-template-*</code> properties, the <code>grid-auto-*</code> properties only accept a single length value.</p>
<h2 id="working-with-explicit-and-implicit-grids" tabindex="-1">Working with explicit and implicit grids <a class="header-anchor" href="https://bitsofco.de/understanding-the-difference-between-grid-template-and-grid-auto/">#</a></h2>
<p>To better understand the difference between the <code>grid-template-*</code> and <code>grid-auto-*</code> properties, and therefore explicit & implicit grids, let's take the following HTML -</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>grid<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>cell<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Cell 1<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>cell<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Cell 2<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>cell<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Cell 3<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>cell<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Cell 4<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>cell<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Cell 5<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>cell<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Cell 6<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>cell<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Cell 7<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>cell<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Cell 8<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></code></pre>
<p>And the following CSS -</p>
<pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.grid</span> <span class="token punctuation">{</span><br> <span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span><br> <span class="token property">grid-gap</span><span class="token punctuation">:</span> 10px<span class="token punctuation">;</span> <span class="token comment">/* add spacing for better visibility */</span><br><span class="token punctuation">}</span></code></pre>
<p>Currently, our grid looks like this -</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/XGN4QWXjxc-756.avif 756w"><source type="image/webp" srcset="https://bitsofco.de/img/XGN4QWXjxc-756.webp 756w"><img alt="Grid with 8 cells, each 100% width of the container and 10px space between each row" loading="lazy" decoding="async" src="https://bitsofco.de/img/XGN4QWXjxc-756.png" width="756" height="380"></picture></p>
<p>Although it may seem like nothing has happened, an implicit grid has actually been created. This grid has a single column and as many rows as there are grid cells. We can see this if we use the grid inspector in Firefox.</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/T0ovPfMplJ-758.avif 758w"><source type="image/webp" srcset="https://bitsofco.de/img/T0ovPfMplJ-758.webp 758w"><img alt="Same image as previous with lines showing where each grid track starts and ends" loading="lazy" decoding="async" src="https://bitsofco.de/img/T0ovPfMplJ-758.png" width="758" height="387"></picture></p>
<h3 id="sizing-the-implicit-grid-with-grid-auto" tabindex="-1">Sizing the implicit grid with <code>grid-auto</code> <a class="header-anchor" href="https://bitsofco.de/understanding-the-difference-between-grid-template-and-grid-auto/">#</a></h3>
<p>If we wanted to specify the size of the height of these rows, we can use the <code>grid-auto-rows</code> property.</p>
<pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.grid</span> <span class="token punctuation">{</span><br> <span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span><br> <span class="token property">grid-gap</span><span class="token punctuation">:</span> 10px<span class="token punctuation">;</span> <span class="token comment">/* add spacing for better visibility */</span><br> <span class="token property">grid-auto-rows</span><span class="token punctuation">:</span> 30px<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/X0BBib2z2B-757.avif 757w"><source type="image/webp" srcset="https://bitsofco.de/img/X0BBib2z2B-757.webp 757w"><img alt="Each row now has a height of 30px" loading="lazy" decoding="async" src="https://bitsofco.de/img/X0BBib2z2B-757.png" width="757" height="468"></picture></p>
<p>Note that all we have done is given a single height value, and this style is applied to all rows within the grid.</p>
<h3 id="creating-an-explicit-grid-with-grid-template" tabindex="-1">Creating an explicit grid with <code>grid-template</code> <a class="header-anchor" href="https://bitsofco.de/understanding-the-difference-between-grid-template-and-grid-auto/">#</a></h3>
<p>Now let's define an explicit grid. We can create an explicit grid that has two rows, each with a height of 100px -</p>
<pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.grid</span> <span class="token punctuation">{</span><br> <span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span><br> <span class="token property">grid-gap</span><span class="token punctuation">:</span> 10px<span class="token punctuation">;</span> <span class="token comment">/* add spacing for better visibility */</span><br> <span class="token property">grid-auto-rows</span><span class="token punctuation">:</span> 30px<span class="token punctuation">;</span><br> <span class="token property">grid-template-rows</span><span class="token punctuation">:</span> 100px 100px<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/dMbfowuIc1-757.avif 757w"><source type="image/webp" srcset="https://bitsofco.de/img/dMbfowuIc1-757.webp 757w"><img alt="The first two rows have a height of 100px, all other rows remain as 50px" loading="lazy" decoding="async" src="https://bitsofco.de/img/dMbfowuIc1-757.png" width="757" height="607"></picture></p>
<p>What we can see is that only the first two rows have the height of 100px, and the rest have the height defined by the <code>grid-auto-rows</code> property. We can more clearly see the distinction between the explicit and implicit grids in the image below -</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/lbRbwfjvXI-765.avif 765w"><source type="image/webp" srcset="https://bitsofco.de/img/lbRbwfjvXI-765.webp 765w"><img alt="The first two rows have a height of 100px, all other rows remain as 50px" loading="lazy" decoding="async" src="https://bitsofco.de/img/lbRbwfjvXI-765.png" width="765" height="532"></picture></p>
<h2 id="summary" tabindex="-1">Summary <a class="header-anchor" href="https://bitsofco.de/understanding-the-difference-between-grid-template-and-grid-auto/">#</a></h2>
<p>To summarize, the <code>grid-template-*</code> properties are used to create, place, and size cells for the explicit grid. Any cell that exists in the grid that is not explicitly created by this property forms part of the implicit grid, which can be sized by the <code>grid-auto-*</code> properties.</p>
[Smashing Magazine] Creating The Feature Queries Manager DevTools Extension2018-05-23T00:00:00Zhttps://bitsofco.de/smashing-magazine/<p>While working with feature queries, I requently find myself in the messy situation of trying to test my fallbacks layout. This lead me to create the <a href="https://github.com/ireade/feature-queries-manager">Feature Queries Manager</a>, a developer tools extension that lets you easily toggle styles behind feature queries.</p>
<p><img src="https://cloud.netlifyusercontent.com/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/425a3e8a-01cf-4e4c-9ce5-788ee7beb256/feature-queries-manager.gif" alt="Demo of toggling feature queries in the FQM extension"></p>
<p>I recently wrote an article for Smashing Magazine about why and how I created this extension. <a href="https://www.smashingmagazine.com/2018/05/feature-queries-manager-devtools-extension/">Read the article on Smashing Magazine</a>.</p>
How display: contents; Works2018-03-27T00:00:00Zhttps://bitsofco.de/how-display-contents-works/<p>As I frequently mention, <a href="https://bitsofco.de/controlling-the-box-model">every element in the document tree is a rectangular box</a>. Broadly speaking, this "rectangular box" consists of two sections. First we have the actual box, which consists of the border, padding, and margin areas. Second, we have the contents of the box; the content area.</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/ocD2Frhdky-723.avif 723w"><source type="image/webp" srcset="https://bitsofco.de/img/ocD2Frhdky-723.webp 723w"><img alt="CSS Box Model showing content, padding, border, and margin areas" loading="lazy" decoding="async" src="https://bitsofco.de/img/ocD2Frhdky-723.png" width="723" height="432"></picture></p>
<p>With the CSS <code>display</code> property, we can control different things about how this box and its children are drawn on the page. We can have the box be placed within its siblings like text with <code>inline</code>. We can even trick the box into behaving like a table with <code>table</code>.</p>
<p>There are only two values for the <code>display</code> property which control whether an element defined in the markup will generate a box at all. The <code>none</code> value will result in neither the box or its contents being drawn on the page. The newly specced <code>contents</code> value, on the other hand, will result in the contents of the box being drawn as normal, but the surrounding box being omitted entirely.</p>
<h2 id="what-happens-when-you-use-display-contents" tabindex="-1">What happens when you use <code>display: contents</code>? <a class="header-anchor" href="https://bitsofco.de/how-display-contents-works/">#</a></h2>
<p>The easiest way to understand what happens when <code>display: contents</code> is used is to imagine the element’s opening and closing tags being omitted from the markup. In the <a href="https://www.w3.org/TR/css-display-3/#box-generation">specification</a>, it states -</p>
<blockquote>
<p>For the purposes of box generation and layout, the element must be treated as if it had been replaced in the element tree by its contents</p>
</blockquote>
<p>Let's take, for example, the following markup -</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>outer<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br> I’m some content<br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>inner<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>I’m some inner content<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></code></pre>
<p>And the following styles -</p>
<pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.outer</span> <span class="token punctuation">{</span><br> <span class="token property">border</span><span class="token punctuation">:</span> 2px solid lightcoral<span class="token punctuation">;</span><br> <span class="token property">background-color</span><span class="token punctuation">:</span> lightpink<span class="token punctuation">;</span><br> <span class="token property">padding</span><span class="token punctuation">:</span> 20px<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token selector">.inner</span> <span class="token punctuation">{</span><br> <span class="token property">background-color</span><span class="token punctuation">:</span> #ffdb3a<span class="token punctuation">;</span><br> <span class="token property">padding</span><span class="token punctuation">:</span> 20px<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre>
<p>Typically, this is how we would expect the elements to be drawn on the page -</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/sux4tdovfb-561.avif 561w"><source type="image/webp" srcset="https://bitsofco.de/img/sux4tdovfb-561.webp 561w"><img alt="Example showing .outer box being drawn as normal" loading="lazy" decoding="async" src="https://bitsofco.de/img/sux4tdovfb-561.png" width="561" height="128"></picture></p>
<p>However, if we add <code>display: contents</code> to the <code>.outer</code> element, this is how it is displayed -</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/mmD_ByT4rR-564.avif 564w"><source type="image/webp" srcset="https://bitsofco.de/img/mmD_ByT4rR-564.webp 564w"><img alt="Example shwing .outer box background, padding, and borders not being displayed" loading="lazy" decoding="async" src="https://bitsofco.de/img/mmD_ByT4rR-564.png" width="564" height="96"></picture></p>
<p>Visually speaking, the above result is exactly the same as what we would expect if the markup was written without the opening and closing tags of the outer element.</p>
<pre class="language-html" tabindex="0"><code class="language-html">I’m some content<br><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>inner<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>I’m some inner content<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></code></pre>
<h2 id="what-about" tabindex="-1">What about…? <a class="header-anchor" href="https://bitsofco.de/how-display-contents-works/">#</a></h2>
<p>This CSS rule, although seemingly straightforward, has quite a few edge cases and specific behaviours to take note of. We have to remember that the <code>display: contents</code> rule only affects the box being visually drawn on the page; it does not affect the markup within the document.</p>
<h3 id="what-about-the-element-s-attributes" tabindex="-1">What about the element’s attributes? <a class="header-anchor" href="https://bitsofco.de/how-display-contents-works/">#</a></h3>
<p><em>If the element is effectively replaced by its contents, what does that mean for any attributes applied to it?</em> Since this replacement is, for the most part, visual only, we can actually still select, target, and interact with the element using its attributes.</p>
<p>We can still target the element by its ID by, for example, making a reference to it using <code>aria-labelledby</code>.</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>label<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">display</span><span class="token punctuation">:</span> contents<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span>Label here!<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">aria-labelledby</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>label<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span><span class="token punctuation">></span></span></code></pre>
<p>However, the one thing I have found that doesn't work properly is that we can no longer navigate to the element using a fragment identifier.</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>target<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">display</span><span class="token punctuation">:</span> contents<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span>Target Content<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span><br><br><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><br> window<span class="token punctuation">.</span>location<span class="token punctuation">.</span>hash <span class="token operator">=</span> <span class="token string">"target"</span><span class="token punctuation">;</span><br> <span class="token comment">// => Nothing happens</span><br></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></code></pre>
<h3 id="what-about-javascript-events" tabindex="-1">What about JavaScript events? <a class="header-anchor" href="https://bitsofco.de/how-display-contents-works/">#</a></h3>
<p>As we have just covered, we can still target an element with <code>display: contents</code> applied to it. In fact, we can target an element with <code>display: none</code> applied, but the event will never trigger because we cannot interact with the element. However, since the contents of an element with <code>display: contents</code> are still visible, we can interact with the element through its contents.</p>
<p>If we set an event listener for a click on the element, for example, and log the value of <code>this</code>, we will still get the outer element because it still exists in the document.</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>outer<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>I’m some content<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span><br><br><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><br> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">".outer"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">"click"</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">event</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token comment">// => <div class="outer"></div></span><br> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></code></pre>
<h3 id="what-about-pseudo-elements" tabindex="-1">What about pseudo-elements? <a class="header-anchor" href="https://bitsofco.de/how-display-contents-works/">#</a></h3>
<p>The pseudo-elements of an element with <code>display: contents</code> are considered to be part of its children, so are displayed as normal.</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>style</span><span class="token punctuation">></span></span><span class="token style"><span class="token language-css"><br> <span class="token selector">.outer</span> <span class="token punctuation">{</span> <span class="token property">display</span><span class="token punctuation">:</span> contents<span class="token punctuation">;</span> <span class="token punctuation">}</span><br> <span class="token selector">.outer::before</span> <span class="token punctuation">{</span> <span class="token property">content</span><span class="token punctuation">:</span> <span class="token string">"Before"</span> <span class="token punctuation">}</span><br> <span class="token selector">.outer::after</span> <span class="token punctuation">{</span> <span class="token property">content</span><span class="token punctuation">:</span> <span class="token string">"After"</span> <span class="token punctuation">}</span><br></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>style</span><span class="token punctuation">></span></span><br><br><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>outer<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>I’m some content<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></code></pre>
<p>The above markup will generate the following result -</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/b_0097Md8d-395.avif 395w"><source type="image/webp" srcset="https://bitsofco.de/img/b_0097Md8d-395.webp 395w"><img alt="Result showing text 'Before I'm some content After'" loading="lazy" decoding="async" src="https://bitsofco.de/img/b_0097Md8d-395.png" width="395" height="82"></picture></p>
<h3 id="what-about-form-elements-images-and-other-replaced-elements" tabindex="-1">What about form elements, images and other replaced elements? <a class="header-anchor" href="https://bitsofco.de/how-display-contents-works/">#</a></h3>
<p>Replaced elements and some form elements have a different behaviour when <code>display: contents</code> is applied to them.</p>
<h4 id="replaced-elements" tabindex="-1">Replaced elements <a class="header-anchor" href="https://bitsofco.de/how-display-contents-works/">#</a></h4>
<p>Replaced elements are elements, such as images, whose appearance and "boxes" are controlled by an external resource. Attempting to remove the box for elements like this doesn't really make sense because it isn't entirely clear what the "box" is. For these elements, <code>display: contents</code> functions exactly like <code>display: none</code>. The entire box and contents of the element are not drawn on the page at all.</p>
<h4 id="form-elements" tabindex="-1">Form elements <a class="header-anchor" href="https://bitsofco.de/how-display-contents-works/">#</a></h4>
<p>For many form elements, they are not composed of a single "box". They look like that from the perspective of us, the web page authors. But under the hood, they are made up of several smaller elements. Similarly to the replaced elements, it doesn't make sense to remove the box, because there isn't one box. And so, for form elements like <code><select></code>, <code><input></code>, and <code><textarea></code>, <code>display: contents</code> functions exactly like <code>display: none</code>.</p>
<p>(See the <a href="https://www.w3.org/TR/css-display-3/#unbox-html">full list of elements that <code>display: contents</code> works differenly for</a>)</p>
<h3 id="what-about-buttons-and-links" tabindex="-1">What about buttons and links? <a class="header-anchor" href="https://bitsofco.de/how-display-contents-works/">#</a></h3>
<p>Both the <code><button></code> and <code><a></code> elements do not have any special behaviour when it comes to <code>display: contents</code>. However, it is useful to know how this rule affects them because it may not be immediately obvious.</p>
<h4 id="buttons" tabindex="-1">Buttons <a class="header-anchor" href="https://bitsofco.de/how-display-contents-works/">#</a></h4>
<p>Buttons are not one of the form elements which are composed of other boxes. Therefore, <code>display: contents</code> will just remove the surrounding box, leaving the content of the button displayed. If used within a form, clicking the button will still attempt to submit the form and, as we have covered, any event listeners on the button will function normally.</p>
<h4 id="links" tabindex="-1">Links <a class="header-anchor" href="https://bitsofco.de/how-display-contents-works/">#</a></h4>
<p>For links, the same applies in that the surrounding box is visually removed, leaving the contents of the link behind. Since attributes aren't generally affected by this CSS rule, the link will still function properly and can be used to navigate as normal.</p>
<h2 id="why-is-display-contents-useful" tabindex="-1">Why is <code>display: contents</code> useful? <a class="header-anchor" href="https://bitsofco.de/how-display-contents-works/">#</a></h2>
<p>In the past, we have had to lay out our HTML in a way that works both semantically, and for the purposes of styling with CSS. This has led to cases where we either have too many unnecessary elements for wrapping purposes, or too few elements to enable direct sibling styling. The latter has become particularly pertinent with the introduction of CSS Grid Layout which, for now at least, needs to work with direct sibling elements.</p>
<p>Let's take, for example, this layout -</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/SfGjn-h9Tq-677.avif 677w"><source type="image/webp" srcset="https://bitsofco.de/img/SfGjn-h9Tq-677.webp 677w"><img alt="Card Layout" loading="lazy" decoding="async" src="https://bitsofco.de/img/SfGjn-h9Tq-677.png" width="677" height="476"></picture></p>
<p>We have two "cards" placed next to each other, each with a heading, a pargraph, and a footer. What we want is for each of the sections within each card to be the same height, regardless of the content of each section (e.g. the first card has only a 1-line whereas the third card has a 3-line heading, but the first card heading section height should match the third).</p>
<p>We could achieve this layout using CSS Grid, but we would need all the elements within each "card" to be direct siblings of each other. So, we may have to layout our HTML like this -</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>grid<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</span><span class="token punctuation">></span></span>This is a heading<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>...<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Footer stuff<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span><br><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</span><span class="token punctuation">></span></span>This is a really really really super duper loooong heading<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>...<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Footer stuff<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></code></pre>
<p>And we could apply the following styles -</p>
<pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.grid</span> <span class="token punctuation">{</span><br> <span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span><br> <span class="token property">grid-auto-flow</span><span class="token punctuation">:</span> column<span class="token punctuation">;</span><br> <span class="token property">grid-template-rows</span><span class="token punctuation">:</span> auto 1fr auto<span class="token punctuation">;</span><br> <span class="token property">grid-template-columns</span><span class="token punctuation">:</span> <span class="token function">repeat</span><span class="token punctuation">(</span>2<span class="token punctuation">,</span> 1fr<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token property">grid-column-gap</span><span class="token punctuation">:</span> 20px<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre>
<p>Although this is not exactly an incorrect way to structure this document, it probably makes more sense to group each "card" within an <code><article></code> element. This is where <code>display: contents</code> comes in. We can have the best of both worlds here - by laying out our markup in a way that makes sense semantically, but having our CSS act in a way that makes sense layout-wise.</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>grid<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>article</span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">display</span><span class="token punctuation">:</span> contents<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</span><span class="token punctuation">></span></span>This is a heading<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>...<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Footer stuff<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>article</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>article</span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">display</span><span class="token punctuation">:</span> contents<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</span><span class="token punctuation">></span></span>This is a really really really super duper loooong heading<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>...<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Footer stuff<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>article</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></code></pre>
<p>With the same CSS as above, we can achieve the layout we want.</p>
<h2 id="using-display-contents" tabindex="-1">Using <code>display: contents</code> <a class="header-anchor" href="https://bitsofco.de/how-display-contents-works/">#</a></h2>
<p>At the time of writing, <code>display: contents</code> is only supported in two major browsers, with support arriving soon in many others.</p>
<p class="ciu_embed" data-feature="css-display-contents" data-periods="future_1,current,past_1,past_2" data-accessible-colours="false">
<picture>
<source type="image/webp" srcset="https://caniuse.bitsofco.de/image/css-display-contents.webp">
<source type="image/png" srcset="https://caniuse.bitsofco.de/image/css-display-contents.png">
<img src="https://caniuse.bitsofco.de/image/css-display-contents.jpg" alt="Data on support for the css-display-contents feature across the major browsers from caniuse.com">
</picture>
</p>
<p>Because of this, this feature should still currently be considered a progressive enhancement, and an appropriate fallback should be used.</p>
<pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">article</span> <span class="token punctuation">{</span><br> <span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span><br> <span class="token property">grid-template-rows</span><span class="token punctuation">:</span> 200px 1fr auto<span class="token punctuation">;</span> <span class="token comment">/* e.g. Use a fixed height for the header */</span><br><span class="token punctuation">}</span><br><br><span class="token atrule"><span class="token rule">@supports</span> <span class="token punctuation">(</span><span class="token property">display</span><span class="token punctuation">:</span> contents<span class="token punctuation">)</span></span> <span class="token punctuation">{</span><br> <span class="token selector">article</span> <span class="token punctuation">{</span> <span class="token property">display</span><span class="token punctuation">:</span> contents<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token punctuation">}</span></code></pre>
Recreating the GitHub Contribution Graph with CSS Grid Layout2018-01-23T00:00:00Zhttps://bitsofco.de/github-contribution-graph-css-grid/<p>While learning CSS Grid Layout, I’ve found that the best way to internalise all the new concepts and terminology is by working on various layouts using them. Recently, I decided to try to recreate the GitHub Contribution graph using CSS Grid Layout, and found it was an interesting challenge.</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/m0kiUJekQe-640.avif 640w"><source type="image/webp" srcset="https://bitsofco.de/img/m0kiUJekQe-640.webp 640w"><img alt="Contribution Graph" loading="lazy" decoding="async" src="https://bitsofco.de/img/m0kiUJekQe-640.png" width="640" height="111"></picture></p>
<p>As I always find while working with CSS Grid Layout, I end up with far less CSS than I would have using almost any other method. In this case, the layout-related part of my CSS ended up being less than 30 lines, with only 15 declarations!</p>
<p>To explain how I recreated this layout, let’s break it down into four stages:</p>
<ol>
<li>The overall graph grid</li>
<li>The "days" column</li>
<li>The "months" row</li>
<li>The graph squares</li>
</ol>
<h2 id="the-overall-graph-grid" tabindex="-1">The Overall Graph Grid <a class="header-anchor" href="https://bitsofco.de/github-contribution-graph-css-grid/">#</a></h2>
<p>The broad layout of the contribution graph is broken up into three main areas - the days column, the months row, and the individual squares.</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/y4TXQzTALT-1130.avif 1130w"><source type="image/webp" srcset="https://bitsofco.de/img/y4TXQzTALT-1130.webp 1130w"><img alt="" loading="lazy" decoding="async" src="https://bitsofco.de/img/y4TXQzTALT-1130.png" width="1130" height="229"></picture></p>
<p>Within the main <code>.grid</code> element, we have three elements:</p>
<pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>grid<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ul</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>months<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ul</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ul</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>days<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ul</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ul</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>squares<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ul</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></code></pre>
<p>I laid out the three areas within the main graph using the <code>grid-template-areas</code> property. This property allows us to, in a more explicit way, define where the different areas of a grid fall. To do this, we first name each area of the grid.</p>
<pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.months</span> <span class="token punctuation">{</span> <span class="token property">grid-area</span><span class="token punctuation">:</span> months<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token selector">.days</span> <span class="token punctuation">{</span> <span class="token property">grid-area</span><span class="token punctuation">:</span> days<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token selector">.squares</span> <span class="token punctuation">{</span> <span class="token property">grid-area</span><span class="token punctuation">:</span> squares<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre>
<p>Then, using these names, we write out how we want the areas to be laid out. The <code>grid-template-areas</code> property accepts space-separated strings. Each string represents a row, and within each string we have columns.</p>
<pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.graph</span> <span class="token punctuation">{</span><br> <span class="token property">display</span><span class="token punctuation">:</span> inline-grid<span class="token punctuation">;</span><br> <span class="token property">grid-template-areas</span><span class="token punctuation">:</span> <span class="token string">"empty months"</span><br> <span class="token string">"days squares"</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre>
<p>I prefer to separate each string by a new line, so it is more visually apparent that each string represents a row, but a space is all that is needed. The above property could just as easily be written in the following way -</p>
<pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.graph</span> <span class="token punctuation">{</span><br> <span class="token property">grid-template-areas</span><span class="token punctuation">:</span> <span class="token string">"empty months"</span> <span class="token string">"days squares"</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre>
<p>You'll notice I added an unnammed grid area, <code>empty</code> to the <code>grid-template-areas</code> value. This is because we want the <code>months</code> area to start from the second column and the <code>days</code> area to start from the second row. If we use a grid area name that doesn't correspond to an actual grid area, we can essentially create a "phantom" element. We can act as if there is an element there without having anything obstructing our actual markup. Cool, right?!</p>
<p>In addition to laying out these grid areas, there are two more styles that need to be applied to graph. First, we want to add some spacing between each element, which we add with the <code>grid-gap</code> property.</p>
<pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.graph</span> <span class="token punctuation">{</span><br> <span class="token property">grid-gap</span><span class="token punctuation">:</span> 10px<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre>
<p>Finally, we want the the first column to only take up as much space as it needed by the content within it, but the second column to fill up the rest of the available space. We can achieve the latter using the <code>fr</code> unit, which represents the remaining available space within a grid.</p>
<pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.graph</span> <span class="token punctuation">{</span><br> <span class="token property">grid-template-columns</span><span class="token punctuation">:</span> auto 1fr<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre>
<p>And that’s all the styles we need to apply to the containing graph element.</p>
<h2 id="the-days-column" tabindex="-1">The "Days" Column <a class="header-anchor" href="https://bitsofco.de/github-contribution-graph-css-grid/">#</a></h2>
<p>The list of days on the left side of the contribution graph is a simple one-column grid with exactly 7 rows.</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/4gvvp5-yBC-1127.avif 1127w"><source type="image/webp" srcset="https://bitsofco.de/img/4gvvp5-yBC-1127.webp 1127w"><img alt="" loading="lazy" decoding="async" src="https://bitsofco.de/img/4gvvp5-yBC-1127.png" width="1127" height="213"></picture></p>
<p>This layout is achieved with exactly 2 lines of CSS -</p>
<pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">:root</span> <span class="token punctuation">{</span><br> <span class="token property">--square-size</span><span class="token punctuation">:</span> 15px<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token selector">.days</span> <span class="token punctuation">{</span><br> <span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span><br> <span class="token property">grid-template-rows</span><span class="token punctuation">:</span> <span class="token function">repeat</span><span class="token punctuation">(</span>7<span class="token punctuation">,</span> <span class="token function">var</span><span class="token punctuation">(</span>--square-size<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre>
<p>We use the <code>repeat()</code> function here, passing in the following options:</p>
<ol>
<li>For how many rows we want the sizing (see the next item) to apply for. In this case, we chose <code>7</code> because there are 7 days in the week.</li>
<li>The size we want each row to be. In this case, we used the same size the individual squares are going to be, using the <code>--square-size</code> variable declared earlier in the stylesheet</li>
</ol>
<p>Lastly, we want some spacing between each of the rows. As with the spacing between the areas in the <code>.graph</code> element, we use the <code>grid-gap</code> property, passing in the amount of spacing we want.</p>
<pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">:root</span> <span class="token punctuation">{</span><br> <span class="token property">--square-size</span><span class="token punctuation">:</span> 15px<span class="token punctuation">;</span><br> <span class="token property">--square-gap</span><span class="token punctuation">:</span> 5px<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token selector">.days</span> <span class="token punctuation">{</span><br> <span class="token property">grid-gap</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--square-gap<span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre>
<h2 id="the-months-row" tabindex="-1">The "Months" Row <a class="header-anchor" href="https://bitsofco.de/github-contribution-graph-css-grid/">#</a></h2>
<p>The list of months at the top of the contribution graph is a one-row grid.</p>
<p><picture><source type="image/avif" srcset="https://bitsofco.de/img/GvGaAN8Le--1127.avif 1127w"><source type="image/webp" srcset="https://bitsofco.de/img/GvGaAN8Le--1127.webp 1127w"><img alt="" loading="lazy" decoding="async" src="https://bitsofco.de/img/GvGaAN8Le--1127.png" width="1127" height="226"></picture></p>
<p>However, unlike the list of days, the amount of space (i.e. columns) taken up by each items is not so straightforward. Ideally, we would want each month to take up the equivalent of 4 columns to represent four weeks. We could achieve this using a similar method to how we laid out the weeks.</p>
<pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">:root</span> <span class="token punctuation">{</span><br> <span class="token property">--square-size</span><span class="token punctuation">:</span> 15px<span class="token punctuation">;</span><br> <span class="token property">--square-gap</span><span class="token punctuation">:</span> 5px<span class="token punctuation">;</span><br> <span class="token property">--week-width</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--square-size<span class="token punctuation">)</span> + <span class="token function">var</span><span class="token punctuation">(</span>--square-gap<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token selector">.months</span> <span class="token punctuation">{</span><br> <span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span><br> <span class="token property">grid-template-columns</span><span class="token punct